# Copyright (C) 2023 Apple, Inc. # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # The 22.1.1 tarball contains an empty sources/freetype directory, which confuses the default CurlDownloadStrategy. # A custom strategy also allows us to restrict extraction to just the wine subdirectory. class TarballDownloadStrategy < CurlDownloadStrategy def stage(&block) ohai "Staging #{cached_location} in #{pwd}" system "tar", "-xf", cached_location, "--include=sources/wine/*", "--strip-components=1" yield if block_given? end end class GamePortingToolkit < Formula version "1.0.4" desc "Apple Game Porting Toolkit" homepage "https://developer.apple.com/" url "https://media.codeweavers.com/pub/crossover/source/crossover-sources-22.1.1.tar.gz", using: TarballDownloadStrategy sha256 "cdfe282ce33788bd4f969c8bfb1d3e2de060eb6c296fa1c3cdf4e4690b8b1831" patch :p0, :DATA depends_on arch: :x86_64 depends_on "game-porting-toolkit-compiler" depends_on "bison" => :build uses_from_macos "flex" => :build depends_on "mingw-w64" => :build depends_on "gstreamer" depends_on "pkg-config" # to find the rest of the runtime dependencies @@named_deps = ["zlib", # must be explicitly added to PKG_CONFIG_PATH "freetype", "sdl2", "libgphoto2", "faudio", "jpeg", "libpng", "mpg123", "libtiff", "libgsm", "glib", "gnutls", "libusb", "gettext", "openssl@1.1", "sane-backends"] @@named_deps.each do |dep| depends_on dep end def install # Bypass the Homebrew shims to build native binaries with the dedicated compiler. # (PE binaries will be built with mingw32-gcc.) compiler = Formula["game-porting-toolkit-compiler"] compiler_options = ["CC=#{compiler.bin}/clang", "CXX=#{compiler.bin}/clang++"] # Becuase we are bypassing the Homebrew shims, we need to make the dependencies’ headers visible. # (mingw32-gcc will automatically make the mingw-w64 headers visible.) @@named_deps.each do |dep| formula = Formula[dep] ENV.append_to_cflags "-I#{formula.include}" ENV.append "LDFLAGS", "-L#{formula.lib}" end # Glib & GStreamer have also has a non-standard include path ENV.append "GSTREAMER_CFLAGS", "-I#{Formula['gstreamer'].include}/gstreamer-1.0" ENV.append "GSTREAMER_LIBS", "-L#{Formula['gstreamer'].lib}" ENV.append "GSTREAMER_CFLAGS", "-I#{Formula['glib'].include}/glib-2.0" ENV.append "GSTREAMER_CFLAGS", "-I#{Formula['glib'].lib}/glib-2.0/include" ENV.append "GSTREAMER_LIBS", "-lglib-2.0 -lgmodule-2.0 -lgstreamer-1.0 -lgstaudio-1.0 -lgstvideo-1.0 -lgstgl-1.0 -lgobject-2.0" # We also need to tell the linker to add Homebrew to the rpath stack. ENV.append "LDFLAGS", "-lSystem -L#{HOMEBREW_PREFIX}/lib -Wl,-rpath,#{HOMEBREW_PREFIX}/lib -Wl,-rpath,@executable_path/../lib/external" # Common compiler flags for both Mach-O and PE binaries. ENV.append_to_cflags "-O3 -Wno-implicit-function-declaration -Wno-format -Wno-deprecated-declarations -Wno-incompatible-pointer-types" # Use an older deployment target to avoid new dyld behaviors. # The custom compiler is too old to accept "13.0", so we use "10.14". ENV["MACOSX_DEPLOYMENT_TARGET"] = "10.14" wine_configure_options = ["--prefix=#{prefix}", "--disable-win16", "--disable-tests", "--without-x", "--without-pulse", "--without-dbus", "--without-inotify", "--without-alsa", "--without-capi", "--without-oss", "--without-udev", "--without-krb5"] wine64_configure_options = ["--enable-win64", "--with-gnutls", "--with-freetype", "--with-gstreamer"] wine32_configure_options = ["--enable-win32on64", "--with-wine64=../wine64-build", "--without-gstreamer", "--without-gphoto", "--without-sane", "--without-krb5", "--disable-winedbg", "--without-vulkan", "--disable-vulkan_1", "--disable-winevulkan", "--without-openal", "--without-unwind", "--without-usb"] # Build 64-bit Wine first. mkdir buildpath/"wine64-build" do system buildpath/"wine/configure", *wine_configure_options, *wine64_configure_options, *compiler_options system "make" end # Now build 32-on-64 Wine. mkdir buildpath/"wine32-build" do system buildpath/"wine/configure", *wine_configure_options, *wine32_configure_options, *compiler_options system "make" end # Install both builds. cd "wine64-build" do system "make", "install" end cd "wine32-build" do system "make", "install" end end def post_install #Homebrew replaces wine's rpath names with absolute paths, we need to change them back to @rpath relative paths. #Wine relies on @rpath names to cause dlopen to always return the first dylib with that name loaded into the process rather than the actual dylib found using rpath lookup. Dir["#{lib}/wine/{x86_64-unix,x86_32on64-unix}/*.so"].each do |dylib| chmod 0664, dylib MachO::Tools.change_dylib_id(dylib, "@rpath/#{File.basename(dylib)}") MachO.codesign!(dylib) chmod 0444, dylib end end def caveats return unless latest_version_installed? "Please follow the instructions in the Game Porting Toolkit README to complete installation." end test do system bin/"wine64", "--version" end end __END__ diff --git a/include/distversion.h b/include/distversion.h new file mode 100644 index 00000000000..b8a3724b76b --- /dev/null +++ wine/include/distversion.h @@ -0,0 +1,12 @@ +/* --------------------------------------------------------------- +* distversion.c +* +* Copyright 2013, CodeWeavers, Inc. +* +* Information from DISTVERSION which needs to find +* its way into the wine tree. +* --------------------------------------------------------------- */ + +#define WINDEBUG_WHAT_HAPPENED_MESSAGE "This can be caused by a problem in the program or a deficiency in Wine. You may want to check http://www.codeweavers.com/compatibility/ for tips about running this application." + +#define WINDEBUG_USER_SUGGESTION_MESSAGE "If this problem is not present under Windows and has not been reported yet, you can save the detailed information to a file using the \"Save As\" button, then file a bug report and attach that file to the report." \ No newline at end of file -- 2.39.2 (Apple Git-144) diff --git a/configure b/configure index 2d57c7e085d..b9b57c0d509 100755 --- wine/configure +++ wine/configure @@ -950,6 +950,7 @@ enable_amstream enable_apisetschema enable_apphelp enable_appwiz_cpl +enable_api_ms_win_power_base_l1_1_0 enable_atl enable_atl100 enable_atl110 @@ -21776,6 +21777,7 @@ wine_fn_config_makefile dlls/apisetschema enable_apisetschema wine_fn_config_makefile dlls/apphelp enable_apphelp wine_fn_config_makefile dlls/apphelp/tests enable_tests wine_fn_config_makefile dlls/appwiz.cpl enable_appwiz_cpl +wine_fn_config_makefile dlls/api-ms-win-power-base-l1-1-0 enable_api_ms_win_power_base_l1_1_0 wine_fn_config_makefile dlls/atl enable_atl wine_fn_config_makefile dlls/atl/tests enable_tests wine_fn_config_makefile dlls/atl100 enable_atl100 diff --git a/configure.ac b/configure.ac diff --git a/configure.ac b/configure.ac index 50c50d15eda..58063421cce 100644 --- wine/configure.ac +++ wine/configure.ac @@ -2424,6 +2424,7 @@ WINE_CONFIG_MAKEFILE(dlls/apisetschema) WINE_CONFIG_MAKEFILE(dlls/apphelp) WINE_CONFIG_MAKEFILE(dlls/apphelp/tests) WINE_CONFIG_MAKEFILE(dlls/appwiz.cpl) +WINE_CONFIG_MAKEFILE(dlls/api-ms-win-power-base-l1-1-0) WINE_CONFIG_MAKEFILE(dlls/atl) WINE_CONFIG_MAKEFILE(dlls/atl/tests) WINE_CONFIG_MAKEFILE(dlls/atl100) diff --git a/dlls/api-ms-win-power-base-l1-1-0/Makefile.in b/dlls/api-ms-win-power-base-l1-1-0/Makefile.in new file mode 100644 index 00000000000..8b26d4be82f --- /dev/null +++ wine/dlls/api-ms-win-power-base-l1-1-0/Makefile.in @@ -0,0 +1 @@ +MODULE = api-ms-win-power-base-l1-1-0.dll diff --git a/dlls/api-ms-win-power-base-l1-1-0/api-ms-win-power-base-l1-1-0.spec b/dlls/api-ms-win-power-base-l1-1-0/api-ms-win-power-base-l1-1-0.spec new file mode 100644 index 00000000000..dd056946ac6 --- /dev/null +++ wine/dlls/api-ms-win-power-base-l1-1-0/api-ms-win-power-base-l1-1-0.spec @@ -0,0 +1,5 @@ +@ stdcall CallNtPowerInformation(long ptr long ptr long) powrprof.CallNtPowerInformation +@ stdcall GetPwrCapabilities(ptr) powrprof.GetPwrCapabilities +@ stdcall PowerDeterminePlatformRoleEx(long) powrprof.PowerDeterminePlatformRoleEx +@ stdcall PowerRegisterSuspendResumeNotification(long ptr ptr) powrprof.PowerRegisterSuspendResumeNotification +@ stub PowerUnregisterSuspendResumeNotification diff --git a/tools/make_specfiles b/tools/make_specfiles index 3c4c40544b8..cdaf0ccf209 100755 --- wine/tools/make_specfiles +++ wine/tools/make_specfiles @@ -139,6 +139,11 @@ my @dll_groups = "sppc", "slc", ], + [ + "ntdll", + "powrprof", + "api-ms-win-power-base-l1-1-0", + ] ); my $update_flags = 0; -- 2.39.2 (Apple Git-144) diff --git a/configure b/configure index b9b57c0d509..24f958073a0 100755 --- wine/configure +++ wine/configure @@ -950,6 +950,7 @@ enable_amstream enable_apisetschema enable_apphelp enable_appwiz_cpl +enable_api_ms_win_core_psm_appnotify_l1_1_0 enable_api_ms_win_power_base_l1_1_0 enable_atl enable_atl100 @@ -21777,6 +21778,7 @@ wine_fn_config_makefile dlls/apisetschema enable_apisetschema wine_fn_config_makefile dlls/apphelp enable_apphelp wine_fn_config_makefile dlls/apphelp/tests enable_tests wine_fn_config_makefile dlls/appwiz.cpl enable_appwiz_cpl +wine_fn_config_makefile dlls/api-ms-win-core-psm-appnotify-l1-1-0 enable_api_ms_win_core_psm_appnotify_l1_1_0 wine_fn_config_makefile dlls/api-ms-win-power-base-l1-1-0 enable_api_ms_win_power_base_l1_1_0 wine_fn_config_makefile dlls/atl enable_atl wine_fn_config_makefile dlls/atl/tests enable_tests diff --git a/dlls/api-ms-win-core-psm-appnotify-l1-1-0/Makefile.in b/dlls/api-ms-win-core-psm-appnotify-l1-1-0/Makefile.in new file mode 100644 index 00000000000..8a3d2ad98cb --- /dev/null +++ wine/dlls/api-ms-win-core-psm-appnotify-l1-1-0/Makefile.in @@ -0,0 +1 @@ +MODULE = api-ms-win-core-psm-appnotify-l1-1-0.dll diff --git a/dlls/api-ms-win-core-psm-appnotify-l1-1-0/api-ms-win-core-psm-appnotify-l1-1-0.spec b/dlls/api-ms-win-core-psm-appnotify-l1-1-0/api-ms-win-core-psm-appnotify-l1-1-0.spec new file mode 100644 index 00000000000..8b069d66e62 --- /dev/null +++ wine/dlls/api-ms-win-core-psm-appnotify-l1-1-0/api-ms-win-core-psm-appnotify-l1-1-0.spec @@ -0,0 +1,2 @@ +@ stub RegisterAppStateChangeNotification +@ stub UnregisterAppStateChangeNotification -- 2.39.2 (Apple Git-144) diff --git a/dlls/crypt32/base64.c b/dlls/crypt32/base64.c index 11fb137ed91..b61ed7ff8cc 100644 --- wine/dlls/crypt32/base64.c +++ wine/dlls/crypt32/base64.c @@ -241,6 +241,63 @@ static BOOL BinaryToBase64A(const BYTE *pbBinary, return ret; } +static BOOL BinaryToHexRawA(const BYTE *bin, DWORD nbin, DWORD flags, char *str, DWORD *nstr) +{ + static const char hex[] = "0123456789abcdef"; + DWORD needed; + + if (flags & CRYPT_STRING_NOCRLF) + needed = 0; + else if (flags & CRYPT_STRING_NOCR) + needed = 1; + else + needed = 2; + + needed += nbin * 2 + 1; + + if (!str) + { + *nstr = needed; + return TRUE; + } + + if (needed > *nstr && *nstr < 3) + { + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + nbin = min(nbin, (*nstr - 1) / 2); + + while (nbin--) + { + *str++ = hex[(*bin >> 4) & 0xf]; + *str++ = hex[*bin & 0xf]; + bin++; + } + + if (needed > *nstr) + { + *str = 0; + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + + if (flags & CRYPT_STRING_NOCR) + { + *str++ = '\n'; + } + else if (!(flags & CRYPT_STRING_NOCRLF)) + { + *str++ = '\r'; + *str++ = '\n'; + } + + *str = 0; + *nstr = needed - 1; + return TRUE; +} + BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary, DWORD cbBinary, DWORD dwFlags, LPSTR pszString, DWORD *pcchString) { @@ -271,6 +328,9 @@ BOOL WINAPI CryptBinaryToStringA(const BYTE *pbBinary, case CRYPT_STRING_BASE64X509CRLHEADER: encoder = BinaryToBase64A; break; + case CRYPT_STRING_HEXRAW: + encoder = BinaryToHexRawA; + break; case CRYPT_STRING_HEX: case CRYPT_STRING_HEXASCII: case CRYPT_STRING_HEXADDR: @@ -883,6 +943,120 @@ static LONG DecodeAnyA(LPCSTR pszString, DWORD cchString, return ret; } +static BOOL is_hex_string_special_char(WCHAR c) +{ + switch (c) + { + case '-': + case ',': + case ' ': + case '\t': + case '\r': + case '\n': + return TRUE; + + default: + return FALSE; + } +} + +static WCHAR wchar_from_str(BOOL wide, const void **str, DWORD *len) +{ + WCHAR c; + + if (!*len) + return 0; + + --*len; + if (wide) + c = *(*(const WCHAR **)str)++; + else + c = *(*(const char **)str)++; + + return c ? c : 0xffff; +} + +static BYTE digit_from_char(WCHAR c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + c = towlower(c); + if (c >= 'a' && c <= 'f') + return c - 'a' + 0xa; + return 0xff; +} + +static LONG string_to_hex(const void* str, BOOL wide, DWORD len, BYTE *hex, DWORD *hex_len, + DWORD *skipped, DWORD *ret_flags) +{ + unsigned int byte_idx = 0; + BYTE d1, d2; + WCHAR c; + + if (!str || !hex_len) + return ERROR_INVALID_PARAMETER; + + if (!len) + len = wide ? wcslen(str) : strlen(str); + + if (wide && !len) + return ERROR_INVALID_PARAMETER; + + if (skipped) + *skipped = 0; + if (ret_flags) + *ret_flags = 0; + + while ((c = wchar_from_str(wide, &str, &len)) && is_hex_string_special_char(c)) + ; + + while ((d1 = digit_from_char(c)) != 0xff) + { + if ((d2 = digit_from_char(wchar_from_str(wide, &str, &len))) == 0xff) + { + if (!hex) + *hex_len = 0; + return ERROR_INVALID_DATA; + } + + if (hex && byte_idx < *hex_len) + hex[byte_idx] = (d1 << 4) | d2; + + ++byte_idx; + + do + { + c = wchar_from_str(wide, &str, &len); + } while (c == '-' || c == ','); + } + + while (c) + { + if (!is_hex_string_special_char(c)) + { + if (!hex) + *hex_len = 0; + return ERROR_INVALID_DATA; + } + c = wchar_from_str(wide, &str, &len); + } + + if (hex && byte_idx > *hex_len) + return ERROR_MORE_DATA; + + if (ret_flags) + *ret_flags = CRYPT_STRING_HEX; + + *hex_len = byte_idx; + + return ERROR_SUCCESS; +} + +static LONG string_to_hexA(const char *str, DWORD len, BYTE *hex, DWORD *hex_len, DWORD *skipped, DWORD *ret_flags) +{ + return string_to_hex(str, FALSE, len, hex, hex_len, skipped, ret_flags); +} + BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString, DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) @@ -928,6 +1102,8 @@ BOOL WINAPI CryptStringToBinaryA(LPCSTR pszString, decoder = DecodeAnyA; break; case CRYPT_STRING_HEX: + decoder = string_to_hexA; + break; case CRYPT_STRING_HEXASCII: case CRYPT_STRING_HEXADDR: case CRYPT_STRING_HEXASCIIADDR: @@ -1094,6 +1270,11 @@ static LONG DecodeAnyW(LPCWSTR pszString, DWORD cchString, return ret; } +static LONG string_to_hexW(const WCHAR *str, DWORD len, BYTE *hex, DWORD *hex_len, DWORD *skipped, DWORD *ret_flags) +{ + return string_to_hex(str, TRUE, len, hex, hex_len, skipped, ret_flags); +} + BOOL WINAPI CryptStringToBinaryW(LPCWSTR pszString, DWORD cchString, DWORD dwFlags, BYTE *pbBinary, DWORD *pcbBinary, DWORD *pdwSkip, DWORD *pdwFlags) @@ -1139,6 +1320,8 @@ BOOL WINAPI CryptStringToBinaryW(LPCWSTR pszString, decoder = DecodeAnyW; break; case CRYPT_STRING_HEX: + decoder = string_to_hexW; + break; case CRYPT_STRING_HEXASCII: case CRYPT_STRING_HEXADDR: case CRYPT_STRING_HEXASCIIADDR: diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c index ad39b7d18c7..b57cc685212 100644 --- wine/dlls/crypt32/cert.c +++ wine/dlls/crypt32/cert.c @@ -1006,6 +1006,7 @@ BOOL WINAPI CryptAcquireCertificatePrivateKey(PCCERT_CONTEXT pCert, CryptMemFree(info); if (cert_in_store) CertFreeCertificateContext(cert_in_store); + if (ret) SetLastError(0); return ret; } diff --git a/dlls/crypt32/chain.c b/dlls/crypt32/chain.c index cf244f2ac6c..4a60e9a60ff 100644 --- wine/dlls/crypt32/chain.c +++ wine/dlls/crypt32/chain.c @@ -3696,6 +3696,44 @@ static BYTE msPubKey4[] = { 0xa6,0xc6,0x48,0x4c,0xc3,0x37,0x51,0x23,0xd3,0x27,0xd7,0xb8,0x4e,0x70,0x96, 0xf0,0xa1,0x44,0x76,0xaf,0x78,0xcf,0x9a,0xe1,0x66,0x13,0x02,0x03,0x01,0x00, 0x01 }; +/* from Microsoft Root Certificate Authority 2011 */ +static BYTE msPubKey5[] = { +0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xb2,0x80,0x41,0xaa,0x35,0x38, +0x4d,0x13,0x72,0x32,0x68,0x22,0x4d,0xb8,0xb2,0xf1,0xff,0xd5,0x52,0xbc,0x6c, +0xc7,0xf5,0xd2,0x4a,0x8c,0x36,0xee,0xd1,0xc2,0x5c,0x7e,0x8c,0x8a,0xae,0xaf, +0x13,0x28,0x6f,0xc0,0x73,0xe3,0x3a,0xce,0xd0,0x25,0xa8,0x5a,0x3a,0x6d,0xef, +0xa8,0xb8,0x59,0xab,0x13,0x23,0x68,0xcd,0x0c,0x29,0x87,0xd1,0x6f,0x80,0x5c, +0x8f,0x44,0x7f,0x5d,0x90,0x01,0x52,0x58,0xac,0x51,0xc5,0x5f,0x2a,0x87,0xdc, +0xdc,0xd8,0x0a,0x1d,0xc1,0x03,0xb9,0x7b,0xb0,0x56,0xe8,0xa3,0xde,0x64,0x61, +0xc2,0x9e,0xf8,0xf3,0x7c,0xb9,0xec,0x0d,0xb5,0x54,0xfe,0x4c,0xb6,0x65,0x4f, +0x88,0xf0,0x9c,0x48,0x99,0x0c,0x42,0x0b,0x09,0x7c,0x31,0x59,0x17,0x79,0x06, +0x78,0x28,0x8d,0x89,0x3a,0x4c,0x03,0x25,0xbe,0x71,0x6a,0x5c,0x0b,0xe7,0x84, +0x60,0xa4,0x99,0x22,0xe3,0xd2,0xaf,0x84,0xa4,0xa7,0xfb,0xd1,0x98,0xed,0x0c, +0xa9,0xde,0x94,0x89,0xe1,0x0e,0xa0,0xdc,0xc0,0xce,0x99,0x3d,0xea,0x08,0x52, +0xbb,0x56,0x79,0xe4,0x1f,0x84,0xba,0x1e,0xb8,0xb4,0xc4,0x49,0x5c,0x4f,0x31, +0x4b,0x87,0xdd,0xdd,0x05,0x67,0x26,0x99,0x80,0xe0,0x71,0x11,0xa3,0xb8,0xa5, +0x41,0xe2,0xa4,0x53,0xb9,0xf7,0x32,0x29,0x83,0x0c,0x13,0xbf,0x36,0x5e,0x04, +0xb3,0x4b,0x43,0x47,0x2f,0x6b,0xe2,0x91,0x1e,0xd3,0x98,0x4f,0xdd,0x42,0x07, +0xc8,0xe8,0x1d,0x12,0xfc,0x99,0xa9,0x6b,0x3e,0x92,0x7e,0xc8,0xd6,0x69,0x3a, +0xfc,0x64,0xbd,0xb6,0x09,0x9d,0xca,0xfd,0x0c,0x0b,0xa2,0x9b,0x77,0x60,0x4b, +0x03,0x94,0xa4,0x30,0x69,0x12,0xd6,0x42,0x2d,0xc1,0x41,0x4c,0xca,0xdc,0xaa, +0xfd,0x8f,0x5b,0x83,0x46,0x9a,0xd9,0xfc,0xb1,0xd1,0xe3,0xb3,0xc9,0x7f,0x48, +0x7a,0xcd,0x24,0xf0,0x41,0x8f,0x5c,0x74,0xd0,0xac,0xb0,0x10,0x20,0x06,0x49, +0xb7,0xc7,0x2d,0x21,0xc8,0x57,0xe3,0xd0,0x86,0xf3,0x03,0x68,0xfb,0xd0,0xce, +0x71,0xc1,0x89,0x99,0x4a,0x64,0x01,0x6c,0xfd,0xec,0x30,0x91,0xcf,0x41,0x3c, +0x92,0xc7,0xe5,0xba,0x86,0x1d,0x61,0x84,0xc7,0x5f,0x83,0x39,0x62,0xae,0xb4, +0x92,0x2f,0x47,0xf3,0x0b,0xf8,0x55,0xeb,0xa0,0x1f,0x59,0xd0,0xbb,0x74,0x9b, +0x1e,0xd0,0x76,0xe6,0xf2,0xe9,0x06,0xd7,0x10,0xe8,0xfa,0x64,0xde,0x69,0xc6, +0x35,0x96,0x88,0x02,0xf0,0x46,0xb8,0x3f,0x27,0x99,0x6f,0xcb,0x71,0x89,0x29, +0x35,0xf7,0x48,0x16,0x02,0x35,0x8f,0xd5,0x79,0x7c,0x4d,0x02,0xcf,0x5f,0xeb, +0x8a,0x83,0x4f,0x45,0x71,0x88,0xf9,0xa9,0x0d,0x4e,0x72,0xe9,0xc2,0x9c,0x07, +0xcf,0x49,0x1b,0x4e,0x04,0x0e,0x63,0x51,0x8c,0x5e,0xd8,0x00,0xc1,0x55,0x2c, +0xb6,0xc6,0xe0,0xc2,0x65,0x4e,0xc9,0x34,0x39,0xf5,0x9c,0xb3,0xc4,0x7e,0xe8, +0x61,0x6e,0x13,0x5f,0x15,0xc4,0x5f,0xd9,0x7e,0xed,0x1d,0xce,0xee,0x44,0xec, +0xcb,0x2e,0x86,0xb1,0xec,0x38,0xf6,0x70,0xed,0xab,0x5c,0x13,0xc1,0xd9,0x0f, +0x0d,0xc7,0x80,0xb2,0x55,0xed,0x34,0xf7,0xac,0x9b,0xe4,0xc3,0xda,0xe7,0x47, +0x3c,0xa6,0xb5,0x8f,0x31,0xdf,0xc5,0x4b,0xaf,0xeb,0xf1,0x02,0x03,0x01,0x00, +0x01 }; static BOOL WINAPI verify_ms_root_policy(LPCSTR szPolicyOID, PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara, @@ -3705,21 +3743,38 @@ static BOOL WINAPI verify_ms_root_policy(LPCSTR szPolicyOID, CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } }; DWORD i; - CRYPT_DATA_BLOB keyBlobs[] = { + static const CRYPT_DATA_BLOB keyBlobs[] = { { sizeof(msPubKey1), msPubKey1 }, { sizeof(msPubKey2), msPubKey2 }, { sizeof(msPubKey3), msPubKey3 }, { sizeof(msPubKey4), msPubKey4 }, }; + static const CRYPT_DATA_BLOB keyBlobs_approot[] = { + { sizeof(msPubKey5), msPubKey5 }, + }; PCERT_SIMPLE_CHAIN rootChain = pChainContext->rgpChain[pChainContext->cChain - 1]; PCCERT_CONTEXT root = rootChain->rgpElement[rootChain->cElement - 1]->pCertContext; - for (i = 0; !isMSRoot && i < ARRAY_SIZE(keyBlobs); i++) + const CRYPT_DATA_BLOB *keys; + unsigned int key_count; + + if (pPolicyPara && pPolicyPara->dwFlags & MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG) + { + keys = keyBlobs_approot; + key_count = ARRAY_SIZE(keyBlobs_approot); + } + else + { + keys = keyBlobs; + key_count = ARRAY_SIZE(keyBlobs); + } + + for (i = 0; !isMSRoot && i < key_count; i++) { - msPubKey.PublicKey.cbData = keyBlobs[i].cbData; - msPubKey.PublicKey.pbData = keyBlobs[i].pbData; + msPubKey.PublicKey.cbData = keys[i].cbData; + msPubKey.PublicKey.pbData = keys[i].pbData; if (CertComparePublicKeyInfo(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, &root->pCertInfo->SubjectPublicKeyInfo, &msPubKey)) isMSRoot = TRUE; } diff --git a/dlls/crypt32/decode.c b/dlls/crypt32/decode.c index 762d1b54661..19643194b49 100644 --- wine/dlls/crypt32/decode.c +++ wine/dlls/crypt32/decode.c @@ -3874,7 +3874,7 @@ static BOOL WINAPI CRYPT_AsnDecodeCertPolicyConstraints( struct DECODED_RSA_PUB_KEY { - DWORD pubexp; + CRYPT_INTEGER_BLOB pubexp; CRYPT_INTEGER_BLOB modulus; }; @@ -3893,12 +3893,23 @@ static BOOL CRYPT_raw_decode_rsa_pub_key(struct DECODED_RSA_PUB_KEY **decodedKey FALSE, TRUE, offsetof(struct DECODED_RSA_PUB_KEY, modulus.pbData), 0 }, { ASN_INTEGER, offsetof(struct DECODED_RSA_PUB_KEY, pubexp), - CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, 0, 0 }, + CRYPT_AsnDecodeUnsignedIntegerInternal, sizeof(CRYPT_INTEGER_BLOB), + FALSE, TRUE, offsetof(struct DECODED_RSA_PUB_KEY, pubexp.pbData), + 0 }, }; ret = CRYPT_AsnDecodeSequence(items, ARRAY_SIZE(items), pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, decodedKey, size, NULL, NULL); + + if (ret && (*decodedKey)->pubexp.cbData > sizeof(DWORD)) + { + WARN("Unexpected exponent length %lu.\n", (*decodedKey)->pubexp.cbData); + LocalFree(*decodedKey); + SetLastError(CRYPT_E_ASN1_LARGE); + ret = FALSE; + } + return ret; } @@ -3920,7 +3931,7 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey_Bcrypt(DWORD dwCertEncodingType, if (ret) { /* Header, exponent, and modulus */ - DWORD bytesNeeded = sizeof(BCRYPT_RSAKEY_BLOB) + sizeof(DWORD) + + DWORD bytesNeeded = sizeof(BCRYPT_RSAKEY_BLOB) + decodedKey->pubexp.cbData + decodedKey->modulus.cbData; if (!pvStructInfo) @@ -3939,7 +3950,7 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey_Bcrypt(DWORD dwCertEncodingType, hdr = pvStructInfo; hdr->Magic = BCRYPT_RSAPUBLIC_MAGIC; hdr->BitLength = decodedKey->modulus.cbData * 8; - hdr->cbPublicExp = sizeof(DWORD); + hdr->cbPublicExp = decodedKey->pubexp.cbData; hdr->cbModulus = decodedKey->modulus.cbData; hdr->cbPrime1 = 0; hdr->cbPrime2 = 0; @@ -3947,9 +3958,9 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey_Bcrypt(DWORD dwCertEncodingType, * in big-endian format, so we need to convert from little-endian */ CRYPT_CopyReversed((BYTE *)pvStructInfo + sizeof(BCRYPT_RSAKEY_BLOB), - (BYTE *)&decodedKey->pubexp, sizeof(DWORD)); + decodedKey->pubexp.pbData, hdr->cbPublicExp); CRYPT_CopyReversed((BYTE *)pvStructInfo + sizeof(BCRYPT_RSAKEY_BLOB) + - sizeof(DWORD), decodedKey->modulus.pbData, + hdr->cbPublicExp, decodedKey->modulus.pbData, decodedKey->modulus.cbData); } LocalFree(decodedKey); @@ -3984,13 +3995,13 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType, if (!pvStructInfo) { *pcbStructInfo = bytesNeeded; - ret = TRUE; } else if ((ret = CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, pcbStructInfo, bytesNeeded))) { BLOBHEADER *hdr; RSAPUBKEY *rsaPubKey; + unsigned int i; if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) pvStructInfo = *(BYTE **)pvStructInfo; @@ -4002,7 +4013,11 @@ static BOOL WINAPI CRYPT_AsnDecodeRsaPubKey(DWORD dwCertEncodingType, rsaPubKey = (RSAPUBKEY *)((BYTE *)pvStructInfo + sizeof(BLOBHEADER)); rsaPubKey->magic = RSA1_MAGIC; - rsaPubKey->pubexp = decodedKey->pubexp; + rsaPubKey->pubexp = 0; + assert(decodedKey->pubexp.cbData <= sizeof(rsaPubKey->pubexp)); + for (i = 0; i < decodedKey->pubexp.cbData; ++i) + rsaPubKey->pubexp |= decodedKey->pubexp.pbData[i] << (i * 8); + rsaPubKey->bitlen = decodedKey->modulus.cbData * 8; memcpy((BYTE *)pvStructInfo + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), decodedKey->modulus.pbData, @@ -6351,6 +6366,112 @@ static BOOL CRYPT_AsnDecodeOCSPNextUpdate(const BYTE *pbEncoded, return ret; } +static BOOL CRYPT_AsnDecodeCertStatus(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + BOOL ret = TRUE; + BYTE tag = pbEncoded[0] & ~3, status = pbEncoded[0] & 3; + DWORD bytesNeeded = FIELD_OFFSET(OCSP_BASIC_RESPONSE_ENTRY, ThisUpdate) - + FIELD_OFFSET(OCSP_BASIC_RESPONSE_ENTRY, dwCertStatus); + + if (!cbEncoded) + { + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + + switch (status) + { + case 0: + if (tag != ASN_CONTEXT) + { + WARN("Unexpected tag %02x\n", tag); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + if (cbEncoded < 2 || pbEncoded[1]) + { + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + if (pvStructInfo) + { + *(DWORD *)pvStructInfo = 0; + *(OCSP_BASIC_REVOKED_INFO **)((char *)pvStructInfo + + FIELD_OFFSET(OCSP_BASIC_RESPONSE_ENTRY, u.pRevokedInfo) + - FIELD_OFFSET(OCSP_BASIC_RESPONSE_ENTRY, dwCertStatus)) = NULL; + } + *pcbStructInfo = bytesNeeded; + *pcbDecoded = 2; + break; + + case 1: + { + DWORD dataLen; + + if (tag != (ASN_CONTEXT | ASN_CONSTRUCTOR)) + { + WARN("Unexpected tag %02x\n", tag); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen))) + { + BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]); + DWORD bytesDecoded, size; + FILETIME date; + + if (dataLen) + { + size = sizeof(date); + ret = CRYPT_AsnDecodeGeneralizedTime(pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes, + dwFlags, &date, &size, &bytesDecoded); + if (ret) + { + OCSP_BASIC_REVOKED_INFO *info; + + bytesNeeded += sizeof(*info); + if (!pvStructInfo) + *pcbStructInfo = bytesNeeded; + else if (*pcbStructInfo < bytesNeeded) + { + *pcbStructInfo = bytesNeeded; + SetLastError(ERROR_MORE_DATA); + return FALSE; + } + if (pvStructInfo) + { + *(DWORD *)pvStructInfo = 1; + info = *(OCSP_BASIC_REVOKED_INFO **)((char *)pvStructInfo + + FIELD_OFFSET(OCSP_BASIC_RESPONSE_ENTRY, u.pRevokedInfo) + - FIELD_OFFSET(OCSP_BASIC_RESPONSE_ENTRY, dwCertStatus)); + info->RevocationDate = date; + } + *pcbStructInfo = bytesNeeded; + *pcbDecoded = 1 + lenBytes + bytesDecoded; + } + } + } + break; + } + default: + FIXME("Unhandled status %u\n", status); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } + + return ret; +} + static BOOL CRYPT_AsnDecodeOCSPBasicResponseEntry(const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded) { @@ -6367,10 +6488,9 @@ static BOOL CRYPT_AsnDecodeOCSPBasicResponseEntry(const BYTE *pbEncoded, DWORD c { ASN_INTEGER, offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.SerialNumber), CRYPT_AsnDecodeIntegerInternal, sizeof(CRYPT_INTEGER_BLOB), FALSE, TRUE, offsetof(OCSP_BASIC_RESPONSE_ENTRY, CertId.SerialNumber.pbData), 0 }, - { ASN_CONTEXT, offsetof(OCSP_BASIC_RESPONSE_ENTRY, dwCertStatus), - CRYPT_AsnDecodeIntInternal, sizeof(DWORD), FALSE, FALSE, - 0, 0 }, - /* FIXME: pRevokedInfo */ + { 0, offsetof(OCSP_BASIC_RESPONSE_ENTRY, dwCertStatus), + CRYPT_AsnDecodeCertStatus, sizeof(DWORD), FALSE, TRUE, + offsetof(OCSP_BASIC_RESPONSE_ENTRY, u.pRevokedInfo), 0 }, { ASN_GENERALTIME, offsetof(OCSP_BASIC_RESPONSE_ENTRY, ThisUpdate), CRYPT_AsnDecodeGeneralizedTime, sizeof(FILETIME), FALSE, FALSE, 0, 0 }, @@ -6401,13 +6521,13 @@ static BOOL CRYPT_AsnDecodeOCSPBasicResponseEntriesArray(const BYTE *pbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo, pcbDecoded); } -static BOOL CRYPT_AsnDecodeResponderID(const BYTE *pbEncoded, +static BOOL CRYPT_AsnDecodeResponderIDByName(const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, DWORD *pcbDecoded) { OCSP_BASIC_RESPONSE_INFO *info = pvStructInfo; - BYTE tag = pbEncoded[0] & ~3, choice = pbEncoded[0] & 3; - DWORD decodedLen, dataLen, lenBytes, bytesNeeded = sizeof(*info), len; + DWORD dataLen, decodedLen, lenBytes, bytesNeeded = sizeof(*info); + BYTE tag = pbEncoded[0] & ~3; CERT_NAME_BLOB *blob; if (tag != (ASN_CONTEXT | ASN_CONSTRUCTOR)) @@ -6416,15 +6536,78 @@ static BOOL CRYPT_AsnDecodeResponderID(const BYTE *pbEncoded, SetLastError(CRYPT_E_ASN1_BADTAG); return FALSE; } - if (choice > 2) + + if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)) + return FALSE; + lenBytes = GET_LEN_BYTES(pbEncoded[1]); + cbEncoded -= 1 + lenBytes; + + if (dataLen > cbEncoded) { - WARN("Unexpected choice %02x\n", choice); - SetLastError(CRYPT_E_ASN1_CORRUPT); + SetLastError(CRYPT_E_ASN1_EOD); + return FALSE; + } + pbEncoded += 1 + lenBytes; + decodedLen = 1 + lenBytes + dataLen; + + if (pbEncoded[0] != ASN_SEQUENCE) + { + WARN("Unexpected tag %02x %02x\n", pbEncoded[0], pbEncoded[1]); + SetLastError(CRYPT_E_ASN1_BADTAG); return FALSE; } + if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) bytesNeeded += dataLen; if (pvStructInfo && *pcbStructInfo >= bytesNeeded) - info->dwResponderIdChoice = choice; + { + info->dwResponderIdChoice = 1; + + blob = &info->u.ByNameResponderId; + blob->cbData = dataLen; + if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) + blob->pbData = (BYTE *)pbEncoded; + else if (blob->cbData) + { + blob->pbData = (BYTE *)(info + 1); + memcpy(blob->pbData, pbEncoded, blob->cbData); + } + } + + if (pcbDecoded) + *pcbDecoded = decodedLen; + + if (!pvStructInfo) + { + *pcbStructInfo = bytesNeeded; + return TRUE; + } + + if (*pcbStructInfo < bytesNeeded) + { + SetLastError(ERROR_MORE_DATA); + *pcbStructInfo = bytesNeeded; + return FALSE; + } + + *pcbStructInfo = bytesNeeded; + return TRUE; +} + +static BOOL CRYPT_AsnDecodeResponderIDByKey(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + OCSP_BASIC_RESPONSE_INFO *info = pvStructInfo; + DWORD dataLen, decodedLen, lenBytes, bytesNeeded = sizeof(*info), len; + BYTE tag = pbEncoded[0] & ~3; + CRYPT_HASH_BLOB *blob; + + if (tag != (ASN_CONTEXT | ASN_CONSTRUCTOR)) + { + WARN("Unexpected tag %02x\n", tag); + SetLastError(CRYPT_E_ASN1_BADTAG); + return FALSE; + } if (!CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)) return FALSE; @@ -6461,7 +6644,8 @@ static BOOL CRYPT_AsnDecodeResponderID(const BYTE *pbEncoded, if (!(dwFlags & CRYPT_DECODE_NOCOPY_FLAG)) bytesNeeded += len; if (pvStructInfo && *pcbStructInfo >= bytesNeeded) { - blob = &info->u.ByNameResponderId; + info->dwResponderIdChoice = 2; + blob = &info->u.ByKeyResponderId; blob->cbData = len; if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG) blob->pbData = (BYTE *)pbEncoded; @@ -6492,6 +6676,28 @@ static BOOL CRYPT_AsnDecodeResponderID(const BYTE *pbEncoded, return TRUE; } +static BOOL CRYPT_AsnDecodeResponderID(const BYTE *pbEncoded, + DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo, + DWORD *pcbDecoded) +{ + BYTE choice = pbEncoded[0] & 3; + + TRACE("choice %02x\n", choice); + switch (choice) + { + case 1: + return CRYPT_AsnDecodeResponderIDByName(pbEncoded, cbEncoded, dwFlags, + pvStructInfo, pcbStructInfo, pcbDecoded); + case 2: + return CRYPT_AsnDecodeResponderIDByKey(pbEncoded, cbEncoded, dwFlags, + pvStructInfo, pcbStructInfo, pcbDecoded); + default: + WARN("Unexpected choice %02x\n", choice); + SetLastError(CRYPT_E_ASN1_CORRUPT); + return FALSE; + } +} + static BOOL WINAPI CRYPT_AsnDecodeOCSPBasicResponse(DWORD dwCertEncodingType, LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, CRYPT_DECODE_PARA *pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) diff --git a/dlls/crypt32/encode.c b/dlls/crypt32/encode.c index 8086ad2fc0a..8968eb9f15a 100644 --- wine/dlls/crypt32/encode.c +++ wine/dlls/crypt32/encode.c @@ -4500,7 +4500,7 @@ static BOOL WINAPI CRYPT_AsnEncodeCertId(DWORD dwCertEncodingType, return ret; } -static BOOL WINAPI CRYPT_AsnEncodeOCSPRequestEntry(DWORD dwCertEncodingType, +static BOOL CRYPT_AsnEncodeOCSPRequestEntry(DWORD dwCertEncodingType, LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) { diff --git a/dlls/crypt32/object.c b/dlls/crypt32/object.c index 8123ed73351..4440c9e0498 100644 --- wine/dlls/crypt32/object.c +++ wine/dlls/crypt32/object.c @@ -643,7 +643,7 @@ static BOOL CRYPT_QueryEmbeddedMessageObject(DWORD dwObjectType, } file = CreateFileW(temp_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); - if (file == INVALID_HANDLE_VALUE) + if (!file) { ERR("Could not create temp file.\n"); SetLastError(ERROR_OUTOFMEMORY); diff --git a/dlls/crypt32/str.c b/dlls/crypt32/str.c index 277aeb70d4a..d74df308e4a 100644 --- wine/dlls/crypt32/str.c +++ wine/dlls/crypt32/str.c @@ -29,77 +29,45 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt); -DWORD WINAPI CertRDNValueToStrA(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, - LPSTR psz, DWORD csz) +DWORD WINAPI CertRDNValueToStrA(DWORD type, PCERT_RDN_VALUE_BLOB value_blob, + LPSTR value, DWORD value_len) { - DWORD ret = 0, len; + DWORD len, len_mb, ret; + LPWSTR valueW; - TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz); + TRACE("(%ld, %p, %p, %ld)\n", type, value_blob, value, value_len); - switch (dwValueType) - { - case CERT_RDN_ANY_TYPE: - break; - case CERT_RDN_NUMERIC_STRING: - case CERT_RDN_PRINTABLE_STRING: - case CERT_RDN_TELETEX_STRING: - case CERT_RDN_VIDEOTEX_STRING: - case CERT_RDN_IA5_STRING: - case CERT_RDN_GRAPHIC_STRING: - case CERT_RDN_VISIBLE_STRING: - case CERT_RDN_GENERAL_STRING: - len = pValue->cbData; - if (!psz || !csz) - ret = len; - else - { - DWORD chars = min(len, csz - 1); + len = CertRDNValueToStrW(type, value_blob, NULL, 0); - if (chars) - { - memcpy(psz, pValue->pbData, chars); - ret += chars; - csz -= chars; - } - } - break; - case CERT_RDN_BMP_STRING: - case CERT_RDN_UTF8_STRING: - len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData, - pValue->cbData / sizeof(WCHAR), NULL, 0, NULL, NULL); - if (!psz || !csz) - ret = len; - else - { - DWORD chars = min(pValue->cbData / sizeof(WCHAR), csz - 1); + if (!(valueW = CryptMemAlloc(len * sizeof(*valueW)))) + { + ERR("No memory.\n"); + if (value && value_len) *value = 0; + return 1; + } - if (chars) - { - ret = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData, - chars, psz, csz - 1, NULL, NULL); - csz -= ret; - } - } - break; - default: - FIXME("string type %ld unimplemented\n", dwValueType); + len = CertRDNValueToStrW(type, value_blob, valueW, len); + len_mb = WideCharToMultiByte(CP_ACP, 0, valueW, len, NULL, 0, NULL, NULL); + if (!value || !value_len) + { + CryptMemFree(valueW); + return len_mb; } - if (psz && csz) + + ret = WideCharToMultiByte(CP_ACP, 0, valueW, len, value, value_len, NULL, NULL); + if (ret < len_mb) { - *(psz + ret) = '\0'; - csz--; - ret++; + value[0] = 0; + ret = 1; } - else - ret++; - TRACE("returning %ld (%s)\n", ret, debugstr_a(psz)); + CryptMemFree(valueW); return ret; } -DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, - LPWSTR psz, DWORD csz) +static DWORD rdn_value_to_strW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, + LPWSTR psz, DWORD csz, BOOL partial_copy) { - DWORD ret = 0, len, i, strLen; + DWORD ret = 0, len, i; TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz); @@ -116,44 +84,42 @@ DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, case CERT_RDN_VISIBLE_STRING: case CERT_RDN_GENERAL_STRING: len = pValue->cbData; - if (!psz || !csz) - ret = len; - else + if (!psz || !csz) ret = len; + else if (len < csz || partial_copy) { - WCHAR *ptr = psz; - - for (i = 0; i < pValue->cbData && ptr - psz < csz; ptr++, i++) - *ptr = pValue->pbData[i]; - ret = ptr - psz; + len = min(len, csz - 1); + for (i = 0; i < len; ++i) + psz[i] = pValue->pbData[i]; + ret = len; } break; case CERT_RDN_BMP_STRING: case CERT_RDN_UTF8_STRING: - strLen = len = pValue->cbData / sizeof(WCHAR); + len = pValue->cbData / sizeof(WCHAR); if (!psz || !csz) ret = len; - else + else if (len < csz || partial_copy) { WCHAR *ptr = psz; - for (i = 0; i < strLen && ptr - psz < csz; ptr++, i++) - *ptr = ((LPCWSTR)pValue->pbData)[i]; - ret = ptr - psz; + len = min(len, csz - 1); + for (i = 0; i < len; ++i) + ptr[i] = ((LPCWSTR)pValue->pbData)[i]; + ret = len; } break; default: FIXME("string type %ld unimplemented\n", dwValueType); } - if (psz && csz) - { - *(psz + ret) = '\0'; - csz--; - ret++; - } - else - ret++; - TRACE("returning %ld (%s)\n", ret, debugstr_w(psz)); - return ret; + if (psz && csz) psz[ret] = 0; + TRACE("returning %ld (%s)\n", ret + 1, debugstr_w(psz)); + return ret + 1; +} + +DWORD WINAPI CertRDNValueToStrW(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, + LPWSTR psz, DWORD csz) +{ + return rdn_value_to_strW(dwValueType, pValue, psz, csz, FALSE); } static inline BOOL is_quotable_char(WCHAR c) @@ -175,115 +141,6 @@ static inline BOOL is_quotable_char(WCHAR c) } } -static DWORD quote_rdn_value_to_str_a(DWORD dwValueType, - PCERT_RDN_VALUE_BLOB pValue, LPSTR psz, DWORD csz) -{ - DWORD ret = 0, len, i; - BOOL needsQuotes = FALSE; - - TRACE("(%ld, %p, %p, %ld)\n", dwValueType, pValue, psz, csz); - - switch (dwValueType) - { - case CERT_RDN_ANY_TYPE: - break; - case CERT_RDN_NUMERIC_STRING: - case CERT_RDN_PRINTABLE_STRING: - case CERT_RDN_TELETEX_STRING: - case CERT_RDN_VIDEOTEX_STRING: - case CERT_RDN_IA5_STRING: - case CERT_RDN_GRAPHIC_STRING: - case CERT_RDN_VISIBLE_STRING: - case CERT_RDN_GENERAL_STRING: - len = pValue->cbData; - if (pValue->cbData && isspace(pValue->pbData[0])) - needsQuotes = TRUE; - if (pValue->cbData && isspace(pValue->pbData[pValue->cbData - 1])) - needsQuotes = TRUE; - for (i = 0; i < pValue->cbData; i++) - { - if (is_quotable_char(pValue->pbData[i])) - needsQuotes = TRUE; - if (pValue->pbData[i] == '"') - len += 1; - } - if (needsQuotes) - len += 2; - if (!psz || !csz) - ret = len; - else - { - char *ptr = psz; - - if (needsQuotes) - *ptr++ = '"'; - for (i = 0; i < pValue->cbData && ptr - psz < csz; ptr++, i++) - { - *ptr = pValue->pbData[i]; - if (pValue->pbData[i] == '"' && ptr - psz < csz - 1) - *(++ptr) = '"'; - } - if (needsQuotes && ptr - psz < csz) - *ptr++ = '"'; - ret = ptr - psz; - } - break; - case CERT_RDN_BMP_STRING: - case CERT_RDN_UTF8_STRING: - len = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)pValue->pbData, - pValue->cbData / sizeof(WCHAR), NULL, 0, NULL, NULL); - if (pValue->cbData && iswspace(((LPCWSTR)pValue->pbData)[0])) - needsQuotes = TRUE; - if (pValue->cbData && - iswspace(((LPCWSTR)pValue->pbData)[pValue->cbData / sizeof(WCHAR)-1])) - needsQuotes = TRUE; - for (i = 0; i < pValue->cbData / sizeof(WCHAR); i++) - { - if (is_quotable_char(((LPCWSTR)pValue->pbData)[i])) - needsQuotes = TRUE; - if (((LPCWSTR)pValue->pbData)[i] == '"') - len += 1; - } - if (needsQuotes) - len += 2; - if (!psz || !csz) - ret = len; - else - { - char *dst = psz; - - if (needsQuotes) - *dst++ = '"'; - for (i = 0; i < pValue->cbData / sizeof(WCHAR) && - dst - psz < csz; dst++, i++) - { - LPCWSTR src = (LPCWSTR)pValue->pbData + i; - - WideCharToMultiByte(CP_ACP, 0, src, 1, dst, - csz - (dst - psz) - 1, NULL, NULL); - if (*src == '"' && dst - psz < csz - 1) - *(++dst) = '"'; - } - if (needsQuotes && dst - psz < csz) - *dst++ = '"'; - ret = dst - psz; - } - break; - default: - FIXME("string type %ld unimplemented\n", dwValueType); - } - if (psz && csz) - { - *(psz + ret) = '\0'; - csz--; - ret++; - } - else - ret++; - TRACE("returning %ld (%s)\n", ret, debugstr_a(psz)); - return ret; -} - static DWORD quote_rdn_value_to_str_w(DWORD dwValueType, PCERT_RDN_VALUE_BLOB pValue, LPWSTR psz, DWORD csz) { @@ -375,148 +232,41 @@ static DWORD quote_rdn_value_to_str_w(DWORD dwValueType, default: FIXME("string type %ld unimplemented\n", dwValueType); } - if (psz && csz) - { - *(psz + ret) = '\0'; - csz--; - ret++; - } - else - ret++; TRACE("returning %ld (%s)\n", ret, debugstr_w(psz)); return ret; } -/* Adds the prefix prefix to the string pointed to by psz, followed by the - * character '='. Copies no more than csz characters. Returns the number of - * characters copied. If psz is NULL, returns the number of characters that - * would be copied. - */ -static DWORD CRYPT_AddPrefixA(LPCSTR prefix, LPSTR psz, DWORD csz) +DWORD WINAPI CertNameToStrA(DWORD encoding_type, PCERT_NAME_BLOB name_blob, DWORD str_type, LPSTR str, DWORD str_len) { - DWORD chars; + DWORD len, len_mb, ret; + LPWSTR strW; - TRACE("(%s, %p, %ld)\n", debugstr_a(prefix), psz, csz); + TRACE("(%ld, %p, %08lx, %p, %ld)\n", encoding_type, name_blob, str_type, str, str_len); - if (psz) + len = CertNameToStrW(encoding_type, name_blob, str_type, NULL, 0); + + if (!(strW = CryptMemAlloc(len * sizeof(*strW)))) { - chars = min(strlen(prefix), csz); - memcpy(psz, prefix, chars); - *(psz + chars) = '='; - chars++; + ERR("No memory.\n"); + if (str && str_len) *str = 0; + return 1; } - else - chars = lstrlenA(prefix) + 1; - return chars; -} -DWORD WINAPI CertNameToStrA(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, - DWORD dwStrType, LPSTR psz, DWORD csz) -{ - static const DWORD unsupportedFlags = CERT_NAME_STR_NO_QUOTING_FLAG | - CERT_NAME_STR_ENABLE_T61_UNICODE_FLAG; - static const char commaSep[] = ", "; - static const char semiSep[] = "; "; - static const char crlfSep[] = "\r\n"; - static const char plusSep[] = " + "; - static const char spaceSep[] = " "; - DWORD ret = 0, bytes = 0; - BOOL bRet; - CERT_NAME_INFO *info; - - TRACE("(%ld, %p, %08lx, %p, %ld)\n", dwCertEncodingType, pName, dwStrType, - psz, csz); - if (dwStrType & unsupportedFlags) - FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags); - - bRet = CryptDecodeObjectEx(dwCertEncodingType, X509_NAME, pName->pbData, - pName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &bytes); - if (bRet) + len = CertNameToStrW(encoding_type, name_blob, str_type, strW, len); + len_mb = WideCharToMultiByte(CP_ACP, 0, strW, len, NULL, 0, NULL, NULL); + if (!str || !str_len) { - DWORD i, j, sepLen, rdnSepLen; - LPCSTR sep, rdnSep; - BOOL reverse = dwStrType & CERT_NAME_STR_REVERSE_FLAG; - const CERT_RDN *rdn = info->rgRDN; - - if(reverse && info->cRDN > 1) rdn += (info->cRDN - 1); - - if (dwStrType & CERT_NAME_STR_SEMICOLON_FLAG) - sep = semiSep; - else if (dwStrType & CERT_NAME_STR_CRLF_FLAG) - sep = crlfSep; - else - sep = commaSep; - sepLen = strlen(sep); - if (dwStrType & CERT_NAME_STR_NO_PLUS_FLAG) - rdnSep = spaceSep; - else - rdnSep = plusSep; - rdnSepLen = strlen(rdnSep); - for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++) - { - for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++) - { - DWORD chars; - char prefixBuf[13]; /* big enough for SERIALNUMBER */ - LPCSTR prefix = NULL; - - if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) - prefix = rdn->rgRDNAttr[j].pszObjId; - else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) - { - PCCRYPT_OID_INFO oidInfo = CryptFindOIDInfo( - CRYPT_OID_INFO_OID_KEY, - rdn->rgRDNAttr[j].pszObjId, - CRYPT_RDN_ATTR_OID_GROUP_ID); - - if (oidInfo) - { - WideCharToMultiByte(CP_ACP, 0, oidInfo->pwszName, -1, - prefixBuf, sizeof(prefixBuf), NULL, NULL); - prefix = prefixBuf; - } - else - prefix = rdn->rgRDNAttr[j].pszObjId; - } - if (prefix) - { - /* - 1 is needed to account for the NULL terminator. */ - chars = CRYPT_AddPrefixA(prefix, - psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); - ret += chars; - } - chars = quote_rdn_value_to_str_a( - rdn->rgRDNAttr[j].dwValueType, - &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL, - psz ? csz - ret : 0); - if (chars) - ret += chars - 1; - if (j < rdn->cRDNAttr - 1) - { - if (psz && ret < csz - rdnSepLen - 1) - memcpy(psz + ret, rdnSep, rdnSepLen); - ret += rdnSepLen; - } - } - if (i < info->cRDN - 1) - { - if (psz && ret < csz - sepLen - 1) - memcpy(psz + ret, sep, sepLen); - ret += sepLen; - } - if(reverse) rdn--; - else rdn++; - } - LocalFree(info); + CryptMemFree(strW); + return len_mb; } - if (psz && csz) + + ret = WideCharToMultiByte(CP_ACP, 0, strW, len, str, str_len, NULL, NULL); + if (ret < len_mb) { - *(psz + ret) = '\0'; - ret++; + str[0] = 0; + ret = 1; } - else - ret++; - TRACE("Returning %s\n", debugstr_a(psz)); + CryptMemFree(strW); return ret; } @@ -580,6 +330,7 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel, DWORD ret = 0, bytes = 0; BOOL bRet; CERT_NAME_INFO *info; + DWORD chars; if (dwStrType & unsupportedFlags) FIXME("unsupported flags: %08lx\n", dwStrType & unsupportedFlags); @@ -607,14 +358,17 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel, else rdnSep = L" + "; rdnSepLen = lstrlenW(rdnSep); - for (i = 0; (!psz || ret < csz) && i < info->cRDN; i++) + if (!csz) psz = NULL; + for (i = 0; i < info->cRDN; i++) { - for (j = 0; (!psz || ret < csz) && j < rdn->cRDNAttr; j++) + if (psz && ret + 1 == csz) break; + for (j = 0; j < rdn->cRDNAttr; j++) { - DWORD chars; LPCSTR prefixA = NULL; LPCWSTR prefixW = NULL; + if (psz && ret + 1 == csz) break; + if ((dwStrType & 0x000000ff) == CERT_OID_NAME_STR) prefixA = rdn->rgRDNAttr[j].pszObjId; else if ((dwStrType & 0x000000ff) == CERT_X500_NAME_STR) @@ -644,6 +398,7 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel, chars = lstrlenW(indent); ret += chars; } + if (psz && ret + 1 == csz) break; } if (prefixW) { @@ -659,38 +414,40 @@ DWORD cert_name_to_str_with_indent(DWORD dwCertEncodingType, DWORD indentLevel, psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); ret += chars; } - chars = quote_rdn_value_to_str_w( - rdn->rgRDNAttr[j].dwValueType, - &rdn->rgRDNAttr[j].Value, psz ? psz + ret : NULL, - psz ? csz - ret : 0); - if (chars) - ret += chars - 1; + if (psz && ret + 1 == csz) break; + + chars = quote_rdn_value_to_str_w(rdn->rgRDNAttr[j].dwValueType, &rdn->rgRDNAttr[j].Value, + psz ? psz + ret : NULL, psz ? csz - ret - 1 : 0); + ret += chars; if (j < rdn->cRDNAttr - 1) { - if (psz && ret < csz - rdnSepLen - 1) - memcpy(psz + ret, rdnSep, rdnSepLen * sizeof(WCHAR)); - ret += rdnSepLen; + if (psz) + { + chars = min(rdnSepLen, csz - ret - 1); + memcpy(psz + ret, rdnSep, chars * sizeof(WCHAR)); + ret += chars; + } + else ret += rdnSepLen; } } + if (psz && ret + 1 == csz) break; if (i < info->cRDN - 1) { - if (psz && ret < csz - sepLen - 1) - memcpy(psz + ret, sep, sepLen * sizeof(WCHAR)); - ret += sepLen; + if (psz) + { + chars = min(sepLen, csz - ret - 1); + memcpy(psz + ret, sep, chars * sizeof(WCHAR)); + ret += chars; + } + else ret += sepLen; } if(reverse) rdn--; else rdn++; } LocalFree(info); } - if (psz && csz) - { - *(psz + ret) = '\0'; - ret++; - } - else - ret++; - return ret; + if (psz && csz) psz[ret] = 0; + return ret + 1; } DWORD WINAPI CertNameToStrW(DWORD dwCertEncodingType, PCERT_NAME_BLOB pName, @@ -1113,49 +870,74 @@ BOOL WINAPI CertStrToNameW(DWORD dwCertEncodingType, LPCWSTR pszX500, return ret; } -DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, - DWORD dwFlags, void *pvTypePara, LPSTR pszNameString, DWORD cchNameString) +DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT cert, DWORD type, + DWORD flags, void *type_para, LPSTR name, DWORD name_len) { - DWORD ret; + DWORD len, len_mb, ret; + LPWSTR nameW; + + TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", cert, type, flags, type_para, name, name_len); - TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType, dwFlags, - pvTypePara, pszNameString, cchNameString); + len = CertGetNameStringW(cert, type, flags, type_para, NULL, 0); - if (pszNameString) + if (!(nameW = CryptMemAlloc(len * sizeof(*nameW)))) { - LPWSTR wideName; - DWORD nameLen; + ERR("No memory.\n"); + if (name && name_len) *name = 0; + return 1; + } - nameLen = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara, - NULL, 0); - wideName = CryptMemAlloc(nameLen * sizeof(WCHAR)); - if (wideName) - { - CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara, - wideName, nameLen); - nameLen = WideCharToMultiByte(CP_ACP, 0, wideName, nameLen, - pszNameString, cchNameString, NULL, NULL); - if (nameLen <= cchNameString) - ret = nameLen; - else - { - pszNameString[cchNameString - 1] = '\0'; - ret = cchNameString; - } - CryptMemFree(wideName); - } - else - { - *pszNameString = '\0'; - ret = 1; - } + len = CertGetNameStringW(cert, type, flags, type_para, nameW, len); + len_mb = WideCharToMultiByte(CP_ACP, 0, nameW, len, NULL, 0, NULL, NULL); + if (!name || !name_len) + { + CryptMemFree(nameW); + return len_mb; } - else - ret = CertGetNameStringW(pCertContext, dwType, dwFlags, pvTypePara, - NULL, 0); + + ret = WideCharToMultiByte(CP_ACP, 0, nameW, len, name, name_len, NULL, NULL); + if (ret < len_mb) + { + name[0] = 0; + ret = 1; + } + CryptMemFree(nameW); return ret; } +static BOOL cert_get_alt_name_info(PCCERT_CONTEXT cert, BOOL alt_name_issuer, PCERT_ALT_NAME_INFO *info) +{ + static const char *oids[][2] = + { + { szOID_SUBJECT_ALT_NAME2, szOID_SUBJECT_ALT_NAME }, + { szOID_ISSUER_ALT_NAME2, szOID_ISSUER_ALT_NAME }, + }; + PCERT_EXTENSION ext; + DWORD bytes = 0; + + ext = CertFindExtension(oids[!!alt_name_issuer][0], cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); + if (!ext) + ext = CertFindExtension(oids[!!alt_name_issuer][1], cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); + if (!ext) return FALSE; + + return CryptDecodeObjectEx(cert->dwCertEncodingType, X509_ALTERNATE_NAME, ext->Value.pbData, ext->Value.cbData, + CRYPT_DECODE_ALLOC_FLAG, NULL, info, &bytes); +} + +static PCERT_ALT_NAME_ENTRY cert_find_next_alt_name_entry(PCERT_ALT_NAME_INFO info, DWORD entry_type, + unsigned int *index) +{ + unsigned int i; + + for (i = *index; i < info->cAltEntry; ++i) + if (info->rgAltEntry[i].dwAltNameChoice == entry_type) + { + *index = i + 1; + return &info->rgAltEntry[i]; + } + return NULL; +} + /* Searches cert's extensions for the alternate name extension with OID * altNameOID, and if found, searches it for the alternate name type entryType. * If found, returns a pointer to the entry, otherwise returns NULL. @@ -1165,31 +947,13 @@ DWORD WINAPI CertGetNameStringA(PCCERT_CONTEXT pCertContext, DWORD dwType, * The return value is a pointer within *info, so don't free *info before * you're done with the return value. */ -static PCERT_ALT_NAME_ENTRY cert_find_alt_name_entry(PCCERT_CONTEXT cert, - LPCSTR altNameOID, DWORD entryType, PCERT_ALT_NAME_INFO *info) +static PCERT_ALT_NAME_ENTRY cert_find_alt_name_entry(PCCERT_CONTEXT cert, BOOL alt_name_issuer, + DWORD entry_type, PCERT_ALT_NAME_INFO *info) { - PCERT_ALT_NAME_ENTRY entry = NULL; - PCERT_EXTENSION ext = CertFindExtension(altNameOID, - cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension); - - if (ext) - { - DWORD bytes = 0; + unsigned int index = 0; - if (CryptDecodeObjectEx(cert->dwCertEncodingType, X509_ALTERNATE_NAME, - ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, - info, &bytes)) - { - DWORD i; - - for (i = 0; !entry && i < (*info)->cAltEntry; i++) - if ((*info)->rgAltEntry[i].dwAltNameChoice == entryType) - entry = &(*info)->rgAltEntry[i]; - } - } - else - *info = NULL; - return entry; + if (!cert_get_alt_name_info(cert, alt_name_issuer, info)) return NULL; + return cert_find_next_alt_name_entry(*info, entry_type, &index); } static DWORD cert_get_name_from_rdn_attr(DWORD encodingType, @@ -1207,222 +971,195 @@ static DWORD cert_get_name_from_rdn_attr(DWORD encodingType, oid = szOID_RSA_emailAddr; nameAttr = CertFindRDNAttr(oid, nameInfo); if (nameAttr) - ret = CertRDNValueToStrW(nameAttr->dwValueType, &nameAttr->Value, - pszNameString, cchNameString); + ret = rdn_value_to_strW(nameAttr->dwValueType, &nameAttr->Value, + pszNameString, cchNameString, TRUE); LocalFree(nameInfo); } return ret; } -DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT pCertContext, DWORD dwType, - DWORD dwFlags, void *pvTypePara, LPWSTR pszNameString, DWORD cchNameString) +static DWORD copy_output_str(WCHAR *dst, const WCHAR *src, DWORD dst_size) { - DWORD ret = 0; + DWORD len = wcslen(src); + + if (!dst || !dst_size) return len + 1; + len = min(len, dst_size - 1); + memcpy(dst, src, len * sizeof(*dst)); + dst[len] = 0; + return len + 1; +} + +DWORD WINAPI CertGetNameStringW(PCCERT_CONTEXT cert, DWORD type, DWORD flags, void *type_para, + LPWSTR name_string, DWORD name_len) +{ + static const DWORD supported_flags = CERT_NAME_ISSUER_FLAG | CERT_NAME_SEARCH_ALL_NAMES_FLAG; + BOOL alt_name_issuer, search_all_names; + CERT_ALT_NAME_INFO *info = NULL; + PCERT_ALT_NAME_ENTRY entry; PCERT_NAME_BLOB name; - LPCSTR altNameOID; + DWORD ret = 0; - TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", pCertContext, dwType, - dwFlags, pvTypePara, pszNameString, cchNameString); + TRACE("(%p, %ld, %08lx, %p, %p, %ld)\n", cert, type, flags, type_para, name_string, name_len); - if (!pCertContext) + if (!cert) goto done; - if (dwFlags & CERT_NAME_ISSUER_FLAG) - { - name = &pCertContext->pCertInfo->Issuer; - altNameOID = szOID_ISSUER_ALT_NAME; - } - else + if (flags & ~supported_flags) + FIXME("Unsupported flags %#lx.\n", flags); + + search_all_names = flags & CERT_NAME_SEARCH_ALL_NAMES_FLAG; + if (search_all_names && type != CERT_NAME_DNS_TYPE) { - name = &pCertContext->pCertInfo->Subject; - altNameOID = szOID_SUBJECT_ALT_NAME; + WARN("CERT_NAME_SEARCH_ALL_NAMES_FLAG used with type %lu.\n", type); + goto done; } - switch (dwType) + alt_name_issuer = flags & CERT_NAME_ISSUER_FLAG; + name = alt_name_issuer ? &cert->pCertInfo->Issuer : &cert->pCertInfo->Subject; + + switch (type) { case CERT_NAME_EMAIL_TYPE: { - CERT_ALT_NAME_INFO *info; - PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, - altNameOID, CERT_ALT_NAME_RFC822_NAME, &info); + entry = cert_find_alt_name_entry(cert, alt_name_issuer, CERT_ALT_NAME_RFC822_NAME, &info); if (entry) { - if (!pszNameString) - ret = lstrlenW(entry->u.pwszRfc822Name) + 1; - else if (cchNameString) - { - ret = min(lstrlenW(entry->u.pwszRfc822Name), cchNameString - 1); - memcpy(pszNameString, entry->u.pwszRfc822Name, - ret * sizeof(WCHAR)); - pszNameString[ret++] = 0; - } + ret = copy_output_str(name_string, entry->u.pwszRfc822Name, name_len); + break; } - if (info) - LocalFree(info); - if (!ret) - ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType, - name, szOID_RSA_emailAddr, pszNameString, cchNameString); + ret = cert_get_name_from_rdn_attr(cert->dwCertEncodingType, name, szOID_RSA_emailAddr, + name_string, name_len); break; } case CERT_NAME_RDN_TYPE: { - DWORD type = pvTypePara ? *(DWORD *)pvTypePara : 0; + DWORD param = type_para ? *(DWORD *)type_para : 0; if (name->cbData) - ret = CertNameToStrW(pCertContext->dwCertEncodingType, name, - type, pszNameString, cchNameString); + { + ret = CertNameToStrW(cert->dwCertEncodingType, name, param, name_string, name_len); + } else { - CERT_ALT_NAME_INFO *info; - PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, - altNameOID, CERT_ALT_NAME_DIRECTORY_NAME, &info); + entry = cert_find_alt_name_entry(cert, alt_name_issuer, CERT_ALT_NAME_DIRECTORY_NAME, &info); if (entry) - ret = CertNameToStrW(pCertContext->dwCertEncodingType, - &entry->u.DirectoryName, type, pszNameString, cchNameString); - if (info) - LocalFree(info); + ret = CertNameToStrW(cert->dwCertEncodingType, &entry->u.DirectoryName, + param, name_string, name_len); } break; } case CERT_NAME_ATTR_TYPE: - ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType, - name, pvTypePara, pszNameString, cchNameString); - if (!ret) - { - CERT_ALT_NAME_INFO *altInfo; - PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, - altNameOID, CERT_ALT_NAME_DIRECTORY_NAME, &altInfo); + ret = cert_get_name_from_rdn_attr(cert->dwCertEncodingType, name, type_para, + name_string, name_len); + if (ret) break; - if (entry) - ret = cert_name_to_str_with_indent(X509_ASN_ENCODING, 0, - &entry->u.DirectoryName, 0, pszNameString, cchNameString); - if (altInfo) - LocalFree(altInfo); - } + entry = cert_find_alt_name_entry(cert, alt_name_issuer, CERT_ALT_NAME_DIRECTORY_NAME, &info); + + if (entry) + ret = cert_name_to_str_with_indent(X509_ASN_ENCODING, 0, &entry->u.DirectoryName, + 0, name_string, name_len); break; case CERT_NAME_SIMPLE_DISPLAY_TYPE: { - static const LPCSTR simpleAttributeOIDs[] = { szOID_COMMON_NAME, - szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME, - szOID_RSA_emailAddr }; + static const LPCSTR simpleAttributeOIDs[] = + { + szOID_COMMON_NAME, szOID_ORGANIZATIONAL_UNIT_NAME, szOID_ORGANIZATION_NAME, szOID_RSA_emailAddr + }; CERT_NAME_INFO *nameInfo = NULL; DWORD bytes = 0, i; - if (CryptDecodeObjectEx(pCertContext->dwCertEncodingType, X509_NAME, - name->pbData, name->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo, - &bytes)) + if (CryptDecodeObjectEx(cert->dwCertEncodingType, X509_NAME, name->pbData, name->cbData, + CRYPT_DECODE_ALLOC_FLAG, NULL, &nameInfo, &bytes)) { PCERT_RDN_ATTR nameAttr = NULL; for (i = 0; !nameAttr && i < ARRAY_SIZE(simpleAttributeOIDs); i++) nameAttr = CertFindRDNAttr(simpleAttributeOIDs[i], nameInfo); if (nameAttr) - ret = CertRDNValueToStrW(nameAttr->dwValueType, - &nameAttr->Value, pszNameString, cchNameString); + ret = rdn_value_to_strW(nameAttr->dwValueType, &nameAttr->Value, name_string, name_len, TRUE); LocalFree(nameInfo); } - if (!ret) - { - CERT_ALT_NAME_INFO *altInfo; - PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, - altNameOID, CERT_ALT_NAME_RFC822_NAME, &altInfo); - - if (altInfo) - { - if (!entry && altInfo->cAltEntry) - entry = &altInfo->rgAltEntry[0]; - if (entry) - { - if (!pszNameString) - ret = lstrlenW(entry->u.pwszRfc822Name) + 1; - else if (cchNameString) - { - ret = min(lstrlenW(entry->u.pwszRfc822Name), - cchNameString - 1); - memcpy(pszNameString, entry->u.pwszRfc822Name, - ret * sizeof(WCHAR)); - pszNameString[ret++] = 0; - } - } - LocalFree(altInfo); - } - } + if (ret) break; + entry = cert_find_alt_name_entry(cert, alt_name_issuer, CERT_ALT_NAME_RFC822_NAME, &info); + if (!info) break; + if (!entry && info->cAltEntry) + entry = &info->rgAltEntry[0]; + if (entry) ret = copy_output_str(name_string, entry->u.pwszRfc822Name, name_len); break; } case CERT_NAME_FRIENDLY_DISPLAY_TYPE: { - DWORD cch = cchNameString; + DWORD len = name_len; - if (CertGetCertificateContextProperty(pCertContext, - CERT_FRIENDLY_NAME_PROP_ID, pszNameString, &cch)) - ret = cch; + if (CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, name_string, &len)) + ret = len; else - ret = CertGetNameStringW(pCertContext, - CERT_NAME_SIMPLE_DISPLAY_TYPE, dwFlags, pvTypePara, pszNameString, - cchNameString); + ret = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, flags, + type_para, name_string, name_len); break; } case CERT_NAME_DNS_TYPE: { - CERT_ALT_NAME_INFO *info; - PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, - altNameOID, CERT_ALT_NAME_DNS_NAME, &info); + unsigned int index = 0, len; - if (entry) + if (cert_get_alt_name_info(cert, alt_name_issuer, &info) + && (entry = cert_find_next_alt_name_entry(info, CERT_ALT_NAME_DNS_NAME, &index))) { - if (!pszNameString) - ret = lstrlenW(entry->u.pwszDNSName) + 1; - else if (cchNameString) + if (search_all_names) { - ret = min(lstrlenW(entry->u.pwszDNSName), cchNameString - 1); - memcpy(pszNameString, entry->u.pwszDNSName, ret * sizeof(WCHAR)); - pszNameString[ret++] = 0; + do + { + if (name_string && name_len == 1) break; + ret += len = copy_output_str(name_string, entry->u.pwszDNSName, name_len ? name_len - 1 : 0); + if (name_string && name_len) + { + name_string += len; + name_len -= len; + } + } + while ((entry = cert_find_next_alt_name_entry(info, CERT_ALT_NAME_DNS_NAME, &index))); } + else ret = copy_output_str(name_string, entry->u.pwszDNSName, name_len); } - if (info) - LocalFree(info); - if (!ret) - ret = cert_get_name_from_rdn_attr(pCertContext->dwCertEncodingType, - name, szOID_COMMON_NAME, pszNameString, cchNameString); - break; - } - case CERT_NAME_URL_TYPE: - { - CERT_ALT_NAME_INFO *info; - PCERT_ALT_NAME_ENTRY entry = cert_find_alt_name_entry(pCertContext, - altNameOID, CERT_ALT_NAME_URL, &info); - - if (entry) + else { - if (!pszNameString) - ret = lstrlenW(entry->u.pwszURL) + 1; - else if (cchNameString) + if (!search_all_names || name_len != 1) { - ret = min(lstrlenW(entry->u.pwszURL), cchNameString - 1); - memcpy(pszNameString, entry->u.pwszURL, ret * sizeof(WCHAR)); - pszNameString[ret++] = 0; + len = search_all_names && name_len ? name_len - 1 : name_len; + ret = cert_get_name_from_rdn_attr(cert->dwCertEncodingType, name, szOID_COMMON_NAME, + name_string, len); + if (name_string) name_string += ret; } } - if (info) - LocalFree(info); + + if (search_all_names) + { + if (name_string && name_len) *name_string = 0; + ++ret; + } + break; + } + case CERT_NAME_URL_TYPE: + { + if ((entry = cert_find_alt_name_entry(cert, alt_name_issuer, CERT_ALT_NAME_URL, &info))) + ret = copy_output_str(name_string, entry->u.pwszURL, name_len); break; } default: - FIXME("unimplemented for type %ld\n", dwType); + FIXME("unimplemented for type %lu.\n", type); ret = 0; + break; } done: + if (info) + LocalFree(info); + if (!ret) { - if (!pszNameString) - ret = 1; - else if (cchNameString) - { - pszNameString[0] = 0; - ret = 1; - } + ret = 1; + if (name_string && name_len) name_string[0] = 0; } return ret; } diff --git a/dlls/crypt32/tests/base64.c b/dlls/crypt32/tests/base64.c index a1517b294ad..e81a57c576d 100644 --- wine/dlls/crypt32/tests/base64.c +++ wine/dlls/crypt32/tests/base64.c @@ -23,7 +23,6 @@ #include #include -#include "wine/heap.h" #include "wine/test.h" #define CERT_HEADER "-----BEGIN CERTIFICATE-----\r\n" @@ -93,7 +92,7 @@ static WCHAR *strdupAtoW(const char *str) if (!str) return ret; len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); - ret = heap_alloc(len * sizeof(WCHAR)); + ret = malloc(len * sizeof(WCHAR)); if (ret) MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); return ret; @@ -128,7 +127,7 @@ static void encodeAndCompareBase64_A(const BYTE *toEncode, DWORD toEncodeLen, ok(ret, "CryptBinaryToStringA failed: %ld\n", GetLastError()); ok(strLen == strLen2, "Unexpected required length %lu, expected %lu.\n", strLen2, strLen); - str = heap_alloc(strLen); + str = malloc(strLen); /* Partially filled output buffer. */ strLen2 = strLen - 1; @@ -157,7 +156,7 @@ static void encodeAndCompareBase64_A(const BYTE *toEncode, DWORD toEncodeLen, if (trailer) ok(!strncmp(trailer, ptr, strlen(trailer)), "Expected trailer %s, got %s\n", trailer, ptr); - heap_free(str); + free(str); } static void encode_compare_base64_W(const BYTE *toEncode, DWORD toEncodeLen, DWORD format, @@ -196,7 +195,7 @@ static void encode_compare_base64_W(const BYTE *toEncode, DWORD toEncodeLen, DWO ok(ret, "CryptBinaryToStringW failed: %ld\n", GetLastError()); ok(strLen == strLen2, "Unexpected required length.\n"); - strW = heap_alloc(strLen * sizeof(WCHAR)); + strW = malloc(strLen * sizeof(WCHAR)); headerW = strdupAtoW(header); trailerW = strdupAtoW(trailer); @@ -231,9 +230,9 @@ static void encode_compare_base64_W(const BYTE *toEncode, DWORD toEncodeLen, DWO ok(!memcmp(trailerW, ptr, lstrlenW(trailerW)), "Expected trailer %s, got %s.\n", wine_dbgstr_w(trailerW), wine_dbgstr_w(ptr)); - heap_free(strW); - heap_free(headerW); - heap_free(trailerW); + free(strW); + free(headerW); + free(trailerW); } static DWORD binary_to_hex_len(DWORD binary_len, DWORD flags) @@ -267,6 +266,7 @@ static void test_CryptBinaryToString(void) BYTE input[256 * sizeof(WCHAR)]; DWORD strLen, strLen2, i, j, k; WCHAR *hex, *cmp, *ptr; + char *hex_a, *cmp_a; BOOL ret; ret = CryptBinaryToStringA(NULL, 0, 0, NULL, NULL); @@ -299,12 +299,12 @@ static void test_CryptBinaryToString(void) ok(strLen == tests[i].toEncodeLen, "Unexpected required length %lu.\n", strLen); strLen2 = strLen; - str = heap_alloc(strLen); + str = malloc(strLen); ret = CryptBinaryToStringA(tests[i].toEncode, tests[i].toEncodeLen, CRYPT_STRING_BINARY, str, &strLen2); ok(ret, "CryptBinaryToStringA failed: %ld\n", GetLastError()); ok(strLen == strLen2, "Expected length %lu, got %lu\n", strLen, strLen2); ok(!memcmp(str, tests[i].toEncode, tests[i].toEncodeLen), "Unexpected value\n"); - heap_free(str); + free(str); strLen = 0; ret = CryptBinaryToStringW(tests[i].toEncode, tests[i].toEncodeLen, CRYPT_STRING_BINARY, NULL, &strLen); @@ -312,12 +312,12 @@ static void test_CryptBinaryToString(void) ok(strLen == tests[i].toEncodeLen, "Unexpected required length %lu.\n", strLen); strLen2 = strLen; - strW = heap_alloc(strLen); + strW = malloc(strLen); ret = CryptBinaryToStringW(tests[i].toEncode, tests[i].toEncodeLen, CRYPT_STRING_BINARY, strW, &strLen2); ok(ret, "CryptBinaryToStringW failed: %ld\n", GetLastError()); ok(strLen == strLen2, "Expected length %lu, got %lu\n", strLen, strLen2); ok(!memcmp(strW, tests[i].toEncode, tests[i].toEncodeLen), "Unexpected value\n"); - heap_free(strW); + free(strW); encodeAndCompareBase64_A(tests[i].toEncode, tests[i].toEncodeLen, CRYPT_STRING_BASE64, tests[i].base64, NULL, NULL); @@ -338,7 +338,7 @@ static void test_CryptBinaryToString(void) encode_compare_base64_W(tests[i].toEncode, tests[i].toEncodeLen, CRYPT_STRING_BASE64X509CRLHEADER, encodedW, X509_HEADER, X509_TRAILER); - heap_free(encodedW); + free(encodedW); } for (i = 0; i < ARRAY_SIZE(testsNoCR); i++) @@ -352,13 +352,13 @@ static void test_CryptBinaryToString(void) ok(ret, "CryptBinaryToStringA failed: %ld\n", GetLastError()); strLen2 = strLen; - str = heap_alloc(strLen); + str = malloc(strLen); ret = CryptBinaryToStringA(testsNoCR[i].toEncode, testsNoCR[i].toEncodeLen, CRYPT_STRING_BINARY | CRYPT_STRING_NOCR, str, &strLen2); ok(ret, "CryptBinaryToStringA failed: %ld\n", GetLastError()); ok(strLen == strLen2, "Expected length %ld, got %ld\n", strLen, strLen2); ok(!memcmp(str, testsNoCR[i].toEncode, testsNoCR[i].toEncodeLen), "Unexpected value\n"); - heap_free(str); + free(str); encodeAndCompareBase64_A(testsNoCR[i].toEncode, testsNoCR[i].toEncodeLen, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCR, testsNoCR[i].base64, NULL, NULL); @@ -383,7 +383,7 @@ static void test_CryptBinaryToString(void) CRYPT_STRING_BASE64X509CRLHEADER | CRYPT_STRING_NOCR, encodedW, X509_HEADER_NOCR, X509_TRAILER_NOCR); - heap_free(encodedW); + free(encodedW); } /* Systems that don't support HEXRAW format convert to BASE64 instead - 3 bytes in -> 4 chars + crlf + 1 null out. */ @@ -402,11 +402,17 @@ static void test_CryptBinaryToString(void) for (i = 0; i < ARRAY_SIZE(flags); i++) { + winetest_push_context("i %lu", i); strLen = 0; ret = CryptBinaryToStringW(input, sizeof(input), CRYPT_STRING_HEXRAW|flags[i], NULL, &strLen); ok(ret, "CryptBinaryToStringW failed: %ld\n", GetLastError()); ok(strLen > 0, "Unexpected string length.\n"); + strLen = 0; + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW|flags[i], NULL, &strLen); + ok(ret, "failed, error %ld.\n", GetLastError()); + ok(strLen > 0, "Unexpected string length.\n"); + strLen = ~0; ret = CryptBinaryToStringW(input, sizeof(input), CRYPT_STRING_HEXRAW|flags[i], NULL, &strLen); @@ -420,9 +426,12 @@ static void test_CryptBinaryToString(void) strLen2 += sizeof(input) * 2 + 1; ok(strLen == strLen2, "Expected length %ld, got %ld\n", strLen2, strLen); - hex = heap_alloc(strLen * sizeof(WCHAR)); + hex = malloc(strLen * sizeof(WCHAR)); + hex_a = malloc(strLen); + memset(hex, 0xcc, strLen * sizeof(WCHAR)); - ptr = cmp = heap_alloc(strLen * sizeof(WCHAR)); + ptr = cmp = malloc(strLen * sizeof(WCHAR)); + cmp_a = malloc(strLen); for (j = 0; j < ARRAY_SIZE(input); j++) { *ptr++ = hexdig[(input[j] >> 4) & 0xf]; @@ -438,6 +447,11 @@ static void test_CryptBinaryToString(void) *ptr++ = '\n'; } *ptr++ = 0; + + for (j = 0; cmp[j]; ++j) + cmp_a[j] = cmp[j]; + cmp_a[j] = 0; + ret = CryptBinaryToStringW(input, sizeof(input), CRYPT_STRING_HEXRAW|flags[i], hex, &strLen); ok(ret, "CryptBinaryToStringW failed: %ld\n", GetLastError()); @@ -445,6 +459,13 @@ static void test_CryptBinaryToString(void) ok(strLen == strLen2, "Expected length %ld, got %ld\n", strLen, strLen2); ok(!memcmp(hex, cmp, strLen * sizeof(WCHAR)), "Unexpected value\n"); + ++strLen; + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW | flags[i], + hex_a, &strLen); + ok(ret, "failed, error %ld.\n", GetLastError()); + ok(strLen == strLen2, "Expected length %ld, got %ld.\n", strLen, strLen2); + ok(!memcmp(hex_a, cmp_a, strLen), "Unexpected value.\n"); + /* adjusts size if buffer too big */ strLen *= 2; ret = CryptBinaryToStringW(input, sizeof(input), CRYPT_STRING_HEXRAW|flags[i], @@ -452,6 +473,12 @@ static void test_CryptBinaryToString(void) ok(ret, "CryptBinaryToStringW failed: %ld\n", GetLastError()); ok(strLen == strLen2, "Expected length %ld, got %ld\n", strLen, strLen2); + strLen *= 2; + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW|flags[i], + hex_a, &strLen); + ok(ret, "failed, error %ld.\n", GetLastError()); + ok(strLen == strLen2, "Expected length %ld, got %ld.\n", strLen, strLen2); + /* no writes if buffer too small */ strLen /= 2; strLen2 /= 2; @@ -465,8 +492,49 @@ static void test_CryptBinaryToString(void) ok(strLen == strLen2, "Expected length %ld, got %ld\n", strLen, strLen2); ok(!memcmp(hex, cmp, strLen * sizeof(WCHAR)), "Unexpected value\n"); - heap_free(hex); - heap_free(cmp); + SetLastError(0xdeadbeef); + memset(hex_a, 0xcc, strLen + 3); + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW | flags[i], + hex_a, &strLen); + ok(!ret && GetLastError() == ERROR_MORE_DATA,"got ret %d, error %lu.\n", ret, GetLastError()); + ok(strLen == strLen2, "Expected length %ld, got %ld.\n", strLen2, strLen); + /* Output consists of the number of full bytes which fit in plus terminating 0. */ + strLen = (strLen - 1) & ~1; + ok(!memcmp(hex_a, cmp_a, strLen), "Unexpected value\n"); + ok(!hex_a[strLen], "got %#x.\n", (unsigned char)hex_a[strLen]); + ok((unsigned char)hex_a[strLen + 1] == 0xcc, "got %#x.\n", (unsigned char)hex_a[strLen + 1]); + + /* Output is not filled if string length is less than 3. */ + strLen = 1; + memset(hex_a, 0xcc, strLen2); + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW | flags[i], + hex_a, &strLen); + ok(strLen == 1, "got %ld.\n", strLen); + ok((unsigned char)hex_a[0] == 0xcc, "got %#x.\n", (unsigned char)hex_a[strLen - 1]); + + strLen = 2; + memset(hex_a, 0xcc, strLen2); + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW | flags[i], + hex_a, &strLen); + ok(strLen == 2, "got %ld.\n", strLen); + ok((unsigned char)hex_a[0] == 0xcc, "got %#x.\n", (unsigned char)hex_a[0]); + ok((unsigned char)hex_a[1] == 0xcc, "got %#x.\n", (unsigned char)hex_a[1]); + + strLen = 3; + memset(hex_a, 0xcc, strLen2); + ret = CryptBinaryToStringA(input, sizeof(input), CRYPT_STRING_HEXRAW | flags[i], + hex_a, &strLen); + ok(strLen == 3, "got %ld.\n", strLen); + ok(hex_a[0] == 0x30, "got %#x.\n", (unsigned char)hex_a[0]); + ok(hex_a[1] == 0x30, "got %#x.\n", (unsigned char)hex_a[1]); + ok(!hex_a[2], "got %#x.\n", (unsigned char)hex_a[2]); + + free(hex); + free(hex_a); + free(cmp); + free(cmp_a); + + winetest_pop_context(); } for (k = 0; k < ARRAY_SIZE(sizes); k++) @@ -483,10 +551,10 @@ static void test_CryptBinaryToString(void) strLen2 = binary_to_hex_len(sizes[k], CRYPT_STRING_HEX | flags[i]); ok(strLen == strLen2, "%lu: Expected length %ld, got %ld\n", i, strLen2, strLen); - hex = heap_alloc(strLen * sizeof(WCHAR) + 256); + hex = malloc(strLen * sizeof(WCHAR) + 256); memset(hex, 0xcc, strLen * sizeof(WCHAR)); - ptr = cmp = heap_alloc(strLen * sizeof(WCHAR) + 256); + ptr = cmp = malloc(strLen * sizeof(WCHAR) + 256); for (j = 0; j < sizes[k]; j++) { *ptr++ = hexdig[(input[j] >> 4) & 0xf]; @@ -552,8 +620,8 @@ static void test_CryptBinaryToString(void) ok(strLen == strLen2, "%lu: Expected length %ld, got %ld\n", i, strLen, strLen2); ok(!memcmp(hex, cmp, strLen * sizeof(WCHAR)), "%lu: got %s\n", i, wine_dbgstr_wn(hex, strLen)); - heap_free(hex); - heap_free(cmp); + free(hex); + free(cmp); } } @@ -569,7 +637,7 @@ static void decodeAndCompareBase64_A(LPCSTR toDecode, LPCSTR header, len += strlen(header); if (trailer) len += strlen(trailer); - str = HeapAlloc(GetProcessHeap(), 0, len); + str = malloc(len); if (str) { LPBYTE buf; @@ -586,7 +654,7 @@ static void decodeAndCompareBase64_A(LPCSTR toDecode, LPCSTR header, ret = CryptStringToBinaryA(str, 0, useFormat, NULL, &bufLen, NULL, NULL); ok(ret, "CryptStringToBinaryA failed: %ld\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, bufLen); + buf = malloc(bufLen); if (buf) { DWORD skipped, usedFormat; @@ -605,7 +673,7 @@ static void decodeAndCompareBase64_A(LPCSTR toDecode, LPCSTR header, ok(skipped == 0, "Expected skipped 0, got %ld\n", skipped); ok(usedFormat == expectedFormat, "Expected format %ld, got %ld\n", expectedFormat, usedFormat); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } /* Check again, but with garbage up front */ @@ -625,7 +693,7 @@ static void decodeAndCompareBase64_A(LPCSTR toDecode, LPCSTR header, "Expected !ret and last error ERROR_INVALID_DATA, got ret=%d, error=%ld\n", ret, GetLastError()); if (ret) { - buf = HeapAlloc(GetProcessHeap(), 0, bufLen); + buf = malloc(bufLen); if (buf) { DWORD skipped, usedFormat; @@ -636,10 +704,10 @@ static void decodeAndCompareBase64_A(LPCSTR toDecode, LPCSTR header, ok(skipped == strlen(garbage), "Expected %d characters of \"%s\" skipped when trying format %08lx, got %ld (used format is %08lx)\n", lstrlenA(garbage), str, useFormat, skipped, usedFormat); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } - HeapFree(GetProcessHeap(), 0, str); + free(str); } } @@ -707,11 +775,145 @@ static const struct BadString badStrings[] = { { "-----BEGIN X509 CRL-----\r\nAA==\r\n", CRYPT_STRING_BASE64X509CRLHEADER }, }; -static void testStringToBinaryA(void) +static BOOL is_hex_string_special_char(WCHAR c) { - BOOL ret; + switch (c) + { + case '-': + case ',': + case ' ': + case '\t': + case '\r': + case '\n': + return TRUE; + + default: + return FALSE; + } +} + +static WCHAR wchar_from_str(BOOL wide, const void **str, DWORD *len) +{ + WCHAR c; + + if (!*len) + return 0; + + --*len; + if (wide) + c = *(*(const WCHAR **)str)++; + else + c = *(*(const char **)str)++; + + return c ? c : 0xffff; +} + +static BYTE digit_from_char(WCHAR c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + c = towlower(c); + if (c >= 'a' && c <= 'f') + return c - 'a' + 0xa; + return 0xff; +} + +static LONG string_to_hex(const void* str, BOOL wide, DWORD len, BYTE *hex, DWORD *hex_len, + DWORD *skipped, DWORD *ret_flags) +{ + unsigned int byte_idx = 0; + BYTE d1, d2; + WCHAR c; + + if (!str || !hex_len) + return ERROR_INVALID_PARAMETER; + + if (!len) + len = wide ? wcslen(str) : strlen(str); + + if (wide && !len) + return ERROR_INVALID_PARAMETER; + + if (skipped) + *skipped = 0; + if (ret_flags) + *ret_flags = 0; + + while ((c = wchar_from_str(wide, &str, &len)) && is_hex_string_special_char(c)) + ; + + while ((d1 = digit_from_char(c)) != 0xff) + { + if ((d2 = digit_from_char(wchar_from_str(wide, &str, &len))) == 0xff) + { + if (!hex) + *hex_len = 0; + return ERROR_INVALID_DATA; + } + + if (hex && byte_idx < *hex_len) + hex[byte_idx] = (d1 << 4) | d2; + + ++byte_idx; + + do + { + c = wchar_from_str(wide, &str, &len); + } while (c == '-' || c == ','); + } + + while (c) + { + if (!is_hex_string_special_char(c)) + { + if (!hex) + *hex_len = 0; + return ERROR_INVALID_DATA; + } + c = wchar_from_str(wide, &str, &len); + } + + if (hex && byte_idx > *hex_len) + return ERROR_MORE_DATA; + + if (ret_flags) + *ret_flags = CRYPT_STRING_HEX; + + *hex_len = byte_idx; + + return ERROR_SUCCESS; +} + +static void test_CryptStringToBinary(void) +{ + static const char *string_hex_tests[] = + { + "", + "-", + ",-", + "0", + "00", + "000", + "11220", + "1122q", + "q1122", + " aE\t\n\r\n", + "01-02", + "-,01-02", + "01-02-", + "aa,BB-ff,-,", + "1-2", + "010-02", + "aa,BBff,-,", + "aa,,-BB---ff,-,", + "010203040506070809q", + }; + + DWORD skipped, flags, expected_err, expected_len, expected_skipped, expected_flags; + BYTE buf[8], expected[8]; DWORD bufLen = 0, i; - BYTE buf[8]; + WCHAR str_w[64]; + BOOL ret, wide; ret = CryptStringToBinaryA(NULL, 0, 0, NULL, NULL, NULL, NULL); ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, @@ -891,10 +1093,254 @@ static void testStringToBinaryA(void) CRYPT_STRING_ANY, CRYPT_STRING_BASE64HEADER, testsNoCR[i].toEncode, testsNoCR[i].toEncodeLen); } + + /* CRYPT_STRING_HEX */ + + ret = CryptStringToBinaryW(L"01", 2, CRYPT_STRING_HEX, NULL, NULL, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got ret %d, error %lu.\n", ret, GetLastError()); + if (0) + { + /* access violation on Windows. */ + CryptStringToBinaryA("01", 2, CRYPT_STRING_HEX, NULL, NULL, NULL, NULL); + } + + bufLen = 8; + ret = CryptStringToBinaryW(L"0102", 2, CRYPT_STRING_HEX, NULL, &bufLen, NULL, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + ok(bufLen == 1, "got length %lu.\n", bufLen); + + bufLen = 8; + ret = CryptStringToBinaryW(NULL, 0, CRYPT_STRING_HEX, NULL, &bufLen, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 8, "got length %lu.\n", bufLen); + + bufLen = 8; + ret = CryptStringToBinaryA(NULL, 0, CRYPT_STRING_HEX, NULL, &bufLen, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_PARAMETER, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 8, "got length %lu.\n", bufLen); + + bufLen = 8; + ret = CryptStringToBinaryW(L"0102", 3, CRYPT_STRING_HEX, NULL, &bufLen, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(!bufLen, "got length %lu.\n", bufLen); + + bufLen = 8; + buf[0] = 0xcc; + ret = CryptStringToBinaryW(L"0102", 3, CRYPT_STRING_HEX, buf, &bufLen, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 8, "got length %lu.\n", bufLen); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + + bufLen = 8; + buf[0] = 0xcc; + ret = CryptStringToBinaryW(L"0102", 2, CRYPT_STRING_HEX, buf, &bufLen, NULL, NULL); + ok(ret, "got error %lu.\n", GetLastError()); + ok(bufLen == 1, "got length %lu.\n", bufLen); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + + bufLen = 8; + buf[0] = buf[1] = 0xcc; + ret = CryptStringToBinaryA("01\0 02", 4, CRYPT_STRING_HEX, buf, &bufLen, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 8, "got length %lu.\n", bufLen); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + + bufLen = 8; + buf[0] = buf[1] = 0xcc; + ret = CryptStringToBinaryW(L"01\0 02", 4, CRYPT_STRING_HEX, buf, &bufLen, NULL, NULL); + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 8, "got length %lu.\n", bufLen); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + + bufLen = 1; + buf[0] = 0xcc; + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + ret = CryptStringToBinaryW(L"0102", 4, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + ok(!ret && GetLastError() == ERROR_MORE_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 1, "got length %lu.\n", bufLen); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(!flags, "got flags %lu.\n", flags); + ok(!skipped, "got skipped %lu.\n", skipped); + + for (i = 0; i < ARRAY_SIZE(string_hex_tests); ++i) + { + for (wide = 0; wide < 2; ++wide) + { + if (wide) + { + unsigned int j = 0; + + while ((str_w[j] = string_hex_tests[i][j])) + ++j; + } + winetest_push_context("test %lu, %s", i, wide ? debugstr_w(str_w) + : debugstr_a(string_hex_tests[i])); + + expected_len = 0xdeadbeef; + expected_skipped = 0xdeadbeef; + expected_flags = 0xdeadbeef; + expected_err = string_to_hex(wide ? (void *)str_w : (void *)string_hex_tests[i], wide, 0, NULL, + &expected_len, &expected_skipped, &expected_flags); + + bufLen = 0xdeadbeef; + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + SetLastError(0xdeadbeef); + if (wide) + ret = CryptStringToBinaryW(str_w, 0, CRYPT_STRING_HEX, NULL, &bufLen, &skipped, &flags); + else + ret = CryptStringToBinaryA(string_hex_tests[i], 0, CRYPT_STRING_HEX, NULL, &bufLen, &skipped, &flags); + + ok(bufLen == expected_len, "got length %lu.\n", bufLen); + ok(skipped == expected_skipped, "got skipped %lu.\n", skipped); + ok(flags == expected_flags, "got flags %lu.\n", flags); + + if (expected_err) + ok(!ret && GetLastError() == expected_err, "got ret %d, error %lu.\n", ret, GetLastError()); + else + ok(ret, "got error %lu.\n", GetLastError()); + + memset(expected, 0xcc, sizeof(expected)); + expected_len = 8; + expected_skipped = 0xdeadbeef; + expected_flags = 0xdeadbeef; + expected_err = string_to_hex(wide ? (void *)str_w : (void *)string_hex_tests[i], wide, 0, expected, + &expected_len, &expected_skipped, &expected_flags); + + memset(buf, 0xcc, sizeof(buf)); + bufLen = 8; + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + SetLastError(0xdeadbeef); + if (wide) + ret = CryptStringToBinaryW(str_w, 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + else + ret = CryptStringToBinaryA(string_hex_tests[i], 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + + ok(!memcmp(buf, expected, sizeof(buf)), "data does not match, buf[0] %#x, buf[1] %#x.\n", buf[0], buf[1]); + ok(bufLen == expected_len, "got length %lu.\n", bufLen); + if (expected_err) + ok(!ret && GetLastError() == expected_err, "got ret %d, error %lu.\n", ret, GetLastError()); + else + ok(ret, "got error %lu.\n", GetLastError()); + + ok(bufLen == expected_len, "got length %lu.\n", bufLen); + ok(skipped == expected_skipped, "got skipped %lu.\n", skipped); + ok(flags == expected_flags, "got flags %lu.\n", flags); + + winetest_pop_context(); + } + } + + bufLen = 1; + SetLastError(0xdeadbeef); + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + ret = CryptStringToBinaryA("0102", 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + ok(!ret && GetLastError() == ERROR_MORE_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 1, "got length %lu.\n", bufLen); + ok(!skipped, "got skipped %lu.\n", skipped); + ok(!flags, "got flags %lu.\n", flags); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + + bufLen = 1; + SetLastError(0xdeadbeef); + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + ret = CryptStringToBinaryA("0102q", 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 1, "got length %lu.\n", bufLen); + ok(!skipped, "got skipped %lu.\n", skipped); + ok(!flags, "got flags %lu.\n", flags); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + + bufLen = 1; + SetLastError(0xdeadbeef); + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + ret = CryptStringToBinaryW(L"0102q", 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(bufLen == 1, "got length %lu.\n", bufLen); + ok(!skipped, "got skipped %lu.\n", skipped); + ok(!flags, "got flags %lu.\n", flags); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + + bufLen = 1; + SetLastError(0xdeadbeef); + skipped = 0xdeadbeef; + flags = 0xdeadbeef; + memset(buf, 0xcc, sizeof(buf)); + ret = CryptStringToBinaryW(L"0102", 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + ok(bufLen == 1, "got length %lu.\n", bufLen); + ok(!ret && GetLastError() == ERROR_MORE_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + ok(buf[0] == 1, "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + + /* It looks like Windows is normalizing Unicode strings in some way which depending on locale may result in + * some invalid characters in 128-255 range being converted into sequences starting with valid hex numbers. + * Just avoiding characters in the 128-255 range in test. */ + for (i = 1; i < 128; ++i) + { + char str_a[16]; + + for (wide = 0; wide < 2; ++wide) + { + if (wide) + { + str_w[0] = i; + wcscpy(str_w + 1, L"00"); + } + else + { + str_a[0] = i; + strcpy(str_a + 1, "00"); + } + + winetest_push_context("char %#lx, %s", i, wide ? debugstr_w(str_w) : debugstr_a(str_a)); + + bufLen = 1; + buf[0] = buf[1] = 0xcc; + SetLastError(0xdeadbeef); + if (wide) + ret = CryptStringToBinaryW(str_w, 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + else + ret = CryptStringToBinaryA(str_a, 0, CRYPT_STRING_HEX, buf, &bufLen, &skipped, &flags); + ok(bufLen == 1, "got length %lu.\n", bufLen); + if (is_hex_string_special_char(i)) + { + ok(ret, "got error %lu.\n", GetLastError()); + ok(!buf[0], "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[1] %#x.\n", buf[1]); + } + else + { + ok(!ret && GetLastError() == ERROR_INVALID_DATA, "got ret %d, error %lu.\n", ret, GetLastError()); + if (isdigit(i) || (tolower(i) >= 'a' && tolower(i) <= 'f')) + { + ok(buf[0] == (digit_from_char(i) << 4), "got buf[0] %#x.\n", buf[0]); + ok(buf[1] == 0xcc, "got buf[0] %#x.\n", buf[1]); + } + else + { + ok(buf[0] == 0xcc, "got buf[0] %#x.\n", buf[0]); + } + } + winetest_pop_context(); + } + } } START_TEST(base64) { test_CryptBinaryToString(); - testStringToBinaryA(); + test_CryptStringToBinary(); } diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c index 3cdb5e5ceea..882bb4a0723 100644 --- wine/dlls/crypt32/tests/cert.c +++ wine/dlls/crypt32/tests/cert.c @@ -554,7 +554,7 @@ static void testCertProperties(void) ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); if (ret) { - LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, size); + LPBYTE buf = malloc(size); if (buf) { @@ -563,7 +563,7 @@ static void testCertProperties(void) ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); ok(!memcmp(buf, subjectKeyId, size), "Unexpected subject key id\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } CertFreeCertificateContext(context); @@ -1640,7 +1640,7 @@ static void testGetIssuerCert(void) size = 0; ok(CertStrToNameW(X509_ASN_ENCODING, L"CN=dummy, T=Test", CERT_X500_NAME_STR, NULL, NULL, &size, NULL), "CertStrToName should have worked\n"); - certencoded = HeapAlloc(GetProcessHeap(), 0, size); + certencoded = malloc(size); ok(CertStrToNameW(X509_ASN_ENCODING, L"CN=dummy, T=Test", CERT_X500_NAME_STR, NULL, certencoded, &size, NULL), "CertStrToName should have worked\n"); certsubject.pbData = certencoded; @@ -1663,7 +1663,7 @@ static void testGetIssuerCert(void) CertFreeCertificateContext(cert2); CertFreeCertificateContext(cert3); CertCloseStore(store, 0); - HeapFree(GetProcessHeap(), 0, certencoded); + free(certencoded); /* Test root storage self-signed certificate */ store = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"ROOT"); @@ -1913,7 +1913,7 @@ static void testVerifyCertSig(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigned, } CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, X509_ASN_ENCODING, (LPSTR)sigOID, 0, NULL, NULL, &pubKeySize); - pubKeyInfo = HeapAlloc(GetProcessHeap(), 0, pubKeySize); + pubKeyInfo = malloc(pubKeySize); if (pubKeyInfo) { ret = CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, @@ -1927,7 +1927,7 @@ static void testVerifyCertSig(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigned, ok(ret, "CryptVerifyCertificateSignature failed: %08lx\n", GetLastError()); } - HeapFree(GetProcessHeap(), 0, pubKeyInfo); + free(pubKeyInfo); } LocalFree(cert); } @@ -2000,7 +2000,7 @@ static void testVerifyCertSigEx(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigne */ CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, X509_ASN_ENCODING, (LPSTR)sigOID, 0, NULL, NULL, &size); - pubKeyInfo = HeapAlloc(GetProcessHeap(), 0, size); + pubKeyInfo = malloc(size); if (pubKeyInfo) { ret = CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, @@ -2014,7 +2014,7 @@ static void testVerifyCertSigEx(HCRYPTPROV csp, const CRYPT_DATA_BLOB *toBeSigne ok(ret, "CryptVerifyCertificateSignatureEx failed: %08lx\n", GetLastError()); } - HeapFree(GetProcessHeap(), 0, pubKeyInfo); + free(pubKeyInfo); } LocalFree(cert); } @@ -2114,7 +2114,7 @@ static void testSignAndEncodeCert(void) /* oid_rsa_md5 not present in some win2k */ if (ret) { - LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, size); + LPBYTE buf = malloc(size); if (buf) { @@ -2135,7 +2135,7 @@ static void testSignAndEncodeCert(void) else if (size == sizeof(md5SignedEmptyCertNoNull)) ok(!memcmp(buf, md5SignedEmptyCertNoNull, size), "Unexpected value\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } } @@ -2188,7 +2188,7 @@ static void testCreateSelfSignCert(void) ok(ret && size, "Expected non-zero key provider info\n"); if (size) { - PCRYPT_KEY_PROV_INFO pInfo = HeapAlloc(GetProcessHeap(), 0, size); + PCRYPT_KEY_PROV_INFO pInfo = malloc(size); if (pInfo) { @@ -2206,7 +2206,7 @@ static void testCreateSelfSignCert(void) ok(pInfo->dwKeySpec == AT_SIGNATURE, "Expected AT_SIGNATURE, got %ld\n", pInfo->dwKeySpec); } - HeapFree(GetProcessHeap(), 0, pInfo); + free(pInfo); } } @@ -2262,7 +2262,7 @@ static void testCreateSelfSignCert(void) ok(ret && size, "Expected non-zero key provider info\n"); if (size) { - PCRYPT_KEY_PROV_INFO pInfo = HeapAlloc(GetProcessHeap(), 0, size); + PCRYPT_KEY_PROV_INFO pInfo = malloc(size); if (pInfo) { @@ -2280,7 +2280,7 @@ static void testCreateSelfSignCert(void) ok(pInfo->dwKeySpec == AT_SIGNATURE, "Expected AT_SIGNATURE, got %ld\n", pInfo->dwKeySpec); } - HeapFree(GetProcessHeap(), 0, pInfo); + free(pInfo); } } @@ -2309,7 +2309,7 @@ static void testCreateSelfSignCert(void) ok(ret && size, "Expected non-zero key provider info\n"); if (size) { - PCRYPT_KEY_PROV_INFO pInfo = HeapAlloc(GetProcessHeap(), 0, size); + PCRYPT_KEY_PROV_INFO pInfo = malloc(size); if (pInfo) { @@ -2327,7 +2327,7 @@ static void testCreateSelfSignCert(void) ok(pInfo->dwKeySpec == AT_KEYEXCHANGE, "Expected AT_KEYEXCHANGE, got %ld\n", pInfo->dwKeySpec); } - HeapFree(GetProcessHeap(), 0, pInfo); + free(pInfo); } } @@ -2386,7 +2386,7 @@ static void testCreateSelfSignCert(void) ok(ret && size, "Expected non-zero key provider info\n"); if (size) { - PCRYPT_KEY_PROV_INFO pInfo = HeapAlloc(GetProcessHeap(), 0, size); + PCRYPT_KEY_PROV_INFO pInfo = malloc(size); if (pInfo) { @@ -2404,7 +2404,7 @@ static void testCreateSelfSignCert(void) ok(pInfo->dwKeySpec == AT_KEYEXCHANGE, "Expected AT_KEYEXCHANGE, got %ld\n", pInfo->dwKeySpec); } - HeapFree(GetProcessHeap(), 0, pInfo); + free(pInfo); } } @@ -2627,7 +2627,7 @@ static void testKeyUsage(void) ret = CertGetEnhancedKeyUsage(context, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &bufSize); ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + buf = malloc(bufSize); if (buf) { PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; @@ -2644,11 +2644,11 @@ static void testKeyUsage(void) ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), "Expected %s, got %s\n", keyUsages[i], pUsage->rgpszUsageIdentifier[i]); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } ret = CertGetEnhancedKeyUsage(context, 0, NULL, &bufSize); ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + buf = malloc(bufSize); if (buf) { PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; @@ -2668,7 +2668,7 @@ static void testKeyUsage(void) ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), "Expected %s, got %s\n", keyUsages[i], pUsage->rgpszUsageIdentifier[i]); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } /* Shouldn't find it as an extended property */ ret = CertGetEnhancedKeyUsage(context, @@ -2681,7 +2681,7 @@ static void testKeyUsage(void) GetLastError()); ret = CertGetEnhancedKeyUsage(context, 0, NULL, &bufSize); ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + buf = malloc(bufSize); if (buf) { PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; @@ -2696,13 +2696,13 @@ static void testKeyUsage(void) ok(!strcmp(pUsage->rgpszUsageIdentifier[0], szOID_RSA_RSA), "Expected %s, got %s\n", szOID_RSA_RSA, pUsage->rgpszUsageIdentifier[0]); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } /* But querying the cert directly returns its usage */ ret = CertGetEnhancedKeyUsage(context, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, &bufSize); ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + buf = malloc(bufSize); if (buf) { PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; @@ -2718,7 +2718,7 @@ static void testKeyUsage(void) ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), "Expected %s, got %s\n", keyUsages[i], pUsage->rgpszUsageIdentifier[i]); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } /* And removing the only usage identifier in the extended property * results in the cert's key usage being found. @@ -2727,7 +2727,7 @@ static void testKeyUsage(void) ok(ret, "CertRemoveEnhancedKeyUsage failed: %08lx\n", GetLastError()); ret = CertGetEnhancedKeyUsage(context, 0, NULL, &bufSize); ok(ret, "CertGetEnhancedKeyUsage failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, bufSize); + buf = malloc(bufSize); if (buf) { PCERT_ENHKEY_USAGE pUsage = (PCERT_ENHKEY_USAGE)buf; @@ -2743,7 +2743,7 @@ static void testKeyUsage(void) ok(!strcmp(pUsage->rgpszUsageIdentifier[i], keyUsages[i]), "Expected %s, got %s\n", keyUsages[i], pUsage->rgpszUsageIdentifier[i]); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } CertFreeCertificateContext(context); @@ -2810,7 +2810,7 @@ static void testGetValidUsages(void) ok(ret, "CertGetValidUsages failed: %08lx\n", GetLastError()); ok(numOIDs == 3, "Expected 3, got %d\n", numOIDs); ok(size, "Expected non-zero size\n"); - oids = HeapAlloc(GetProcessHeap(), 0, size); + oids = malloc(size); if (oids) { int i; @@ -2825,7 +2825,7 @@ static void testGetValidUsages(void) for (i = 0; i < numOIDs; i++) ok(!lstrcmpA(oids[i], expectedOIDs[i]), "unexpected OID %s\n", oids[i]); - HeapFree(GetProcessHeap(), 0, oids); + free(oids); } numOIDs = 0xdeadbeef; /* Oddly enough, this crashes when the number of contexts is not 1: @@ -2837,7 +2837,7 @@ static void testGetValidUsages(void) ok(ret, "CertGetValidUsages failed: %08lx\n", GetLastError()); ok(numOIDs == 3, "Expected 3, got %d\n", numOIDs); ok(size, "Expected non-zero size\n"); - oids = HeapAlloc(GetProcessHeap(), 0, size); + oids = malloc(size); if (oids) { int i; @@ -2847,7 +2847,7 @@ static void testGetValidUsages(void) for (i = 0; i < numOIDs; i++) ok(!lstrcmpA(oids[i], expectedOIDs[i]), "unexpected OID %s\n", oids[i]); - HeapFree(GetProcessHeap(), 0, oids); + free(oids); } numOIDs = 0xdeadbeef; size = 0; @@ -2855,7 +2855,7 @@ static void testGetValidUsages(void) ok(ret, "CertGetValidUsages failed: %08lx\n", GetLastError()); ok(numOIDs == 2, "Expected 2, got %d\n", numOIDs); ok(size, "Expected non-zero size\n"); - oids = HeapAlloc(GetProcessHeap(), 0, size); + oids = malloc(size); if (oids) { int i; @@ -2865,7 +2865,7 @@ static void testGetValidUsages(void) for (i = 0; i < numOIDs; i++) ok(!lstrcmpA(oids[i], expectedOIDs2[i]), "unexpected OID %s\n", oids[i]); - HeapFree(GetProcessHeap(), 0, oids); + free(oids); } numOIDs = 0xdeadbeef; size = 0; @@ -2873,7 +2873,7 @@ static void testGetValidUsages(void) ok(ret, "CertGetValidUsages failed: %08lx\n", GetLastError()); ok(numOIDs == 2, "Expected 2, got %d\n", numOIDs); ok(size, "Expected non-zero size\n"); - oids = HeapAlloc(GetProcessHeap(), 0, size); + oids = malloc(size); if (oids) { int i; @@ -2883,7 +2883,7 @@ static void testGetValidUsages(void) for (i = 0; i < numOIDs; i++) ok(!lstrcmpA(oids[i], expectedOIDs2[i]), "unexpected OID %s\n", oids[i]); - HeapFree(GetProcessHeap(), 0, oids); + free(oids); } CertFreeCertificateContext(contexts[0]); CertFreeCertificateContext(contexts[1]); @@ -3537,6 +3537,520 @@ static const BYTE rootSignedCRL[] = { 0xd5,0xbc,0xb0,0xd5,0xa5,0x9c,0x1b,0x72,0xc3,0x0f,0xa3,0xe3,0x3c,0xf0,0xc3, 0x91,0xe8,0x93,0x4f,0xd4,0x2f }; +static const BYTE ocsp_cert[] = { + 0x30, 0x82, 0x06, 0xcd, 0x30, 0x82, 0x05, 0xb5, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x08, 0x49, 0x8f, 0x6d, 0xd9, 0xef, 0xfb, 0x40, 0x55, + 0x1e, 0xac, 0x54, 0x54, 0x87, 0xc1, 0xb1, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x4f, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x54, 0x4c, 0x53, 0x20, + 0x52, 0x53, 0x41, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x32, + 0x30, 0x32, 0x30, 0x20, 0x43, 0x41, 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x32, + 0x31, 0x30, 0x34, 0x32, 0x38, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, + 0x17, 0x0d, 0x32, 0x32, 0x30, 0x35, 0x32, 0x39, 0x32, 0x33, 0x35, 0x39, + 0x35, 0x39, 0x5a, 0x30, 0x6b, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x13, 0x0a, 0x57, 0x61, 0x73, 0x68, 0x69, 0x6e, 0x67, + 0x74, 0x6f, 0x6e, 0x31, 0x11, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x04, 0x07, + 0x13, 0x08, 0x42, 0x65, 0x6c, 0x6c, 0x65, 0x76, 0x75, 0x65, 0x31, 0x14, + 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0b, 0x56, 0x61, 0x6c, + 0x76, 0x65, 0x20, 0x43, 0x6f, 0x72, 0x70, 0x2e, 0x31, 0x1e, 0x30, 0x1c, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x15, 0x2a, 0x2e, 0x63, 0x6d, 0x2e, + 0x73, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x65, 0x64, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xcd, 0x98, 0x0c, 0x13, 0xb2, 0xb9, 0xf2, 0xa5, 0xc6, 0x52, 0xad, + 0xf9, 0x4d, 0xcf, 0x1e, 0x2b, 0x74, 0x05, 0xd1, 0x2e, 0x95, 0xc3, 0x9d, + 0x9b, 0x03, 0x2a, 0xc6, 0x65, 0x1e, 0xda, 0x5d, 0x57, 0x3c, 0x61, 0xa1, + 0x3d, 0xa3, 0xe7, 0x0c, 0xdd, 0xb1, 0x6b, 0x30, 0x97, 0x99, 0xc7, 0x77, + 0xab, 0xc2, 0xb0, 0x0a, 0x5b, 0x1c, 0x86, 0x55, 0x42, 0x25, 0xa8, 0x4e, + 0xe2, 0x61, 0xfe, 0x88, 0x10, 0x25, 0xf5, 0x8e, 0x9c, 0x0f, 0xc7, 0xa5, + 0xef, 0x9d, 0xd5, 0xf0, 0x2a, 0xf2, 0x31, 0x59, 0x59, 0xfc, 0xe0, 0x1f, + 0x8f, 0xb4, 0xa1, 0x06, 0x32, 0x04, 0x37, 0x3d, 0x9d, 0xad, 0xc1, 0xe0, + 0x00, 0x3d, 0x8d, 0x60, 0x4c, 0x9e, 0x6a, 0x1f, 0xd2, 0xf6, 0xf8, 0x86, + 0x0b, 0x11, 0x7b, 0xfd, 0x75, 0xae, 0x20, 0x9b, 0xca, 0x52, 0x5f, 0x4e, + 0xad, 0x2e, 0xa2, 0xce, 0xed, 0x35, 0x08, 0x23, 0xa8, 0x6e, 0x61, 0x7e, + 0x18, 0x1a, 0x6a, 0xd9, 0xe0, 0x3b, 0x52, 0x64, 0xe9, 0x2c, 0x81, 0x8f, + 0xbc, 0x4b, 0x48, 0xd1, 0x7a, 0x3e, 0x02, 0x9c, 0xad, 0x87, 0x73, 0xae, + 0xaa, 0xea, 0x32, 0xfb, 0x07, 0x4e, 0xcb, 0xe9, 0xac, 0xac, 0x50, 0x0f, + 0x49, 0xb7, 0x23, 0x3b, 0x1f, 0xb2, 0x24, 0x46, 0x78, 0x32, 0x11, 0x9e, + 0xa2, 0xeb, 0xd8, 0x8b, 0x7e, 0x56, 0x92, 0xaa, 0x29, 0xbd, 0x55, 0xc8, + 0x3e, 0x69, 0xe2, 0x56, 0xf4, 0x24, 0x58, 0x7b, 0xf8, 0xb0, 0xbb, 0x72, + 0xb7, 0x38, 0x34, 0xe3, 0x0f, 0x30, 0xf4, 0xfd, 0x44, 0xf1, 0x53, 0x0f, + 0xc5, 0x31, 0xd6, 0xad, 0x45, 0xbf, 0x57, 0x2c, 0x4c, 0xe5, 0x1a, 0xc0, + 0x08, 0x25, 0x88, 0x2f, 0xca, 0x07, 0x2e, 0x35, 0x31, 0xa7, 0x40, 0x3a, + 0x71, 0x1d, 0xba, 0x09, 0xf8, 0x76, 0x6c, 0x69, 0xb2, 0x89, 0xd7, 0xbe, + 0xca, 0x9d, 0xf5, 0xd4, 0x7d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x03, 0x87, 0x30, 0x82, 0x03, 0x83, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, + 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xb7, 0x6b, 0xa2, 0xea, 0xa8, + 0xaa, 0x84, 0x8c, 0x79, 0xea, 0xb4, 0xda, 0x0f, 0x98, 0xb2, 0xc5, 0x95, + 0x76, 0xb9, 0xf4, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, + 0x04, 0x14, 0x22, 0x68, 0x57, 0xb9, 0xc0, 0x1f, 0xce, 0xa6, 0xbf, 0xb6, + 0x55, 0xcb, 0x2a, 0x1b, 0xe6, 0xe0, 0x76, 0x94, 0x07, 0x06, 0x30, 0x35, + 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x2e, 0x30, 0x2c, 0x82, 0x15, 0x2a, + 0x2e, 0x63, 0x6d, 0x2e, 0x73, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6f, 0x77, + 0x65, 0x72, 0x65, 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x82, 0x13, 0x63, 0x6d, + 0x2e, 0x73, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6f, 0x77, 0x65, 0x72, 0x65, + 0x64, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f, + 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, + 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, + 0x05, 0x07, 0x03, 0x02, 0x30, 0x81, 0x8b, 0x06, 0x03, 0x55, 0x1d, 0x1f, + 0x04, 0x81, 0x83, 0x30, 0x81, 0x80, 0x30, 0x3e, 0xa0, 0x3c, 0xa0, 0x3a, + 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x54, + 0x4c, 0x53, 0x52, 0x53, 0x41, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x32, + 0x30, 0x32, 0x30, 0x43, 0x41, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3e, + 0xa0, 0x3c, 0xa0, 0x3a, 0x86, 0x38, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, + 0x65, 0x72, 0x74, 0x54, 0x4c, 0x53, 0x52, 0x53, 0x41, 0x53, 0x48, 0x41, + 0x32, 0x35, 0x36, 0x32, 0x30, 0x32, 0x30, 0x43, 0x41, 0x31, 0x2e, 0x63, + 0x72, 0x6c, 0x30, 0x3e, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x37, 0x30, + 0x35, 0x30, 0x33, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, + 0x29, 0x30, 0x27, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x01, 0x16, 0x1b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, 0x30, 0x7d, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x71, 0x30, 0x6f, 0x30, 0x24, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, + 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, + 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, + 0x30, 0x47, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, + 0x86, 0x3b, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x61, 0x63, + 0x65, 0x72, 0x74, 0x73, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, + 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, + 0x72, 0x74, 0x54, 0x4c, 0x53, 0x52, 0x53, 0x41, 0x53, 0x48, 0x41, 0x32, + 0x35, 0x36, 0x32, 0x30, 0x32, 0x30, 0x43, 0x41, 0x31, 0x2e, 0x63, 0x72, + 0x74, 0x30, 0x0c, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, + 0x02, 0x30, 0x00, 0x30, 0x82, 0x01, 0x7e, 0x06, 0x0a, 0x2b, 0x06, 0x01, + 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02, 0x04, 0x82, 0x01, 0x6e, 0x04, + 0x82, 0x01, 0x6a, 0x01, 0x68, 0x00, 0x76, 0x00, 0x29, 0x79, 0xbe, 0xf0, + 0x9e, 0x39, 0x39, 0x21, 0xf0, 0x56, 0x73, 0x9f, 0x63, 0xa5, 0x77, 0xe5, + 0xbe, 0x57, 0x7d, 0x9c, 0x60, 0x0a, 0xf8, 0xf9, 0x4d, 0x5d, 0x26, 0x5c, + 0x25, 0x5d, 0xc7, 0x84, 0x00, 0x00, 0x01, 0x79, 0x19, 0x43, 0x10, 0x65, + 0x00, 0x00, 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x21, 0x00, 0x93, + 0x5f, 0x66, 0xe4, 0xfe, 0x76, 0x25, 0xe6, 0x07, 0x74, 0xa1, 0x8b, 0x7f, + 0x37, 0xad, 0xb1, 0x40, 0x8f, 0x20, 0x66, 0x71, 0x57, 0x14, 0x2c, 0x2e, + 0x28, 0xe0, 0xb2, 0x95, 0xd5, 0x19, 0xd0, 0x02, 0x20, 0x32, 0xd1, 0xa8, + 0x59, 0xfd, 0x69, 0x24, 0x8a, 0x27, 0x7e, 0x56, 0x06, 0xce, 0x6d, 0xeb, + 0xa1, 0xc6, 0x2a, 0xce, 0x4b, 0x37, 0xb1, 0x25, 0xca, 0x6d, 0xd3, 0x43, + 0xf3, 0xdb, 0xb8, 0xa5, 0x5e, 0x00, 0x76, 0x00, 0x22, 0x45, 0x45, 0x07, + 0x59, 0x55, 0x24, 0x56, 0x96, 0x3f, 0xa1, 0x2f, 0xf1, 0xf7, 0x6d, 0x86, + 0xe0, 0x23, 0x26, 0x63, 0xad, 0xc0, 0x4b, 0x7f, 0x5d, 0xc6, 0x83, 0x5c, + 0x6e, 0xe2, 0x0f, 0x02, 0x00, 0x00, 0x01, 0x79, 0x19, 0x43, 0x10, 0xa4, + 0x00, 0x00, 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x3a, 0x73, + 0x53, 0xfb, 0xbb, 0x42, 0xdf, 0x2e, 0xa2, 0xc0, 0xc5, 0x29, 0x57, 0xda, + 0xb9, 0x0b, 0x76, 0x58, 0xb6, 0xeb, 0xd3, 0x4d, 0x10, 0x95, 0x1b, 0x58, + 0x3e, 0x58, 0x86, 0xea, 0xec, 0xe5, 0x02, 0x21, 0x00, 0xeb, 0xb2, 0xfe, + 0x83, 0x74, 0xdf, 0xb5, 0xfd, 0x8f, 0x74, 0x82, 0xd3, 0x8f, 0x6b, 0xce, + 0x63, 0x8b, 0x93, 0x94, 0x08, 0x7b, 0x1c, 0x6b, 0x48, 0xae, 0x59, 0xa1, + 0x7e, 0xec, 0x59, 0xdf, 0x56, 0x00, 0x76, 0x00, 0x51, 0xa3, 0xb0, 0xf5, + 0xfd, 0x01, 0x79, 0x9c, 0x56, 0x6d, 0xb8, 0x37, 0x78, 0x8f, 0x0c, 0xa4, + 0x7a, 0xcc, 0x1b, 0x27, 0xcb, 0xf7, 0x9e, 0x88, 0x42, 0x9a, 0x0d, 0xfe, + 0xd4, 0x8b, 0x05, 0xe5, 0x00, 0x00, 0x01, 0x79, 0x19, 0x43, 0x10, 0xea, + 0x00, 0x00, 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, 0x68, 0x89, + 0x8b, 0xab, 0x98, 0xd3, 0x4f, 0x41, 0x4f, 0x7d, 0x1c, 0x52, 0xbe, 0x1b, + 0xf1, 0xbe, 0xb3, 0x68, 0x49, 0x5a, 0x91, 0x93, 0xdc, 0xac, 0xba, 0x6e, + 0x58, 0x8d, 0xcd, 0x3c, 0x5a, 0x26, 0x02, 0x21, 0x00, 0x85, 0x09, 0xf7, + 0x21, 0x4a, 0x66, 0x45, 0x77, 0xfe, 0xd5, 0x77, 0x25, 0xd5, 0xc5, 0x1a, + 0xb3, 0x33, 0xd8, 0x86, 0x52, 0xcc, 0xe1, 0x26, 0x21, 0x03, 0xcf, 0x1b, + 0x34, 0x24, 0xab, 0xc0, 0x1f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, + 0x00, 0xbd, 0x22, 0x40, 0xf1, 0x6d, 0xe7, 0x68, 0x89, 0x82, 0x53, 0xcd, + 0x64, 0xed, 0x21, 0x17, 0x90, 0x3a, 0xd0, 0xa3, 0x21, 0x42, 0x40, 0x60, + 0xf2, 0x2c, 0xf7, 0x40, 0xef, 0xc3, 0xf0, 0x22, 0x24, 0xc2, 0x51, 0x17, + 0x9d, 0x4b, 0x10, 0x9f, 0x86, 0x1a, 0x05, 0x4c, 0x6a, 0xe0, 0x13, 0xbb, + 0x29, 0xad, 0xf7, 0x18, 0x5f, 0x76, 0x01, 0x10, 0x8b, 0x1c, 0x29, 0x79, + 0x23, 0x94, 0x58, 0x1a, 0xa6, 0xf8, 0xac, 0x9b, 0x2e, 0xe0, 0x70, 0x1d, + 0x06, 0x1a, 0xe9, 0x5d, 0x24, 0x9f, 0x03, 0xff, 0x40, 0xe5, 0xc1, 0xb0, + 0xb9, 0xa7, 0x7e, 0x19, 0x3d, 0x0c, 0x99, 0x89, 0x81, 0xe4, 0x53, 0x9b, + 0xbd, 0x66, 0x1b, 0xba, 0x2e, 0xcd, 0xff, 0x24, 0x16, 0xd2, 0x89, 0xc9, + 0x75, 0xdd, 0xc9, 0x78, 0x25, 0x1e, 0x11, 0x43, 0x25, 0x06, 0x15, 0xe5, + 0xe3, 0x6b, 0xf9, 0x33, 0xee, 0x06, 0x16, 0x92, 0x8e, 0xe1, 0x8a, 0x93, + 0x41, 0x15, 0x8b, 0xf1, 0x06, 0xf7, 0x52, 0x07, 0x25, 0xb8, 0x6a, 0xae, + 0x46, 0x70, 0xa6, 0x81, 0x74, 0x70, 0x3c, 0x50, 0x42, 0x85, 0x65, 0x41, + 0xdb, 0x25, 0xb3, 0x4f, 0xce, 0x25, 0xb5, 0x2b, 0x62, 0xb7, 0x2b, 0xbf, + 0x66, 0xc4, 0xb4, 0x8a, 0x10, 0xb0, 0x50, 0x8e, 0x84, 0xf8, 0xe5, 0x28, + 0x86, 0xda, 0x7d, 0xe6, 0x65, 0xbf, 0xb1, 0xd5, 0x7d, 0x09, 0x28, 0x61, + 0xa3, 0x14, 0x89, 0x23, 0x35, 0x6e, 0x9c, 0x70, 0x06, 0x8b, 0xcb, 0x84, + 0xe8, 0x70, 0x8d, 0xb9, 0xfb, 0x74, 0xcf, 0x77, 0x63, 0x00, 0x5d, 0x8c, + 0xbb, 0x62, 0x4a, 0x2b, 0xc2, 0x8b, 0x2c, 0xd9, 0x9a, 0xa8, 0x83, 0x6f, + 0x06, 0x2a, 0x2a, 0x30, 0x4c, 0x39, 0xb4, 0xf8, 0x7d, 0x8c, 0x5e, 0xa7, + 0xcb, 0xce, 0x64, 0xe0, 0x27, 0xfa, 0x24, 0x42, 0xdd, 0xd1, 0x1d, 0xf8, + 0xa9, 0xd7, 0xc4, 0x0c, 0x92 +}; + +static const BYTE ocsp_cert_issuer[] = { + 0x30, 0x82, 0x04, 0xbe, 0x30, 0x82, 0x03, 0xa6, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x06, 0xd8, 0xd9, 0x04, 0xd5, 0x58, 0x43, 0x46, 0xf6, + 0x8a, 0x2f, 0xa7, 0x54, 0x22, 0x7e, 0xc4, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x30, 0x34, 0x31, 0x34, 0x30, + 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, 0x33, 0x31, 0x30, 0x34, + 0x31, 0x33, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x4f, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, + 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x44, 0x69, + 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x54, 0x4c, 0x53, 0x20, 0x52, + 0x53, 0x41, 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x32, 0x30, + 0x32, 0x30, 0x20, 0x43, 0x41, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, + 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, + 0x01, 0x01, 0x00, 0xc1, 0x4b, 0xb3, 0x65, 0x47, 0x70, 0xbc, 0xdd, 0x4f, + 0x58, 0xdb, 0xec, 0x9c, 0xed, 0xc3, 0x66, 0xe5, 0x1f, 0x31, 0x13, 0x54, + 0xad, 0x4a, 0x66, 0x46, 0x1f, 0x2c, 0x0a, 0xec, 0x64, 0x07, 0xe5, 0x2e, + 0xdc, 0xdc, 0xb9, 0x0a, 0x20, 0xed, 0xdf, 0xe3, 0xc4, 0xd0, 0x9e, 0x9a, + 0xa9, 0x7a, 0x1d, 0x82, 0x88, 0xe5, 0x11, 0x56, 0xdb, 0x1e, 0x9f, 0x58, + 0xc2, 0x51, 0xe7, 0x2c, 0x34, 0x0d, 0x2e, 0xd2, 0x92, 0xe1, 0x56, 0xcb, + 0xf1, 0x79, 0x5f, 0xb3, 0xbb, 0x87, 0xca, 0x25, 0x03, 0x7b, 0x9a, 0x52, + 0x41, 0x66, 0x10, 0x60, 0x4f, 0x57, 0x13, 0x49, 0xf0, 0xe8, 0x37, 0x67, + 0x83, 0xdf, 0xe7, 0xd3, 0x4b, 0x67, 0x4c, 0x22, 0x51, 0xa6, 0xdf, 0x0e, + 0x99, 0x10, 0xed, 0x57, 0x51, 0x74, 0x26, 0xe2, 0x7d, 0xc7, 0xca, 0x62, + 0x2e, 0x13, 0x1b, 0x7f, 0x23, 0x88, 0x25, 0x53, 0x6f, 0xc1, 0x34, 0x58, + 0x00, 0x8b, 0x84, 0xff, 0xf8, 0xbe, 0xa7, 0x58, 0x49, 0x22, 0x7b, 0x96, + 0xad, 0xa2, 0x88, 0x9b, 0x15, 0xbc, 0xa0, 0x7c, 0xdf, 0xe9, 0x51, 0xa8, + 0xd5, 0xb0, 0xed, 0x37, 0xe2, 0x36, 0xb4, 0x82, 0x4b, 0x62, 0xb5, 0x49, + 0x9a, 0xec, 0xc7, 0x67, 0xd6, 0xe3, 0x3e, 0xf5, 0xe3, 0xd6, 0x12, 0x5e, + 0x44, 0xf1, 0xbf, 0x71, 0x42, 0x7d, 0x58, 0x84, 0x03, 0x80, 0xb1, 0x81, + 0x01, 0xfa, 0xf9, 0xca, 0x32, 0xbb, 0xb4, 0x8e, 0x27, 0x87, 0x27, 0xc5, + 0x2b, 0x74, 0xd4, 0xa8, 0xd6, 0x97, 0xde, 0xc3, 0x64, 0xf9, 0xca, 0xce, + 0x53, 0xa2, 0x56, 0xbc, 0x78, 0x17, 0x8e, 0x49, 0x03, 0x29, 0xae, 0xfb, + 0x49, 0x4f, 0xa4, 0x15, 0xb9, 0xce, 0xf2, 0x5c, 0x19, 0x57, 0x6d, 0x6b, + 0x79, 0xa7, 0x2b, 0xa2, 0x27, 0x20, 0x13, 0xb5, 0xd0, 0x3d, 0x40, 0xd3, + 0x21, 0x30, 0x07, 0x93, 0xea, 0x99, 0xf5, 0x02, 0x03, 0x01, 0x00, 0x01, + 0xa3, 0x82, 0x01, 0x82, 0x30, 0x82, 0x01, 0x7e, 0x30, 0x12, 0x06, 0x03, + 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, 0x01, + 0xff, 0x02, 0x01, 0x00, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, + 0x16, 0x04, 0x14, 0xb7, 0x6b, 0xa2, 0xea, 0xa8, 0xaa, 0x84, 0x8c, 0x79, + 0xea, 0xb4, 0xda, 0x0f, 0x98, 0xb2, 0xc5, 0x95, 0x76, 0xb9, 0xf4, 0x30, + 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, + 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, 0xf0, 0xa3, 0xe2, + 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, 0x0e, 0x06, 0x03, + 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x01, 0x86, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x76, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x6a, 0x30, 0x68, 0x30, + 0x24, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, + 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, + 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, + 0x6d, 0x30, 0x40, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, + 0x02, 0x86, 0x34, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x61, + 0x63, 0x65, 0x72, 0x74, 0x73, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, + 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, + 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x42, 0x06, 0x03, 0x55, + 0x1d, 0x1f, 0x04, 0x3b, 0x30, 0x39, 0x30, 0x37, 0xa0, 0x35, 0xa0, 0x33, + 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, 0x6c, + 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x3d, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x36, + 0x30, 0x34, 0x30, 0x0b, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, + 0x6c, 0x02, 0x01, 0x30, 0x07, 0x06, 0x05, 0x67, 0x81, 0x0c, 0x01, 0x01, + 0x30, 0x08, 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x01, 0x30, 0x08, + 0x06, 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x02, 0x30, 0x08, 0x06, 0x06, + 0x67, 0x81, 0x0c, 0x01, 0x02, 0x03, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0x80, 0x32, 0xce, 0x5e, 0x0b, 0xdd, 0x6e, 0x5a, 0x0d, 0x0a, + 0xaf, 0xe1, 0xd6, 0x84, 0xcb, 0xc0, 0x8e, 0xfa, 0x85, 0x70, 0xed, 0xda, + 0x5d, 0xb3, 0x0c, 0xf7, 0x2b, 0x75, 0x40, 0xfe, 0x85, 0x0a, 0xfa, 0xf3, + 0x31, 0x78, 0xb7, 0x70, 0x4b, 0x1a, 0x89, 0x58, 0xba, 0x80, 0xbd, 0xf3, + 0x6b, 0x1d, 0xe9, 0x7e, 0xcf, 0x0b, 0xba, 0x58, 0x9c, 0x59, 0xd4, 0x90, + 0xd3, 0xfd, 0x6c, 0xfd, 0xd0, 0x98, 0x6d, 0xb7, 0x71, 0x82, 0x5b, 0xcf, + 0x6d, 0x0b, 0x5a, 0x09, 0xd0, 0x7b, 0xde, 0xc4, 0x43, 0xd8, 0x2a, 0xa4, + 0xde, 0x9e, 0x41, 0x26, 0x5f, 0xbb, 0x8f, 0x99, 0xcb, 0xdd, 0xae, 0xe1, + 0xa8, 0x6f, 0x9f, 0x87, 0xfe, 0x74, 0xb7, 0x1f, 0x1b, 0x20, 0xab, 0xb1, + 0x4f, 0xc6, 0xf5, 0x67, 0x5d, 0x5d, 0x9b, 0x3c, 0xe9, 0xff, 0x69, 0xf7, + 0x61, 0x6c, 0xd6, 0xd9, 0xf3, 0xfd, 0x36, 0xc6, 0xab, 0x03, 0x88, 0x76, + 0xd2, 0x4b, 0x2e, 0x75, 0x86, 0xe3, 0xfc, 0xd8, 0x55, 0x7d, 0x26, 0xc2, + 0x11, 0x77, 0xdf, 0x3e, 0x02, 0xb6, 0x7c, 0xf3, 0xab, 0x7b, 0x7a, 0x86, + 0x36, 0x6f, 0xb8, 0xf7, 0xd8, 0x93, 0x71, 0xcf, 0x86, 0xdf, 0x73, 0x30, + 0xfa, 0x7b, 0xab, 0xed, 0x2a, 0x59, 0xc8, 0x42, 0x84, 0x3b, 0x11, 0x17, + 0x1a, 0x52, 0xf3, 0xc9, 0x0e, 0x14, 0x7d, 0xa2, 0x5b, 0x72, 0x67, 0xba, + 0x71, 0xed, 0x57, 0x47, 0x66, 0xc5, 0xb8, 0x02, 0x4a, 0x65, 0x34, 0x5e, + 0x8b, 0xd0, 0x2a, 0x3c, 0x20, 0x9c, 0x51, 0x99, 0x4c, 0xe7, 0x52, 0x9e, + 0xf7, 0x6b, 0x11, 0x2b, 0x0d, 0x92, 0x7e, 0x1d, 0xe8, 0x8a, 0xeb, 0x36, + 0x16, 0x43, 0x87, 0xea, 0x2a, 0x63, 0xbf, 0x75, 0x3f, 0xeb, 0xde, 0xc4, + 0x03, 0xbb, 0x0a, 0x3c, 0xf7, 0x30, 0xef, 0xeb, 0xaf, 0x4c, 0xfc, 0x8b, + 0x36, 0x10, 0x73, 0x3e, 0xf3, 0xa4 +}; + +static const BYTE ocsp_cert_revoked[] = { + 0x30, 0x82, 0x06, 0x86, 0x30, 0x82, 0x05, 0x6e, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x0d, 0x2e, 0x67, 0xa2, 0x98, 0x85, 0x3b, 0x9a, 0x54, + 0x52, 0xe3, 0xa2, 0x85, 0xa4, 0x57, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x59, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x52, + 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20, 0x54, 0x4c, 0x53, 0x20, + 0x44, 0x56, 0x20, 0x52, 0x53, 0x41, 0x20, 0x4d, 0x69, 0x78, 0x65, 0x64, + 0x20, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x32, 0x30, 0x32, 0x30, + 0x20, 0x43, 0x41, 0x2d, 0x31, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x31, 0x31, + 0x30, 0x32, 0x37, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x5a, 0x17, 0x0d, + 0x32, 0x32, 0x31, 0x30, 0x32, 0x37, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, + 0x5a, 0x30, 0x1d, 0x31, 0x1b, 0x30, 0x19, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x12, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x2e, 0x62, 0x61, + 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xb0, 0x76, 0x2d, 0x55, 0x66, 0xdc, 0x72, + 0x8a, 0xa0, 0x9e, 0x85, 0x92, 0x38, 0x7f, 0x5b, 0xe1, 0x93, 0x8d, 0xad, + 0x06, 0xc8, 0xad, 0xe9, 0x89, 0xb4, 0xef, 0x1e, 0x77, 0x5b, 0x33, 0x45, + 0x16, 0x60, 0x7d, 0x33, 0x38, 0x68, 0x04, 0xd7, 0xc9, 0x83, 0x42, 0x83, + 0xd9, 0x30, 0x4b, 0x54, 0x49, 0x14, 0xca, 0xed, 0xbe, 0x0c, 0x76, 0xba, + 0x5f, 0xa6, 0x5c, 0x33, 0x78, 0x3f, 0x39, 0xf2, 0x49, 0xa8, 0x88, 0x32, + 0xee, 0x53, 0x21, 0x14, 0xd3, 0xaa, 0x5c, 0x58, 0x3c, 0x39, 0xcc, 0xf7, + 0x80, 0xb1, 0x27, 0x1f, 0x54, 0x79, 0x7b, 0x6c, 0x8b, 0xff, 0x41, 0xaa, + 0x39, 0x24, 0x95, 0x5f, 0x71, 0xbc, 0x49, 0xbf, 0x39, 0x3b, 0xa5, 0xd5, + 0xe1, 0xa5, 0xde, 0x1d, 0x40, 0x81, 0x25, 0xdc, 0x8a, 0x47, 0x82, 0xfe, + 0xcd, 0x7c, 0x4b, 0x2c, 0x04, 0xbb, 0xd3, 0x27, 0x56, 0x51, 0xa0, 0x61, + 0xf2, 0xd2, 0xcb, 0x55, 0x08, 0x25, 0x2a, 0x85, 0xdb, 0x2c, 0x06, 0x8d, + 0x0d, 0x61, 0xc2, 0x5b, 0x3e, 0x9b, 0x46, 0xdc, 0x58, 0xff, 0x13, 0x27, + 0xbe, 0x0a, 0x44, 0x1e, 0x68, 0xfe, 0xe1, 0xf6, 0xb7, 0xde, 0x9f, 0x8e, + 0x6c, 0xc4, 0xb5, 0x19, 0xfa, 0xd7, 0xd3, 0x4f, 0x55, 0xa8, 0x61, 0x79, + 0xdb, 0x61, 0x2f, 0x6a, 0x9c, 0x2c, 0xf1, 0xc4, 0x81, 0xbb, 0x9e, 0xd2, + 0x02, 0x05, 0xba, 0x9c, 0x14, 0xa0, 0xf9, 0xf3, 0x54, 0x79, 0x7d, 0x69, + 0xd9, 0xba, 0x66, 0x1c, 0x87, 0x95, 0x41, 0x50, 0x0e, 0xf9, 0x5e, 0xe1, + 0xb7, 0xbd, 0xf5, 0x31, 0x24, 0xc5, 0x21, 0x21, 0x03, 0x8a, 0xcf, 0x6d, + 0x78, 0x58, 0xde, 0xd9, 0x30, 0x7d, 0x03, 0x42, 0x52, 0xd6, 0xb0, 0x1b, + 0xb9, 0xc9, 0x54, 0x1b, 0x5a, 0xe8, 0xc8, 0x53, 0xf0, 0xac, 0x2b, 0x82, + 0x10, 0x27, 0xa6, 0xa9, 0x70, 0x25, 0xae, 0xf8, 0xa7, 0x02, 0x03, 0x01, + 0x00, 0x01, 0xa3, 0x82, 0x03, 0x84, 0x30, 0x82, 0x03, 0x80, 0x30, 0x1f, + 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xa4, + 0x8d, 0xe5, 0xbe, 0x7c, 0x79, 0xe4, 0x70, 0x23, 0x6d, 0x2e, 0x29, 0x34, + 0xad, 0x23, 0x58, 0xdc, 0xf5, 0x31, 0x7f, 0x30, 0x1d, 0x06, 0x03, 0x55, + 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0xb0, 0xc8, 0xce, 0x20, 0xb2, 0x78, + 0xcc, 0x1d, 0x23, 0xef, 0xf0, 0xfe, 0xd6, 0x0e, 0x29, 0x4b, 0xac, 0x15, + 0x72, 0x3c, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, 0x16, 0x30, + 0x14, 0x82, 0x12, 0x72, 0x65, 0x76, 0x6f, 0x6b, 0x65, 0x64, 0x2e, 0x62, + 0x61, 0x64, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x0e, 0x06, + 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x05, + 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, 0x14, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x81, 0x9b, 0x06, + 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x81, 0x93, 0x30, 0x81, 0x90, 0x30, 0x46, + 0xa0, 0x44, 0xa0, 0x42, 0x86, 0x40, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x52, 0x61, 0x70, 0x69, 0x64, + 0x53, 0x53, 0x4c, 0x54, 0x4c, 0x53, 0x44, 0x56, 0x52, 0x53, 0x41, 0x4d, + 0x69, 0x78, 0x65, 0x64, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x32, 0x30, + 0x32, 0x30, 0x43, 0x41, 0x2d, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x46, + 0xa0, 0x44, 0xa0, 0x42, 0x86, 0x40, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x72, 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x52, 0x61, 0x70, 0x69, 0x64, + 0x53, 0x53, 0x4c, 0x54, 0x4c, 0x53, 0x44, 0x56, 0x52, 0x53, 0x41, 0x4d, + 0x69, 0x78, 0x65, 0x64, 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x32, 0x30, + 0x32, 0x30, 0x43, 0x41, 0x2d, 0x31, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x3e, + 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x37, 0x30, 0x35, 0x30, 0x33, 0x06, + 0x06, 0x67, 0x81, 0x0c, 0x01, 0x02, 0x01, 0x30, 0x29, 0x30, 0x27, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1b, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, + 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, + 0x50, 0x53, 0x30, 0x81, 0x85, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x79, 0x30, 0x77, 0x30, 0x24, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, 0x69, 0x67, + 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4f, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x43, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x61, 0x63, 0x65, 0x72, 0x74, + 0x73, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x52, 0x61, 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x54, + 0x4c, 0x53, 0x44, 0x56, 0x52, 0x53, 0x41, 0x4d, 0x69, 0x78, 0x65, 0x64, + 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x32, 0x30, 0x32, 0x30, 0x43, 0x41, + 0x2d, 0x31, 0x2e, 0x63, 0x72, 0x74, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, + 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x82, 0x01, 0x7d, 0x06, 0x0a, 0x2b, + 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02, 0x04, 0x82, 0x01, + 0x6d, 0x04, 0x82, 0x01, 0x69, 0x01, 0x67, 0x00, 0x76, 0x00, 0x29, 0x79, + 0xbe, 0xf0, 0x9e, 0x39, 0x39, 0x21, 0xf0, 0x56, 0x73, 0x9f, 0x63, 0xa5, + 0x77, 0xe5, 0xbe, 0x57, 0x7d, 0x9c, 0x60, 0x0a, 0xf8, 0xf9, 0x4d, 0x5d, + 0x26, 0x5c, 0x25, 0x5d, 0xc7, 0x84, 0x00, 0x00, 0x01, 0x7c, 0xc3, 0xa4, + 0xf7, 0x37, 0x00, 0x00, 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, + 0x77, 0xb0, 0x79, 0x18, 0xf3, 0xde, 0x34, 0x70, 0xfa, 0xf2, 0x1b, 0xc2, + 0x32, 0x39, 0xc8, 0xc8, 0x95, 0xb0, 0xc8, 0x7a, 0x8f, 0x62, 0x23, 0x58, + 0xdd, 0xad, 0xf9, 0x1b, 0xbe, 0x84, 0x95, 0xed, 0x02, 0x21, 0x00, 0xdd, + 0x25, 0x68, 0x47, 0xa3, 0x84, 0x5f, 0x95, 0xb1, 0xea, 0xe7, 0xbc, 0x0a, + 0x09, 0x92, 0xf9, 0x5a, 0x56, 0x72, 0x31, 0xec, 0x07, 0xd6, 0xc6, 0x97, + 0x4d, 0x4c, 0x7b, 0x90, 0x75, 0x64, 0xae, 0x00, 0x76, 0x00, 0x51, 0xa3, + 0xb0, 0xf5, 0xfd, 0x01, 0x79, 0x9c, 0x56, 0x6d, 0xb8, 0x37, 0x78, 0x8f, + 0x0c, 0xa4, 0x7a, 0xcc, 0x1b, 0x27, 0xcb, 0xf7, 0x9e, 0x88, 0x42, 0x9a, + 0x0d, 0xfe, 0xd4, 0x8b, 0x05, 0xe5, 0x00, 0x00, 0x01, 0x7c, 0xc3, 0xa4, + 0xf7, 0x64, 0x00, 0x00, 0x04, 0x03, 0x00, 0x47, 0x30, 0x45, 0x02, 0x20, + 0x4c, 0x22, 0xff, 0x65, 0x39, 0x6b, 0x7e, 0x7b, 0x15, 0x21, 0x79, 0x44, + 0xc2, 0xeb, 0xb8, 0x4c, 0x2a, 0xc9, 0xa5, 0xc7, 0xac, 0xce, 0x5f, 0x6a, + 0x5d, 0xe8, 0xb7, 0x24, 0xc5, 0x76, 0xec, 0x19, 0x02, 0x21, 0x00, 0x94, + 0x5e, 0x02, 0xee, 0x14, 0x60, 0x80, 0x96, 0xbc, 0x0e, 0x39, 0x16, 0x01, + 0xa8, 0x37, 0x9f, 0x15, 0xb9, 0xb9, 0xba, 0x0f, 0xa2, 0x0c, 0x5a, 0x17, + 0x90, 0xa5, 0xe1, 0x33, 0x36, 0x45, 0xf2, 0x00, 0x75, 0x00, 0x41, 0xc8, + 0xca, 0xb1, 0xdf, 0x22, 0x46, 0x4a, 0x10, 0xc6, 0xa1, 0x3a, 0x09, 0x42, + 0x87, 0x5e, 0x4e, 0x31, 0x8b, 0x1b, 0x03, 0xeb, 0xeb, 0x4b, 0xc7, 0x68, + 0xf0, 0x90, 0x62, 0x96, 0x06, 0xf6, 0x00, 0x00, 0x01, 0x7c, 0xc3, 0xa4, + 0xf6, 0xdf, 0x00, 0x00, 0x04, 0x03, 0x00, 0x46, 0x30, 0x44, 0x02, 0x20, + 0x68, 0x8a, 0x5f, 0x50, 0xb7, 0x76, 0xda, 0x7e, 0x34, 0x32, 0xa5, 0x77, + 0x02, 0xa6, 0xfa, 0xa7, 0x87, 0xbb, 0xdb, 0x41, 0x5c, 0x80, 0x40, 0x2c, + 0x05, 0xe5, 0x09, 0xdd, 0x3f, 0xcc, 0x6d, 0x9f, 0x02, 0x20, 0x7b, 0x1d, + 0x64, 0x48, 0x61, 0x19, 0x75, 0xb6, 0x37, 0xd1, 0x3c, 0x1e, 0x38, 0x78, + 0x86, 0x7a, 0xf2, 0x79, 0x14, 0x08, 0x42, 0xe8, 0xdd, 0x0f, 0xff, 0x38, + 0x3a, 0x3c, 0x36, 0xd9, 0xbf, 0xd9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, + 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x03, 0x82, 0x01, + 0x01, 0x00, 0xd5, 0x8c, 0xbd, 0xbe, 0xe4, 0xdc, 0x94, 0xa4, 0xb7, 0xf3, + 0x49, 0xaf, 0xc4, 0x99, 0x26, 0xda, 0x27, 0x68, 0xda, 0xe8, 0xb8, 0xc1, + 0xba, 0xc6, 0x30, 0xb6, 0x16, 0xaa, 0x50, 0xfe, 0xf4, 0x77, 0x07, 0xeb, + 0x99, 0xf2, 0xda, 0xdd, 0x77, 0x1d, 0x19, 0x82, 0xf7, 0x24, 0x2a, 0x3b, + 0xa0, 0x63, 0xe0, 0xdb, 0x09, 0xbe, 0x10, 0x7f, 0xc5, 0x1f, 0x81, 0xba, + 0xaf, 0x9e, 0x49, 0xce, 0x32, 0x30, 0x49, 0x17, 0x8f, 0x74, 0xc6, 0xd6, + 0xcd, 0x6a, 0xd8, 0x3b, 0x47, 0x7b, 0xf0, 0xe0, 0x0c, 0xbb, 0xc0, 0x8e, + 0x3a, 0x1d, 0xa3, 0x7f, 0x92, 0xac, 0x7e, 0x8d, 0xdc, 0xa4, 0xb5, 0x30, + 0x2a, 0x57, 0x13, 0x23, 0xa7, 0xee, 0x25, 0xc6, 0x37, 0xed, 0x48, 0xb2, + 0x4a, 0xd0, 0x01, 0xfc, 0x85, 0xe5, 0xc1, 0xe2, 0xe0, 0xdc, 0x8c, 0x61, + 0x74, 0xaa, 0xaf, 0x68, 0x28, 0x26, 0x45, 0x94, 0xa3, 0xb1, 0x4c, 0xc9, + 0x5c, 0xc7, 0x92, 0xa2, 0x6c, 0x4a, 0x80, 0x6f, 0xdd, 0x48, 0xfa, 0x4f, + 0x04, 0xb2, 0x4a, 0x73, 0x17, 0xf2, 0xf9, 0x1e, 0x8e, 0x5c, 0xe9, 0x23, + 0xec, 0x53, 0xff, 0x3e, 0xc7, 0x8a, 0xb6, 0x18, 0x89, 0xbc, 0x77, 0x45, + 0x67, 0x4b, 0x9a, 0x73, 0x75, 0x6b, 0x57, 0xc8, 0xc0, 0x6a, 0xcb, 0x84, + 0x1d, 0xf4, 0xed, 0xef, 0x70, 0x16, 0x77, 0x8e, 0xf3, 0x1a, 0x8e, 0xbb, + 0x95, 0xf3, 0xeb, 0xf8, 0x5a, 0xe4, 0xa9, 0xb1, 0xdf, 0x1d, 0x36, 0xab, + 0x0a, 0xdd, 0x91, 0xaf, 0x2d, 0x71, 0x3c, 0xab, 0x97, 0x18, 0x03, 0xdc, + 0x5c, 0x1a, 0xa9, 0xb1, 0xdb, 0xb6, 0x48, 0x40, 0xc7, 0x19, 0xa7, 0x81, + 0x14, 0x0b, 0x0d, 0xce, 0x38, 0x6f, 0xda, 0xcf, 0xce, 0x0f, 0x64, 0x13, + 0x28, 0xf3, 0x4d, 0x67, 0x1b, 0x2c, 0xd1, 0x16, 0x54, 0x19, 0x6f, 0xaa, + 0x08, 0x54, 0xa3, 0x4d, 0x67, 0x64 +}; + +static const BYTE ocsp_cert_revoked_issuer[] = { + 0x30, 0x82, 0x05, 0x51, 0x30, 0x82, 0x04, 0x39, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x10, 0x07, 0x98, 0x36, 0x03, 0xad, 0xe3, 0x99, 0x08, 0x21, + 0x9c, 0xa0, 0x0c, 0x27, 0xbc, 0x8a, 0x6c, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, 0x30, 0x61, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, + 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, + 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x10, 0x77, + 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x31, 0x20, 0x30, 0x1e, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x17, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x47, + 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x20, 0x52, 0x6f, 0x6f, 0x74, 0x20, 0x43, + 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x32, 0x30, 0x30, 0x37, 0x31, 0x36, 0x31, + 0x32, 0x32, 0x35, 0x32, 0x37, 0x5a, 0x17, 0x0d, 0x32, 0x33, 0x30, 0x35, + 0x33, 0x31, 0x32, 0x33, 0x35, 0x39, 0x35, 0x39, 0x5a, 0x30, 0x59, 0x31, + 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, + 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0c, 0x44, + 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49, 0x6e, 0x63, 0x31, + 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2a, 0x52, 0x61, + 0x70, 0x69, 0x64, 0x53, 0x53, 0x4c, 0x20, 0x54, 0x4c, 0x53, 0x20, 0x44, + 0x56, 0x20, 0x52, 0x53, 0x41, 0x20, 0x4d, 0x69, 0x78, 0x65, 0x64, 0x20, + 0x53, 0x48, 0x41, 0x32, 0x35, 0x36, 0x20, 0x32, 0x30, 0x32, 0x30, 0x20, + 0x43, 0x41, 0x2d, 0x31, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, + 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, + 0x00, 0xda, 0x6e, 0x43, 0x55, 0x55, 0x99, 0x7b, 0xd9, 0x95, 0xa2, 0x66, + 0xc4, 0x65, 0x58, 0xa2, 0xd0, 0x0c, 0x17, 0x3a, 0x00, 0xa6, 0x88, 0x5b, + 0x24, 0x07, 0x8d, 0xa7, 0x33, 0x7e, 0xe3, 0xd2, 0xdb, 0x82, 0x4a, 0xcc, + 0x2e, 0xfd, 0xad, 0x6e, 0x52, 0x08, 0xf0, 0x7e, 0x37, 0xbc, 0xde, 0xd4, + 0x16, 0xe9, 0xb1, 0x57, 0xb9, 0x49, 0x74, 0xfc, 0x0b, 0x3f, 0x6d, 0xaa, + 0x6b, 0x4b, 0x15, 0xf5, 0xcc, 0x02, 0xaf, 0xa4, 0x19, 0xa0, 0x61, 0x28, + 0x6d, 0xd6, 0xbe, 0xe2, 0x9b, 0x9f, 0x1b, 0x46, 0x92, 0x7c, 0x74, 0x02, + 0x42, 0x1b, 0xa5, 0x6a, 0xa2, 0xa9, 0x3d, 0xc6, 0x18, 0x38, 0xf8, 0xd3, + 0xc2, 0x0a, 0x89, 0x03, 0xce, 0x00, 0x15, 0x88, 0xfc, 0x97, 0xf2, 0x1e, + 0x43, 0xc9, 0xf4, 0xd5, 0x5c, 0x82, 0xba, 0xb3, 0x08, 0x1c, 0x0e, 0x3b, + 0xf2, 0xdb, 0x36, 0x1b, 0xa1, 0x86, 0xb4, 0x4c, 0x74, 0xb9, 0xc9, 0xc4, + 0x7d, 0x5d, 0x90, 0x1d, 0x42, 0xfa, 0xe0, 0x40, 0xb6, 0xca, 0x1e, 0xf2, + 0x6d, 0xba, 0x28, 0xe6, 0xff, 0x27, 0x15, 0x65, 0x78, 0x97, 0x1f, 0xf1, + 0x71, 0xfc, 0x68, 0xc6, 0x41, 0x53, 0x56, 0x70, 0x08, 0x46, 0x01, 0xeb, + 0x1f, 0x6b, 0xd4, 0x74, 0xe8, 0x95, 0xf6, 0xc9, 0x4e, 0x8b, 0x1d, 0xf3, + 0xe4, 0xa3, 0xec, 0xda, 0xb2, 0xb6, 0x6d, 0xb6, 0x9c, 0x87, 0xc4, 0xa1, + 0xe4, 0x64, 0xa4, 0x82, 0x9d, 0x87, 0x46, 0x84, 0xbf, 0x9b, 0x2d, 0x2d, + 0x0a, 0xad, 0x6f, 0x8f, 0x22, 0xc9, 0x78, 0xfd, 0x1a, 0x37, 0x03, 0xdd, + 0xde, 0xb9, 0x39, 0x3b, 0xc2, 0xe2, 0x7d, 0xf2, 0xde, 0xbf, 0xd8, 0xfe, + 0x50, 0xa6, 0x68, 0xd2, 0xdb, 0x74, 0x56, 0xf4, 0xcb, 0x91, 0xd1, 0xa6, + 0x48, 0xde, 0x21, 0xd6, 0x65, 0x58, 0xe8, 0x39, 0xc6, 0x7c, 0xec, 0x29, + 0xd4, 0x2e, 0x52, 0x2b, 0x43, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, + 0x02, 0x0b, 0x30, 0x82, 0x02, 0x07, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, + 0x0e, 0x04, 0x16, 0x04, 0x14, 0xa4, 0x8d, 0xe5, 0xbe, 0x7c, 0x79, 0xe4, + 0x70, 0x23, 0x6d, 0x2e, 0x29, 0x34, 0xad, 0x23, 0x58, 0xdc, 0xf5, 0x31, + 0x7f, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, + 0x80, 0x14, 0x03, 0xde, 0x50, 0x35, 0x56, 0xd1, 0x4c, 0xbb, 0x66, 0xf0, + 0xa3, 0xe2, 0x1b, 0x1b, 0xc3, 0x97, 0xb2, 0x3d, 0xd1, 0x55, 0x30, 0x0e, + 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, + 0x01, 0x86, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, 0x30, + 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x12, 0x06, + 0x03, 0x55, 0x1d, 0x13, 0x01, 0x01, 0xff, 0x04, 0x08, 0x30, 0x06, 0x01, + 0x01, 0xff, 0x02, 0x01, 0x00, 0x30, 0x34, 0x06, 0x08, 0x2b, 0x06, 0x01, + 0x05, 0x05, 0x07, 0x01, 0x01, 0x04, 0x28, 0x30, 0x26, 0x30, 0x24, 0x06, + 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x18, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x64, + 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x7b, 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x74, 0x30, 0x72, 0x30, 0x37, + 0xa0, 0x35, 0xa0, 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, + 0x2f, 0x63, 0x72, 0x6c, 0x33, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, + 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, + 0x65, 0x72, 0x74, 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, + 0x74, 0x43, 0x41, 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x37, 0xa0, 0x35, 0xa0, + 0x33, 0x86, 0x31, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x72, + 0x6c, 0x34, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, + 0x63, 0x6f, 0x6d, 0x2f, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, + 0x47, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x52, 0x6f, 0x6f, 0x74, 0x43, 0x41, + 0x2e, 0x63, 0x72, 0x6c, 0x30, 0x81, 0xce, 0x06, 0x03, 0x55, 0x1d, 0x20, + 0x04, 0x81, 0xc6, 0x30, 0x81, 0xc3, 0x30, 0x81, 0xc0, 0x06, 0x04, 0x55, + 0x1d, 0x20, 0x00, 0x30, 0x81, 0xb7, 0x30, 0x28, 0x06, 0x08, 0x2b, 0x06, + 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x1c, 0x68, 0x74, 0x74, 0x70, + 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, + 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x50, 0x53, + 0x30, 0x81, 0x8a, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, + 0x02, 0x30, 0x7e, 0x0c, 0x7c, 0x41, 0x6e, 0x79, 0x20, 0x75, 0x73, 0x65, + 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x43, 0x65, 0x72, + 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x63, 0x6f, 0x6e, + 0x73, 0x74, 0x69, 0x74, 0x75, 0x74, 0x65, 0x73, 0x20, 0x61, 0x63, 0x63, + 0x65, 0x70, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, + 0x68, 0x65, 0x20, 0x52, 0x65, 0x6c, 0x79, 0x69, 0x6e, 0x67, 0x20, 0x50, + 0x61, 0x72, 0x74, 0x79, 0x20, 0x41, 0x67, 0x72, 0x65, 0x65, 0x6d, 0x65, + 0x6e, 0x74, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x74, 0x65, 0x64, 0x20, 0x61, + 0x74, 0x20, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x77, 0x77, + 0x77, 0x2e, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72, 0x74, 0x2e, 0x63, + 0x6f, 0x6d, 0x2f, 0x72, 0x70, 0x61, 0x2d, 0x75, 0x61, 0x30, 0x0d, 0x06, + 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b, 0x05, 0x00, + 0x03, 0x82, 0x01, 0x01, 0x00, 0x22, 0xe3, 0xdc, 0x6d, 0x48, 0xeb, 0x8e, + 0xca, 0x00, 0x72, 0x73, 0x2e, 0x74, 0xaa, 0xe0, 0x93, 0x84, 0x6e, 0x39, + 0xc4, 0x87, 0x54, 0x02, 0xc4, 0x02, 0x69, 0x71, 0x55, 0x45, 0xaf, 0x5a, + 0xb0, 0xf6, 0x81, 0xfe, 0x32, 0xc8, 0x35, 0x72, 0x4b, 0xde, 0xa5, 0x7d, + 0x27, 0x41, 0xa1, 0xd9, 0xb6, 0x4c, 0xd2, 0x4e, 0x32, 0x38, 0xc7, 0x80, + 0x31, 0x9e, 0x7b, 0xb2, 0x63, 0xfa, 0x26, 0x47, 0x09, 0x8a, 0x18, 0x4e, + 0x16, 0x57, 0xd0, 0x6b, 0x5f, 0x1a, 0x96, 0x37, 0x7e, 0xc4, 0xd7, 0x3a, + 0x6f, 0xe1, 0x97, 0xea, 0x81, 0x5c, 0x08, 0x71, 0xab, 0xfa, 0x0b, 0x04, + 0xc8, 0xf3, 0x3c, 0xaa, 0xf9, 0x4a, 0x1b, 0x17, 0x39, 0x4f, 0x97, 0x87, + 0x57, 0x35, 0x7a, 0x8e, 0x98, 0xe9, 0xcb, 0x39, 0x7a, 0x54, 0x42, 0xa9, + 0x6b, 0x11, 0xfa, 0x81, 0xd1, 0x95, 0xa5, 0x05, 0x60, 0x8e, 0x43, 0x91, + 0xf7, 0x26, 0x3d, 0x5c, 0x05, 0x25, 0x16, 0x7c, 0xe5, 0x38, 0x2a, 0x6a, + 0xb2, 0x6e, 0xeb, 0xd9, 0x95, 0x0a, 0xa4, 0x37, 0xeb, 0x85, 0x49, 0xd5, + 0xcd, 0x7d, 0xa7, 0x48, 0xcd, 0x79, 0x5d, 0x28, 0xf8, 0xf2, 0xb5, 0x41, + 0x04, 0x09, 0xc6, 0x25, 0x69, 0x0b, 0x3e, 0x28, 0xe5, 0x00, 0x27, 0x77, + 0xb1, 0x61, 0x4c, 0x55, 0x48, 0x8a, 0x47, 0x3d, 0x42, 0xe4, 0xf6, 0x72, + 0x7a, 0x5d, 0xa5, 0xec, 0x9f, 0xd6, 0xe1, 0xdf, 0x7d, 0x28, 0x52, 0xd2, + 0x62, 0x0a, 0x32, 0xe4, 0x60, 0xe6, 0x01, 0x1a, 0x70, 0x2d, 0xcf, 0xff, + 0x7d, 0x77, 0xe4, 0xaf, 0x8d, 0x27, 0x31, 0x8f, 0x22, 0x6c, 0x29, 0xb1, + 0x0a, 0xc8, 0xd7, 0x41, 0x37, 0xb4, 0x7c, 0x96, 0xed, 0xae, 0xb2, 0xcb, + 0xc9, 0x64, 0x25, 0x93, 0xd5, 0x43, 0x57, 0x6f, 0x7a, 0x10, 0x8f, 0xe4, + 0x40, 0xe2, 0x4d, 0x2d, 0x51, 0x24, 0x27, 0x9e, 0x0f +}; + static void testVerifyRevocation(void) { BOOL ret; @@ -3643,6 +4157,44 @@ static void testVerifyRevocation(void) CertCloseStore(revPara.hCrlStore, 0); CertFreeCertificateContext(certs[1]); CertFreeCertificateContext(certs[0]); + + /* OCSP */ + certs[0] = CertCreateCertificateContext(X509_ASN_ENCODING, ocsp_cert, sizeof(ocsp_cert)); + memset(&revPara, 0, sizeof(revPara)); + revPara.cbSize = sizeof(revPara); + memset(&status, 0x55, sizeof(status)); + status.cbSize = sizeof(status); + SetLastError(0xdeadbeef); + ret = CertVerifyRevocation(X509_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE, 1, (void **)&certs[0], + 0, &revPara, &status); + ok(!ret, "success\n"); + ok(GetLastError() == CRYPT_E_REVOCATION_OFFLINE, "got %08lx\n", GetLastError()); + + revPara.pIssuerCert = CertCreateCertificateContext(X509_ASN_ENCODING, ocsp_cert_issuer, + sizeof(ocsp_cert_issuer)); + ret = CertVerifyRevocation(X509_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE, 1, (void **)&certs[0], + 0, &revPara, &status); + ok(ret, "got %08lx\n", GetLastError()); + ok(!status.dwError, "got %08lx\n", status.dwError); + ok(!status.dwIndex, "got %ld\n", status.dwIndex); + CertFreeCertificateContext(revPara.pIssuerCert); + CertFreeCertificateContext(certs[0]); + + certs[0] = CertCreateCertificateContext(X509_ASN_ENCODING, ocsp_cert_revoked, sizeof(ocsp_cert_revoked)); + revPara.pIssuerCert = CertCreateCertificateContext(X509_ASN_ENCODING, ocsp_cert_revoked_issuer, + sizeof(ocsp_cert_revoked_issuer)); + memset(&status, 0x55, sizeof(status)); + status.cbSize = sizeof(status); + SetLastError(0xdeadbeef); + ret = CertVerifyRevocation(X509_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE, 1, (void **)&certs[0], + 0, &revPara, &status); + ok(!ret, "success\n"); + ok(GetLastError() == CRYPT_E_REVOKED, "got %08lx\n", GetLastError()); + ok(status.dwError == CRYPT_E_REVOKED, "got %08lx\n", status.dwError); + ok(!status.dwIndex, "got %ld\n", status.dwIndex); + ok(!status.dwReason, "got %lu\n", status.dwReason); + CertFreeCertificateContext(revPara.pIssuerCert); + CertFreeCertificateContext(certs[0]); } static BYTE privKey[] = { @@ -3753,45 +4305,42 @@ static void testAcquireCertPrivateKey(void) CERT_KEY_CONTEXT keyContext; /* Don't cache provider */ + SetLastError(0xdeadbeef); ret = CryptAcquireCertificatePrivateKey(cert, 0, NULL, &certCSP, &keySpec, &callerFree); - ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", - GetLastError()); - if (ret) - { - ok(callerFree, "Expected callerFree to be TRUE\n"); - CryptReleaseContext(certCSP, 0); - } + ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, "got %08lx\n", GetLastError()); + ok(callerFree, "Expected callerFree to be TRUE\n"); + CryptReleaseContext(certCSP, 0); + SetLastError(0xdeadbeef); ret = CryptAcquireCertificatePrivateKey(cert, 0, NULL, &certCSP, NULL, NULL); - ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", - GetLastError()); + ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, "got %08lx\n", GetLastError()); CryptReleaseContext(certCSP, 0); /* Use the key prov info's caching (there shouldn't be any) */ + SetLastError(0xdeadbeef); ret = CryptAcquireCertificatePrivateKey(cert, CRYPT_ACQUIRE_USE_PROV_INFO_FLAG, NULL, &certCSP, &keySpec, &callerFree); - ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", - GetLastError()); - if (ret) - { - ok(callerFree, "Expected callerFree to be TRUE\n"); - CryptReleaseContext(certCSP, 0); - } + ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, "got %08lx\n", GetLastError()); + ok(callerFree, "Expected callerFree to be TRUE\n"); + CryptReleaseContext(certCSP, 0); /* Cache it (and check that it's cached) */ + SetLastError(0xdeadbeef); ret = CryptAcquireCertificatePrivateKey(cert, CRYPT_ACQUIRE_CACHE_FLAG, NULL, &certCSP, &keySpec, &callerFree); - ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", - GetLastError()); + ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, "got %08lx\n", GetLastError()); ok(!callerFree, "Expected callerFree to be FALSE\n"); size = sizeof(keyContext); ret = CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); - ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", - GetLastError()); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); /* Remove the cached provider */ CryptReleaseContext(keyContext.hCryptProv, 0); @@ -3802,17 +4351,17 @@ static void testAcquireCertPrivateKey(void) CertSetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, 0, &keyProvInfo); /* Now use the key prov info's caching */ + SetLastError(0xdeadbeef); ret = CryptAcquireCertificatePrivateKey(cert, CRYPT_ACQUIRE_USE_PROV_INFO_FLAG, NULL, &certCSP, &keySpec, &callerFree); - ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", - GetLastError()); + ok(ret, "CryptAcquireCertificatePrivateKey failed: %08lx\n", GetLastError()); + ok(GetLastError() == ERROR_SUCCESS, "got %08lx\n", GetLastError()); ok(!callerFree, "Expected callerFree to be FALSE\n"); size = sizeof(keyContext); ret = CertGetCertificateContextProperty(cert, CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size); - ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", - GetLastError()); + ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); CryptReleaseContext(certCSP, 0); CryptDestroyKey(key); @@ -3828,7 +4377,7 @@ static void testAcquireCertPrivateKey(void) ok(ret, "CryptExportKey failed: %08lx\n", GetLastError()); if (ret) { - LPBYTE buf = HeapAlloc(GetProcessHeap(), 0, size), encodedKey; + LPBYTE buf = malloc(size), encodedKey; ret = CryptExportKey(key, 0, PUBLICKEYBLOB, 0, buf, &size); ok(ret, "CryptExportKey failed: %08lx\n", GetLastError()); @@ -3846,7 +4395,7 @@ static void testAcquireCertPrivateKey(void) "Unexpected value\n"); LocalFree(encodedKey); } - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } CryptDestroyKey(key); } @@ -3855,7 +4404,7 @@ static void testAcquireCertPrivateKey(void) ok(ret, "CryptExportPublicKeyInfoEx failed: %08lx\n", GetLastError()); if (ret) { - PCERT_PUBLIC_KEY_INFO info = HeapAlloc(GetProcessHeap(), 0, size); + PCERT_PUBLIC_KEY_INFO info = malloc(size); ret = CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, X509_ASN_ENCODING, NULL, 0, NULL, info, &size); @@ -3867,7 +4416,7 @@ static void testAcquireCertPrivateKey(void) ok(!memcmp(info->PublicKey.pbData, asnEncodedPublicKey, info->PublicKey.cbData), "Unexpected value\n"); } - HeapFree(GetProcessHeap(), 0, info); + free(info); } CryptReleaseContext(csp, 0); @@ -3992,7 +4541,7 @@ static void testKeyProvInfo(void) ret = CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size); ok(ret, "CertGetCertificateContextProperty error %#lx\n", GetLastError()); - info = HeapAlloc(GetProcessHeap(), 0, size); + info = malloc(size); ret = CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, info, &size); ok(ret, "CertGetCertificateContextProperty error %#lx\n", GetLastError()); ok(!lstrcmpW(info->pwszContainerName, containerW), "got %s\n", wine_dbgstr_w(info->pwszContainerName)); @@ -4010,7 +4559,7 @@ static void testKeyProvInfo(void) ok(info->rgProvParam[1].cbData == param[1].cbData, "got %#lx\n", info->rgProvParam[1].cbData); ok(!memcmp(info->rgProvParam[1].pbData, param[1].pbData, param[1].cbData), "param2 mismatch\n"); ok(info->rgProvParam[1].dwFlags == param[1].dwFlags, "got %#lx\n", info->rgProvParam[1].dwFlags); - HeapFree(GetProcessHeap(), 0, info); + free(info); ret = CertAddCertificateContextToStore(store, cert, CERT_STORE_ADD_NEW, NULL); ok(ret, "CertAddCertificateContextToStore error %#lx\n", GetLastError()); @@ -4029,7 +4578,7 @@ static void testKeyProvInfo(void) ret = CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &size); ok(ret, "CertGetCertificateContextProperty error %#lx\n", GetLastError()); - info = HeapAlloc(GetProcessHeap(), 0, size); + info = malloc(size); ret = CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, info, &size); ok(ret, "CertGetCertificateContextProperty error %#lx\n", GetLastError()); ok(!lstrcmpW(info->pwszContainerName, containerW), "got %s\n", wine_dbgstr_w(info->pwszContainerName)); @@ -4047,7 +4596,7 @@ static void testKeyProvInfo(void) ok(info->rgProvParam[1].cbData == param[1].cbData, "got %#lx\n", info->rgProvParam[1].cbData); ok(!memcmp(info->rgProvParam[1].pbData, param[1].pbData, param[1].cbData), "param2 mismatch\n"); ok(info->rgProvParam[1].dwFlags == param[1].dwFlags, "got %#lx\n", info->rgProvParam[1].dwFlags); - HeapFree(GetProcessHeap(), 0, info); + free(info); ret = CertDeleteCertificateFromStore(cert); ok(ret, "CertDeleteCertificateFromStore error %#lx\n", GetLastError()); @@ -4126,7 +4675,7 @@ static void test_VerifySignature(void) ok(!status, "got %#lx\n", status); ok(hash_len == sizeof(hash_value), "got %lu\n", hash_len); - sig_value = HeapAlloc(GetProcessHeap(), 0, info->Signature.cbData); + sig_value = malloc(info->Signature.cbData); for (i = 0; i < info->Signature.cbData; i++) sig_value[i] = info->Signature.pbData[info->Signature.cbData - i - 1]; @@ -4134,7 +4683,7 @@ static void test_VerifySignature(void) status = BCryptVerifySignature(bkey, &pad, hash_value, sizeof(hash_value), sig_value, info->Signature.cbData, BCRYPT_PAD_PKCS1); ok(!status, "got %#lx\n", status); - HeapFree(GetProcessHeap(), 0, sig_value); + free(sig_value); BCryptDestroyHash(bhash); BCryptCloseAlgorithmProvider(alg, 0); BCryptDestroyKey(bkey); diff --git a/dlls/crypt32/tests/chain.c b/dlls/crypt32/tests/chain.c index 9ed1b28bf70..32f00801799 100644 --- wine/dlls/crypt32/tests/chain.c +++ wine/dlls/crypt32/tests/chain.c @@ -4958,6 +4958,13 @@ static const ChainPolicyCheck msRootPolicyCheck[] = { { 0, CERT_E_UNTRUSTEDROOT, 0, 0, NULL }, NULL, 0 }, }; +static const ChainPolicyCheck msRootPolicyCheck_approot[] = { + { { ARRAY_SIZE(chain32), chain32 }, + { 0, CERT_E_UNTRUSTEDROOT, 0, 2, NULL }, NULL, TODO_ELEMENTS }, + { { ARRAY_SIZE(chain33), chain33 }, + { 0, 0, 0, 0, NULL }, NULL, 0 }, +}; + static const char *num_to_str(WORD num) { static char buf[6]; @@ -5295,8 +5302,16 @@ static void check_ssl_policy(void) static void check_msroot_policy(void) { + CERT_CHAIN_POLICY_PARA para; + CHECK_CHAIN_POLICY_STATUS_ARRAY(CERT_CHAIN_POLICY_MICROSOFT_ROOT, NULL, msRootPolicyCheck, &may2020, NULL); + + para.cbSize = sizeof(para); + para.pvExtraPolicyPara = NULL; + para.dwFlags = MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG; + CHECK_CHAIN_POLICY_STATUS_ARRAY(CERT_CHAIN_POLICY_MICROSOFT_ROOT, NULL, + msRootPolicyCheck_approot, &may2020, ¶); } static void testVerifyCertChainPolicy(void) diff --git a/dlls/crypt32/tests/encode.c b/dlls/crypt32/tests/encode.c index 9dabe58efba..527e663860a 100644 --- wine/dlls/crypt32/tests/encode.c +++ wine/dlls/crypt32/tests/encode.c @@ -2315,10 +2315,10 @@ static const BYTE modulus1[] = { 0,0,0,1,1,1,1,1 }; static const BYTE modulus2[] = { 1,1,1,1,1,0,0,0 }; static const BYTE modulus3[] = { 0x80,1,1,1,1,0,0,0 }; static const BYTE modulus4[] = { 1,1,1,1,1,0,0,0x80 }; -static const BYTE mod1_encoded[] = { 0x30,0x0f,0x02,0x08,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x02,0x03,0x01,0x00,0x01 }; -static const BYTE mod2_encoded[] = { 0x30,0x0c,0x02,0x05,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x01,0x00,0x01 }; -static const BYTE mod3_encoded[] = { 0x30,0x0c,0x02,0x05,0x01,0x01,0x01,0x01,0x80,0x02,0x03,0x01,0x00,0x01 }; -static const BYTE mod4_encoded[] = { 0x30,0x10,0x02,0x09,0x00,0x80,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x01,0x00,0x01 }; +static const BYTE mod1_encoded[] = { 0x30,0x0f,0x02,0x08,0x01,0x01,0x01,0x01,0x01,0x00,0x00,0x00,0x02,0x03,0x02,0x00,0x01 }; +static const BYTE mod2_encoded[] = { 0x30,0x0c,0x02,0x05,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x02,0x00,0x01 }; +static const BYTE mod3_encoded[] = { 0x30,0x0c,0x02,0x05,0x01,0x01,0x01,0x01,0x80,0x02,0x03,0x02,0x00,0x01 }; +static const BYTE mod4_encoded[] = { 0x30,0x10,0x02,0x09,0x00,0x80,0x00,0x00,0x01,0x01,0x01,0x01,0x01,0x02,0x03,0x02,0x00,0x01 }; struct EncodedRSAPubKey { @@ -2351,7 +2351,7 @@ static void test_encodeRsaPublicKey(DWORD dwEncoding) hdr->aiKeyAlg = CALG_RSA_KEYX; rsaPubKey->magic = 0x31415352; rsaPubKey->bitlen = sizeof(modulus1) * 8; - rsaPubKey->pubexp = 65537; + rsaPubKey->pubexp = 131073; memcpy(toEncode + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), modulus1, sizeof(modulus1)); @@ -2480,7 +2480,7 @@ static void test_decodeRsaPublicKey(DWORD dwEncoding) "Expected magic RSA1, got %08lx\n", rsaPubKey->magic); ok(rsaPubKey->bitlen == rsaPubKeys[i].decodedModulusLen * 8, "Wrong bit len %ld\n", rsaPubKey->bitlen); - ok(rsaPubKey->pubexp == 65537, "Expected pubexp 65537, got %ld\n", + ok(rsaPubKey->pubexp == 131073, "Expected pubexp 131073, got %ld\n", rsaPubKey->pubexp); ok(!memcmp(buf + sizeof(BLOBHEADER) + sizeof(RSAPUBKEY), rsaPubKeys[i].modulus, rsaPubKeys[i].decodedModulusLen), @@ -2497,7 +2497,7 @@ static void test_encodeRsaPublicKey_Bcrypt(DWORD dwEncoding) BOOL ret; BYTE *buf = NULL; DWORD bufSize = 0, i; - BYTE pubexp[] = {0x01,0x00,0x01,0x00}; /* 65537 */ + BYTE pubexp[] = {0x01,0x00,0x02,0x00}; /* 131073 */ /* Verify that the Magic value doesn't matter */ hdr->Magic = 1; @@ -2568,7 +2568,7 @@ static void test_decodeRsaPublicKey_Bcrypt(DWORD dwEncoding) if (ret) { BCRYPT_RSAKEY_BLOB *hdr = (BCRYPT_RSAKEY_BLOB *)buf; - BYTE pubexp[] = {0xff,0xff,0xff,0xff}, pubexp_expected[] = {0x01,0x00,0x01}; + BYTE pubexp[] = {0xff,0xff,0xff,0xcc}, pubexp_expected[] = {0x01,0x00,0x02}; /* CNG_RSA_PUBLIC_KEY_BLOB stores the exponent * in big-endian format, so we need to convert it to little-endian */ @@ -2584,15 +2584,15 @@ static void test_decodeRsaPublicKey_Bcrypt(DWORD dwEncoding) /* Windows decodes the exponent to 3 bytes, since it will fit. * Our implementation currently unconditionally decodes to a DWORD (4 bytes) */ - todo_wine ok(hdr->cbPublicExp == 3, "Expected cbPublicExp 3, got %ld\n", hdr->cbPublicExp); + ok(hdr->cbPublicExp == 3, "Expected cbPublicExp 3, got %ld\n", hdr->cbPublicExp); ok(hdr->cbModulus == rsaPubKeys[i].decodedModulusLen, "Wrong modulus len %ld\n", hdr->cbModulus); ok(hdr->cbPrime1 == 0,"Wrong cbPrime1 %ld\n", hdr->cbPrime1); ok(hdr->cbPrime2 == 0,"Wrong cbPrime2 %ld\n", hdr->cbPrime2); ok(!memcmp(pubexp, pubexp_expected, sizeof(pubexp_expected)), "Wrong exponent\n"); - todo_wine ok(pubexp[3] == 0xff, "Got %02x\n", pubexp[3]); + ok(pubexp[3] == 0xcc, "Got %02x\n", pubexp[3]); - leModulus = HeapAlloc(GetProcessHeap(), 0, hdr->cbModulus); + leModulus = malloc(hdr->cbModulus); /* * CNG_RSA_PUBLIC_KEY_BLOB stores the modulus in big-endian format, * so we need to convert it to little-endian @@ -2603,7 +2603,7 @@ static void test_decodeRsaPublicKey_Bcrypt(DWORD dwEncoding) rsaPubKeys[i].modulus, rsaPubKeys[i].decodedModulusLen), "Unexpected modulus\n"); LocalFree(buf); - LocalFree(leModulus); + free(leModulus); } } } @@ -2799,13 +2799,13 @@ static void test_decodeExtensions(DWORD dwEncoding) ret = CryptDecodeObjectEx(dwEncoding, X509_EXTENSIONS, exts[i].encoded, exts[i].encoded[1] + 2, 0, NULL, NULL, &bufSize); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, bufSize); + buf = calloc(1, bufSize); if (buf) { ret = CryptDecodeObjectEx(dwEncoding, X509_EXTENSIONS, exts[i].encoded, exts[i].encoded[1] + 2, 0, NULL, buf, &bufSize); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } } @@ -3770,14 +3770,14 @@ static void test_decodeCRLDistPoints(DWORD dwEncoding) distPointWithUrlAndIssuer, distPointWithUrlAndIssuer[1] + 2, 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + buf = calloc(1, size); if (buf) { ret = CryptDecodeObjectEx(dwEncoding, X509_CRL_DIST_POINTS, distPointWithUrlAndIssuer, distPointWithUrlAndIssuer[1] + 2, 0, NULL, buf, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } @@ -4906,13 +4906,13 @@ static void test_decodeEnhancedKeyUsage(DWORD dwEncoding) ret = CryptDecodeObjectEx(dwEncoding, X509_ENHANCED_KEY_USAGE, encodedUsage, sizeof(encodedUsage), 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + buf = calloc(1, size); if (buf) { ret = CryptDecodeObjectEx(dwEncoding, X509_ENHANCED_KEY_USAGE, encodedUsage, sizeof(encodedUsage), 0, NULL, buf, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } @@ -5391,14 +5391,14 @@ static void test_decodeAuthorityInfoAccess(DWORD dwEncoding) authorityInfoAccessWithUrlAndIPAddr, sizeof(authorityInfoAccessWithUrlAndIPAddr), 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + buf = calloc(1, size); if (buf) { ret = CryptDecodeObjectEx(dwEncoding, X509_AUTHORITY_INFO_ACCESS, authorityInfoAccessWithUrlAndIPAddr, sizeof(authorityInfoAccessWithUrlAndIPAddr), 0, NULL, buf, &size); ok(ret, "CryptDecodeObjectEx failed: %lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } @@ -6354,13 +6354,13 @@ static void test_decodePKCSAttributes(DWORD dwEncoding) ret = CryptDecodeObjectEx(dwEncoding, PKCS_ATTRIBUTES, doublePKCSAttributes, sizeof(doublePKCSAttributes), 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + buf = calloc(1, size); if (buf) { ret = CryptDecodeObjectEx(dwEncoding, PKCS_ATTRIBUTES, doublePKCSAttributes, sizeof(doublePKCSAttributes), 0, NULL, buf, &size); ok(ret, "CryptDecodeObjectEx failed: %lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } @@ -6522,14 +6522,14 @@ static void test_decodePKCSSMimeCapabilities(DWORD dwEncoding) ret = CryptDecodeObjectEx(dwEncoding, PKCS_SMIME_CAPABILITIES, twoCapabilities, sizeof(twoCapabilities), 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + ptr = calloc(1, size); if (ptr) { SetLastError(0xdeadbeef); ret = CryptDecodeObjectEx(dwEncoding, PKCS_SMIME_CAPABILITIES, twoCapabilities, sizeof(twoCapabilities), 0, NULL, ptr, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, ptr); + free(ptr); } } @@ -7645,13 +7645,13 @@ static void test_decodeCertPolicies(DWORD dwEncoding) ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_POLICIES, twoPolicies, sizeof(twoPolicies), 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + info = calloc(1, size); if (info) { ret = CryptDecodeObjectEx(dwEncoding, X509_CERT_POLICIES, twoPolicies, sizeof(twoPolicies), 0, NULL, info, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, info); + free(info); } } @@ -7788,14 +7788,14 @@ static void test_decodeCertPolicyMappings(DWORD dwEncoding) policyMappingWithTwoMappings, sizeof(policyMappingWithTwoMappings), 0, NULL, NULL, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - info = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + info = calloc(1, size); if (info) { ret = CryptDecodeObjectEx(dwEncoding, mappingOids[i], policyMappingWithTwoMappings, sizeof(policyMappingWithTwoMappings), 0, NULL, info, &size); ok(ret, "CryptDecodeObjectEx failed: %08lx\n", GetLastError()); - HeapFree(GetProcessHeap(), 0, info); + free(info); } } } @@ -8225,7 +8225,6 @@ static void test_decodeRsaPrivateKey(DWORD dwEncoding) } } -/* Free *pInfo with HeapFree */ static void testExportPublicKey(HCRYPTPROV csp, PCERT_PUBLIC_KEY_INFO *pInfo) { BOOL ret; @@ -8262,7 +8261,7 @@ static void testExportPublicKey(HCRYPTPROV csp, PCERT_PUBLIC_KEY_INFO *pInfo) ret = CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, X509_ASN_ENCODING, NULL, 0, NULL, NULL, &size); ok(ret, "CryptExportPublicKeyInfoEx failed: %08lx\n", GetLastError()); - *pInfo = HeapAlloc(GetProcessHeap(), 0, size); + *pInfo = malloc(size); if (*pInfo) { ret = CryptExportPublicKeyInfoEx(csp, AT_SIGNATURE, @@ -8411,7 +8410,7 @@ static void testPortPublicKeyInfo(void) testExportPublicKey(csp, &info); testImportPublicKey(csp, info); - HeapFree(GetProcessHeap(), 0, info); + free(info); CryptReleaseContext(csp, 0); ret = CryptAcquireContextA(&csp, cspName, MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_DELETEKEYSET); @@ -8674,6 +8673,26 @@ static const BYTE ocsp_basic_response[] = { 0x33, 0x36, 0x30, 0x31, 0x5a }; +static const BYTE ocsp_basic_response2[] = { + 0x30, 0x81, 0xbe, 0xa1, 0x34, 0x30, 0x32, 0x31, 0x0b, 0x30, 0x09, 0x06, + 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x16, 0x30, 0x14, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x4c, 0x65, 0x74, 0x27, 0x73, + 0x20, 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x31, 0x0b, 0x30, 0x09, + 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x02, 0x52, 0x33, 0x18, 0x0f, 0x32, + 0x30, 0x32, 0x32, 0x31, 0x30, 0x32, 0x30, 0x30, 0x36, 0x30, 0x31, 0x30, + 0x30, 0x5a, 0x30, 0x75, 0x30, 0x73, 0x30, 0x4b, 0x30, 0x09, 0x06, 0x05, + 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x48, 0xda, 0xc9, + 0xa0, 0xfb, 0x2b, 0xd3, 0x2d, 0x4f, 0xf0, 0xde, 0x68, 0xd2, 0xf5, 0x67, + 0xb7, 0x35, 0xf9, 0xb3, 0xc4, 0x04, 0x14, 0x14, 0x2e, 0xb3, 0x17, 0xb7, + 0x58, 0x56, 0xcb, 0xae, 0x50, 0x09, 0x40, 0xe6, 0x1f, 0xaf, 0x9d, 0x8b, + 0x14, 0xc2, 0xc6, 0x02, 0x12, 0x03, 0x26, 0x1c, 0x82, 0x80, 0xf3, 0x8c, + 0x13, 0xef, 0xae, 0x83, 0x9d, 0x89, 0xb9, 0xcd, 0x59, 0x83, 0x5b, 0x80, + 0x00, 0x18, 0x0f, 0x32, 0x30, 0x32, 0x32, 0x31, 0x30, 0x32, 0x30, 0x30, + 0x36, 0x30, 0x30, 0x30, 0x30, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, + 0x32, 0x32, 0x31, 0x30, 0x32, 0x37, 0x30, 0x35, 0x35, 0x39, 0x35, 0x38, + 0x5a +}; + static const BYTE ocsp_basic_response_revoked[] = { 0x30, 0x81, 0xb1, 0xa2, 0x16, 0x04, 0x14, 0xa4, 0x8d, 0xe5, 0xbe, 0x7c, 0x79, 0xe4, 0x70, 0x23, 0x6d, 0x2e, 0x29, 0x34, 0xad, 0x23, 0x58, 0xdc, @@ -8759,22 +8778,36 @@ static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) static const BYTE resp_id2[] = { 0xa4, 0x8d, 0xe5, 0xbe, 0x7c, 0x79, 0xe4, 0x70, 0x23, 0x6d, 0x2e, 0x29, 0x34, 0xad, 0x23, 0x58, 0xdc, 0xf5, 0x31, 0x7f}; + static const BYTE resp_id3[] = { + 0x30, 0x32, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x4c, 0x65, 0x74, 0x27, 0x73, 0x20, + 0x45, 0x6e, 0x63, 0x72, 0x79, 0x70, 0x74, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x13, 0x02, 0x52, 0x33}; static const BYTE name_hash[] = { 0xe4, 0xe3, 0x95, 0xa2, 0x29, 0xd3, 0xd4, 0xc1, 0xc3, 0x1f, 0xf0, 0x98, 0x0c, 0x0b, 0x4e, 0xc0, 0x09, 0x8a, 0xab, 0xd8}; static const BYTE name_hash2[] = { 0x74, 0xb4, 0xe7, 0x23, 0x19, 0xc7, 0x65, 0x92, 0x15, 0x40, 0x44, 0x7b, 0xc7, 0xce, 0x3e, 0x90, 0xc2, 0x18, 0x76, 0xeb}; + static const BYTE name_hash3[] = { + 0x48, 0xda, 0xc9, 0xa0, 0xfb, 0x2b, 0xd3, 0x2d, 0x4f, 0xf0, 0xde, 0x68, 0xd2, 0xf5, 0x67, 0xb7, + 0x35, 0xf9, 0xb3, 0xc4}; static const BYTE key_hash[] = { 0xb7, 0x6b, 0xa2, 0xea, 0xa8, 0xaa, 0x84, 0x8c, 0x79, 0xea, 0xb4, 0xda, 0x0f, 0x98, 0xb2, 0xc5, 0x95, 0x76, 0xb9, 0xf4}; static const BYTE key_hash2[] = { 0xa4, 0x8d, 0xe5, 0xbe, 0x7c, 0x79, 0xe4, 0x70, 0x23, 0x6d, 0x2e, 0x29, 0x34, 0xad, 0x23, 0x58, 0xdc, 0xf5, 0x31, 0x7f}; + static const BYTE key_hash3[] = { + 0x14, 0x2e, 0xb3, 0x17, 0xb7, 0x58, 0x56, 0xcb, 0xae, 0x50, 0x09, 0x40, 0xe6, 0x1f, 0xaf, 0x9d, + 0x8b, 0x14, 0xc2, 0xc6}; static const BYTE serial[] = { 0xb1, 0xc1, 0x87, 0x54, 0x54, 0xac, 0x1e, 0x55, 0x40, 0xfb, 0xef, 0xd9, 0x6d, 0x8f, 0x49, 0x08}; static const BYTE serial2[] = { 0x2f, 0x57, 0xa4, 0x85, 0xa2, 0xe3, 0x52, 0x54, 0x9a, 0x3b, 0x85, 0x98, 0xa2, 0x67, 0x2e, 0x0d}; + static const BYTE serial3[] = { + 0x5b, 0x83, 0x59, 0xcd, 0xb9, 0x89, 0x9d, 0x83, 0xae, 0xef, 0x13, 0x8c, 0xf3, 0x80, 0x82, 0x1c, + 0x26, 0x03}; OCSP_BASIC_RESPONSE_INFO *info; OCSP_BASIC_RESPONSE_ENTRY *entry; OCSP_BASIC_REVOKED_INFO *revoked; @@ -8801,11 +8834,11 @@ static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) ok(entry->CertId.HashAlgorithm.Parameters.cbData == 2, "got %lu\n", entry->CertId.HashAlgorithm.Parameters.cbData); ok(entry->CertId.HashAlgorithm.Parameters.pbData[0] == 5, "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[0]); ok(!entry->CertId.HashAlgorithm.Parameters.pbData[1], "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[1]); - ok(entry->CertId.IssuerNameHash.cbData == 20, "got %lu\n", entry->CertId.IssuerNameHash.cbData); + ok(entry->CertId.IssuerNameHash.cbData == sizeof(name_hash), "got %lu\n", entry->CertId.IssuerNameHash.cbData); ok(!memcmp(entry->CertId.IssuerNameHash.pbData, name_hash, sizeof(name_hash)), "wrong data\n"); - ok(entry->CertId.IssuerKeyHash.cbData == 20, "got %lu\n", entry->CertId.IssuerKeyHash.cbData); + ok(entry->CertId.IssuerKeyHash.cbData == sizeof(key_hash), "got %lu\n", entry->CertId.IssuerKeyHash.cbData); ok(!memcmp(entry->CertId.IssuerKeyHash.pbData, key_hash, sizeof(key_hash)), "wrong data\n"); - ok(entry->CertId.SerialNumber.cbData == 16, "got %lu\n", entry->CertId.SerialNumber.cbData); + ok(entry->CertId.SerialNumber.cbData == sizeof(serial), "got %lu\n", entry->CertId.SerialNumber.cbData); ok(!memcmp(entry->CertId.SerialNumber.pbData, serial, sizeof(serial)), "wrong data\n"); ok(entry->dwCertStatus == 0, "got %lu\n", entry->dwCertStatus); ok(entry->pRevokedInfo == NULL, "got %p\n", entry->pRevokedInfo); @@ -8824,9 +8857,8 @@ static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) size = 0; ret = CryptDecodeObjectEx(dwEncoding, OCSP_BASIC_RESPONSE, ocsp_basic_response_revoked, sizeof(ocsp_basic_response_revoked), CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size); - todo_wine ok(ret, "got %08lx\n", GetLastError()); + ok(ret, "got %08lx\n", GetLastError()); - if (ret) { ok(!info->dwVersion, "got %lu\n", info->dwVersion); ok(info->dwResponderIdChoice == 2, "got %lu\n", info->dwResponderIdChoice); ok(info->ByKeyResponderId.cbData == sizeof(resp_id), "got %lu\n", info->ByKeyResponderId.cbData); @@ -8841,11 +8873,11 @@ static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) ok(entry->CertId.HashAlgorithm.Parameters.cbData == 2, "got %lu\n", entry->CertId.HashAlgorithm.Parameters.cbData); ok(entry->CertId.HashAlgorithm.Parameters.pbData[0] == 5, "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[0]); ok(!entry->CertId.HashAlgorithm.Parameters.pbData[1], "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[1]); - ok(entry->CertId.IssuerNameHash.cbData == 20, "got %lu\n", entry->CertId.IssuerNameHash.cbData); + ok(entry->CertId.IssuerNameHash.cbData == sizeof(name_hash2), "got %lu\n", entry->CertId.IssuerNameHash.cbData); ok(!memcmp(entry->CertId.IssuerNameHash.pbData, name_hash2, sizeof(name_hash2)), "wrong data\n"); - ok(entry->CertId.IssuerKeyHash.cbData == 20, "got %lu\n", entry->CertId.IssuerKeyHash.cbData); + ok(entry->CertId.IssuerKeyHash.cbData == sizeof(key_hash2), "got %lu\n", entry->CertId.IssuerKeyHash.cbData); ok(!memcmp(entry->CertId.IssuerKeyHash.pbData, key_hash2, sizeof(key_hash2)), "wrong data\n"); - ok(entry->CertId.SerialNumber.cbData == 16, "got %lu\n", entry->CertId.SerialNumber.cbData); + ok(entry->CertId.SerialNumber.cbData == sizeof(serial2), "got %lu\n", entry->CertId.SerialNumber.cbData); ok(!memcmp(entry->CertId.SerialNumber.pbData, serial2, sizeof(serial2)), "wrong data\n"); ok(entry->dwCertStatus == 1, "got %lu\n", entry->dwCertStatus); ok(entry->pRevokedInfo != NULL, "got NULL\n"); @@ -8864,7 +8896,46 @@ static void test_decodeOCSPBasicResponseInfo(DWORD dwEncoding) ok(!info->cExtension, "got %lu\n", info->cExtension); ok(info->rgExtension == NULL, "got %p\n", info->rgExtension); - } + LocalFree(info); + + size = 0; + ret = CryptDecodeObjectEx(dwEncoding, OCSP_BASIC_RESPONSE, ocsp_basic_response2, + sizeof(ocsp_basic_response2), CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size); + ok(ret, "got %08lx\n", GetLastError()); + + ok(!info->dwVersion, "got %lu\n", info->dwVersion); + ok(info->dwResponderIdChoice == 1, "got %lu\n", info->dwResponderIdChoice); + ok(info->ByNameResponderId.cbData == sizeof(resp_id3), "got %lu\n", info->ByNameResponderId.cbData); + ok(!memcmp(info->ByNameResponderId.pbData, resp_id3, sizeof(resp_id3)), "wrong data\n"); + + ok(info->ProducedAt.dwLowDateTime == 1408824832, "got %lu\n", info->ProducedAt.dwLowDateTime); + ok(info->ProducedAt.dwHighDateTime == 30991433, "got %lu\n", info->ProducedAt.dwHighDateTime); + ok(info->cResponseEntry == 1, "got %lu\n", info->cResponseEntry); + ok(info->rgResponseEntry != NULL, "got %p\n", info->rgResponseEntry); + + entry = info->rgResponseEntry; + ok(!strcmp(entry->CertId.HashAlgorithm.pszObjId, szOID_OIWSEC_sha1), "got '%s'\n", entry->CertId.HashAlgorithm.pszObjId); + ok(entry->CertId.HashAlgorithm.Parameters.cbData == 2, "got %lu\n", entry->CertId.HashAlgorithm.Parameters.cbData); + ok(entry->CertId.HashAlgorithm.Parameters.pbData[0] == 5, "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[0]); + ok(!entry->CertId.HashAlgorithm.Parameters.pbData[1], "got 0x%02x\n", entry->CertId.HashAlgorithm.Parameters.pbData[1]); + ok(entry->CertId.IssuerNameHash.cbData == sizeof(name_hash3), "got %lu\n", entry->CertId.IssuerNameHash.cbData); + ok(!memcmp(entry->CertId.IssuerNameHash.pbData, name_hash3, sizeof(name_hash3)), "wrong data\n"); + + ok(entry->CertId.IssuerKeyHash.cbData == sizeof(key_hash3), "got %lu\n", entry->CertId.IssuerKeyHash.cbData); + ok(!memcmp(entry->CertId.IssuerKeyHash.pbData, key_hash3, sizeof(key_hash3)), "wrong data\n"); + ok(entry->CertId.SerialNumber.cbData == sizeof(serial3), "got %lu\n", entry->CertId.SerialNumber.cbData); + ok(!memcmp(entry->CertId.SerialNumber.pbData, serial3, sizeof(serial3)), "wrong data\n"); + ok(entry->dwCertStatus == 0, "got %lu\n", entry->dwCertStatus); + ok(entry->pRevokedInfo == NULL, "got %p\n", entry->pRevokedInfo); + ok(entry->ThisUpdate.dwLowDateTime == 808824832, "got %lu\n", entry->ThisUpdate.dwLowDateTime); + ok(entry->ThisUpdate.dwHighDateTime == 30991433, "got %lu\n", entry->ThisUpdate.dwHighDateTime); + ok(entry->NextUpdate.dwLowDateTime == 1474872064, "got %lu\n", entry->NextUpdate.dwLowDateTime); + ok(entry->NextUpdate.dwHighDateTime == 30992841, "got %lu\n", entry->NextUpdate.dwHighDateTime); + ok(!entry->cExtension, "got %lu\n", entry->cExtension); + ok(entry->rgExtension == NULL, "got %p\n", entry->rgExtension); + + ok(!info->cExtension, "got %lu\n", info->cExtension); + ok(info->rgExtension == NULL, "got %p\n", info->rgExtension); LocalFree(info); } diff --git a/dlls/crypt32/tests/main.c b/dlls/crypt32/tests/main.c index 19dde3fb28f..1b125e89d50 100644 --- wine/dlls/crypt32/tests/main.c +++ wine/dlls/crypt32/tests/main.c @@ -349,7 +349,7 @@ static void test_getDefaultCryptProv(void) prov = pI_CryptGetDefaultCryptProv(test_prov[i].algid); if (!prov) { - todo_wine_if(test_prov[i].algid == CALG_DSS_SIGN || test_prov[i].algid == CALG_NO_SIGN) +todo_wine_if(test_prov[i].algid == CALG_DSS_SIGN || test_prov[i].algid == CALG_NO_SIGN) ok(test_prov[i].optional, "%lu: I_CryptGetDefaultCryptProv(%#x) failed\n", i, test_prov[i].algid); continue; } diff --git a/dlls/crypt32/tests/message.c b/dlls/crypt32/tests/message.c index fa4790a2a6b..e6339553e52 100644 --- wine/dlls/crypt32/tests/message.c +++ wine/dlls/crypt32/tests/message.c @@ -688,14 +688,14 @@ static void test_hash_message(void) /* Actually attempting to get the hashed data fails, perhaps because * detached is FALSE. */ - hashedBlob = HeapAlloc(GetProcessHeap(), 0, hashedBlobSize); + hashedBlob = malloc(hashedBlobSize); SetLastError(0xdeadbeef); ret = CryptHashMessage(¶, FALSE, 2, toHash, hashSize, hashedBlob, &hashedBlobSize, NULL, NULL); ok(!ret && GetLastError() == CRYPT_E_MSG_ERROR, "expected CRYPT_E_MSG_ERROR, got 0x%08lx (%ld)\n", GetLastError(), GetLastError()); - HeapFree(GetProcessHeap(), 0, hashedBlob); + free(hashedBlob); } /* Repeating tests with fDetached = TRUE results in success */ SetLastError(0xdeadbeef); @@ -704,7 +704,7 @@ static void test_hash_message(void) ok(ret, "CryptHashMessage failed: 0x%08lx\n", GetLastError()); if (ret) { - hashedBlob = HeapAlloc(GetProcessHeap(), 0, hashedBlobSize); + hashedBlob = malloc(hashedBlobSize); SetLastError(0xdeadbeef); ret = CryptHashMessage(¶, TRUE, 2, toHash, hashSize, hashedBlob, &hashedBlobSize, NULL, NULL); @@ -713,7 +713,7 @@ static void test_hash_message(void) "unexpected size of detached blob %ld\n", hashedBlobSize); ok(!memcmp(hashedBlob, detachedHashBlob, hashedBlobSize), "unexpected detached blob value\n"); - HeapFree(GetProcessHeap(), 0, hashedBlob); + free(hashedBlob); } /* Hashing a single item with fDetached = FALSE also succeeds */ SetLastError(0xdeadbeef); @@ -722,7 +722,7 @@ static void test_hash_message(void) ok(ret, "CryptHashMessage failed: 0x%08lx\n", GetLastError()); if (ret) { - hashedBlob = HeapAlloc(GetProcessHeap(), 0, hashedBlobSize); + hashedBlob = malloc(hashedBlobSize); ret = CryptHashMessage(¶, FALSE, 1, toHash, hashSize, hashedBlob, &hashedBlobSize, NULL, NULL); ok(ret, "CryptHashMessage failed: 0x%08lx\n", GetLastError()); @@ -730,7 +730,7 @@ static void test_hash_message(void) "unexpected size of detached blob %ld\n", hashedBlobSize); ok(!memcmp(hashedBlob, hashBlob, hashedBlobSize), "unexpected detached blob value\n"); - HeapFree(GetProcessHeap(), 0, hashedBlob); + free(hashedBlob); } /* Check the computed hash value too. You don't need to get the encoded * blob to get it. @@ -743,7 +743,7 @@ static void test_hash_message(void) computedHashSize); if (ret) { - computedHash = HeapAlloc(GetProcessHeap(), 0, computedHashSize); + computedHash = malloc(computedHashSize); SetLastError(0xdeadbeef); ret = CryptHashMessage(¶, TRUE, 2, toHash, hashSize, NULL, &hashedBlobSize, computedHash, &computedHashSize); @@ -752,7 +752,7 @@ static void test_hash_message(void) "unexpected size of hash value %ld\n", computedHashSize); ok(!memcmp(computedHash, hashVal, computedHashSize), "unexpected value\n"); - HeapFree(GetProcessHeap(), 0, computedHash); + free(computedHash); } } diff --git a/dlls/crypt32/tests/msg.c b/dlls/crypt32/tests/msg.c index f779d70695e..16f7402c613 100644 --- wine/dlls/crypt32/tests/msg.c +++ wine/dlls/crypt32/tests/msg.c @@ -274,14 +274,14 @@ static void check_param(LPCSTR test, HCRYPTMSG msg, DWORD param, ret = CryptMsgGetParam(msg, param, 0, NULL, &size); ok(ret, "%s: CryptMsgGetParam failed: %08lx\n", test, GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); ret = CryptMsgGetParam(msg, param, 0, buf, &size); ok(ret, "%s: CryptMsgGetParam failed: %08lx\n", test, GetLastError()); ok(size == expectedSize, "%s: expected size %ld, got %ld\n", test, expectedSize, size); if (size == expectedSize && size) ok(!memcmp(buf, expected, size), "%s: unexpected data\n", test); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } static void test_data_msg_open(void) diff --git a/dlls/crypt32/tests/object.c b/dlls/crypt32/tests/object.c index 22936d3738d..a3c7c8c0743 100644 --- wine/dlls/crypt32/tests/object.c +++ wine/dlls/crypt32/tests/object.c @@ -95,130 +95,10 @@ L"MIIBiQYJKoZIhvcNAQcCoIIBejCCAXYCAQExDjAMBggqhkiG9w0CBQUAMBMGCSqG" "s+9Z0WbRm8CatppebW9tDVmpqm7pLKAe7sJgvFm+P2MGjckRHSNkku8u/FcppK/g" "7pMZOVHkRLgLKPSoDQ=="; -/* Self-signed .exe, built with tcc, signed with signtool - * (and a certificate generated on a self-signed CA). - * - * small.c: - * int _start() - * { - * return 0; - * } - * - * tcc -nostdlib small.c - * signtool sign /v /f codesign.pfx small.exe - */ -static const BYTE signed_pe_blob[] = -{ - 0x4D,0x5A,0x90,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xFF,0xFF,0x00,0x00,0xB8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x0E,0x1F,0xBA,0x0E,0x00,0xB4,0x09,0xCD, - 0x21,0xB8,0x01,0x4C,0xCD,0x21,0x54,0x68,0x69,0x73,0x20,0x70,0x72,0x6F,0x67,0x72,0x61,0x6D,0x20,0x63,0x61,0x6E,0x6E,0x6F, - 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6E,0x20,0x69,0x6E,0x20,0x44,0x4F,0x53,0x20,0x6D,0x6F,0x64,0x65,0x2E,0x0D,0x0D,0x0A, - 0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x45,0x00,0x00,0x4C,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0xE0,0x00,0x0F,0x03,0x0B,0x01,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00, - 0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x02,0x00,0x00, - 0xE7,0x0C,0x00,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00, - 0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x68,0x05,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2E,0x74,0x65,0x78,0x74,0x00,0x00,0x00, - 0x18,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x89,0xE5,0x81,0xEC,0x00,0x00,0x00,0x00,0x90,0xB8,0x00,0x00,0x00,0x00,0xE9, - 0x00,0x00,0x00,0x00,0xC9,0xC3,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, - 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x68,0x05,0x00,0x00,0x00,0x02,0x02,0x00, - /* Start of the signature overlay */ - 0x30,0x82,0x05,0x5A,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x07,0x02,0xA0,0x82,0x05,0x4B,0x30,0x82,0x05,0x47,0x02, - 0x01,0x01,0x31,0x0B,0x30,0x09,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x05,0x00,0x30,0x4C,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01, - 0x82,0x37,0x02,0x01,0x04,0xA0,0x3E,0x30,0x3C,0x30,0x17,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0F,0x30, - 0x09,0x03,0x01,0x00,0xA0,0x04,0xA2,0x02,0x80,0x00,0x30,0x21,0x30,0x09,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x05,0x00,0x04, - 0x14,0xA0,0x95,0xDE,0xBD,0x1A,0xB7,0x86,0xAF,0x50,0x63,0xD8,0x8F,0x90,0xD5,0x49,0x96,0x4E,0x44,0xF0,0x71,0xA0,0x82,0x03, - 0x1D,0x30,0x82,0x03,0x19,0x30,0x82,0x02,0x01,0xA0,0x03,0x02,0x01,0x02,0x02,0x10,0x96,0x53,0x2C,0xC9,0x23,0x56,0x8A,0x87, - 0x42,0x30,0x3E,0xD5,0x8D,0x72,0xD5,0x25,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x30, - 0x17,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x03,0x13,0x0C,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74, - 0x30,0x1E,0x17,0x0D,0x31,0x36,0x30,0x33,0x30,0x33,0x32,0x30,0x32,0x37,0x30,0x37,0x5A,0x17,0x0D,0x34,0x39,0x31,0x32,0x33, - 0x31,0x32,0x33,0x30,0x30,0x30,0x30,0x5A,0x30,0x17,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x03,0x13,0x0C,0x43,0x6F,0x64, - 0x65,0x53,0x69,0x67,0x6E,0x54,0x65,0x73,0x74,0x30,0x82,0x01,0x22,0x30,0x0D,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, - 0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0F,0x00,0x30,0x82,0x01,0x0A,0x02,0x82,0x01,0x01,0x00,0xB2,0xC9,0x91,0x98,0x8C,0xDC, - 0x80,0xBC,0x16,0xBF,0xC1,0x04,0x77,0x90,0xC0,0xFD,0x8C,0xBA,0x68,0x26,0xAC,0xB7,0x20,0x68,0x41,0xED,0xC3,0x9C,0x47,0x7C, - 0x36,0xC2,0x7B,0xE1,0x5E,0xFD,0xA9,0x99,0xF4,0x29,0x36,0x86,0x93,0x40,0x55,0x53,0x65,0x79,0xBC,0x9F,0x8F,0x6E,0x2B,0x05, - 0x84,0xE1,0xFD,0xD2,0xEF,0xEA,0x89,0x8C,0xEC,0xF9,0x55,0xF0,0x2C,0xE5,0xA7,0x29,0xF9,0x7E,0x50,0xDC,0x9C,0xA1,0x23,0xA5, - 0xD9,0x78,0xA1,0xE7,0x7C,0xD7,0x04,0x4F,0x11,0xAC,0x9F,0x4A,0x47,0xA1,0x1E,0xD5,0x9E,0xE7,0x5B,0xB5,0x8C,0x9C,0x67,0x7A, - 0xD0,0xF8,0x54,0xD1,0x64,0x7F,0x39,0x48,0xB6,0xCF,0x2F,0x26,0x7D,0x7B,0x13,0x2B,0xC2,0x8F,0xA6,0x3F,0x42,0x71,0x95,0x3E, - 0x59,0x0F,0x12,0xFA,0xC2,0x70,0x89,0xB7,0xB6,0x10,0x49,0xE0,0x7D,0x4D,0xFC,0x80,0x61,0x53,0x50,0x72,0xFD,0x46,0x35,0x51, - 0x36,0xE6,0x06,0xA9,0x4C,0x0D,0x82,0x15,0xF6,0x5D,0xDE,0xD4,0xDB,0xE7,0x82,0x10,0x40,0xA1,0x47,0x68,0x88,0x0C,0x0A,0x80, - 0xD1,0xE5,0x9A,0x35,0x28,0x82,0x1F,0x0F,0x80,0x5A,0x6E,0x1D,0x22,0x22,0xB3,0xA7,0xA2,0x9E,0x82,0x2D,0xC0,0x7F,0x5A,0xD0, - 0xBA,0xB2,0xCA,0x20,0xE2,0x97,0xE9,0x72,0x41,0xB7,0xD6,0x1A,0x93,0x23,0x97,0xF0,0xA9,0x61,0xD2,0x91,0xBD,0xB6,0x6B,0x95, - 0x12,0x67,0x16,0xAC,0x0A,0xB7,0x55,0x02,0x0D,0xA5,0xAD,0x17,0x95,0x77,0xF9,0x96,0x03,0x41,0xD3,0xE1,0x61,0x68,0xBB,0x0A, - 0xB5,0xC4,0xEE,0x70,0x40,0x08,0x05,0xC4,0xF1,0x5D,0x02,0x03,0x01,0x00,0x01,0xA3,0x61,0x30,0x5F,0x30,0x13,0x06,0x03,0x55, - 0x1D,0x25,0x04,0x0C,0x30,0x0A,0x06,0x08,0x2B,0x06,0x01,0x05,0x05,0x07,0x03,0x03,0x30,0x48,0x06,0x03,0x55,0x1D,0x01,0x04, - 0x41,0x30,0x3F,0x80,0x10,0x35,0x40,0x67,0x8F,0x7D,0x03,0x1B,0x76,0x52,0x62,0x2D,0xF5,0x21,0xF6,0x7C,0xBC,0xA1,0x19,0x30, - 0x17,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x03,0x13,0x0C,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74, - 0x82,0x10,0xA0,0x4B,0xEB,0xAC,0xFA,0x08,0xF2,0x8B,0x47,0xD2,0xB3,0x54,0x60,0x6C,0xE6,0x29,0x30,0x0D,0x06,0x09,0x2A,0x86, - 0x48,0x86,0xF7,0x0D,0x01,0x01,0x0B,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x5F,0x8C,0x7F,0xDA,0x1D,0x21,0x7A,0x15,0xD8,0x20, - 0x04,0x53,0x7F,0x44,0x6D,0x7B,0x57,0xBE,0x7F,0x86,0x77,0x58,0xC4,0xD4,0x80,0xC7,0x2E,0x64,0x9B,0x44,0xC5,0x2D,0x6D,0xDB, - 0x35,0x5A,0xFE,0xA4,0xD8,0x66,0x9B,0xF7,0x6E,0xFC,0xEF,0x52,0x7B,0xC5,0x16,0xE6,0xA3,0x7D,0x59,0xB7,0x31,0x28,0xEB,0xB5, - 0x45,0xC9,0xB1,0xD1,0x08,0x67,0xC6,0x37,0xE7,0xD7,0x2A,0xE6,0x1F,0xD9,0x6A,0xE5,0x04,0xDF,0x6A,0x9D,0x91,0xFA,0x41,0xBD, - 0x2A,0x50,0xEA,0x99,0x24,0xA9,0x0F,0x2B,0x50,0x51,0x5F,0xD9,0x0B,0x89,0x1B,0xCB,0xDB,0x88,0xE8,0xEC,0x87,0xB0,0x16,0xCC, - 0x43,0xEE,0x5A,0xBD,0x57,0xE2,0x46,0xA7,0x56,0x54,0x23,0x32,0x8A,0xFB,0x25,0x51,0x39,0x38,0xE6,0x87,0xF5,0x73,0x63,0xD0, - 0x5B,0xC7,0x3F,0xFD,0x04,0x75,0x74,0x4C,0x3D,0xB5,0x31,0x22,0x7D,0xF1,0x8D,0xB4,0xE0,0xAA,0xE1,0xFF,0x8F,0xDD,0xB8,0x04, - 0x6A,0x31,0xEE,0x30,0x2D,0x6E,0x74,0x0F,0x37,0x71,0x77,0x2B,0xB8,0x9E,0x62,0x47,0x00,0x9C,0xA5,0x82,0x2B,0x9F,0x24,0x67, - 0x50,0x86,0x8B,0xC9,0x36,0x81,0xEB,0x44,0xC2,0xF1,0x91,0xA6,0x84,0x75,0x15,0x8F,0x22,0xDE,0xAC,0xB5,0x16,0xE3,0x96,0x74, - 0x72,0x2F,0x15,0xD5,0xFB,0x01,0x22,0xC4,0x24,0xEE,0x3D,0xDF,0x9E,0xA9,0x0A,0x5B,0x16,0x21,0xE8,0x4A,0x8C,0x7E,0x3A,0x9C, - 0x22,0xA0,0x49,0x60,0x97,0x1B,0x3E,0x2D,0x80,0x91,0xDB,0xF7,0x78,0x38,0x76,0x78,0x0C,0xE3,0xD4,0x27,0x77,0x69,0x96,0xE6, - 0x41,0xC7,0x2E,0xE9,0x61,0xD6,0x31,0x82,0x01,0xC4,0x30,0x82,0x01,0xC0,0x02,0x01,0x01,0x30,0x2B,0x30,0x17,0x31,0x15,0x30, - 0x13,0x06,0x03,0x55,0x04,0x03,0x13,0x0C,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x20,0x52,0x6F,0x6F,0x74,0x02,0x10,0x96,0x53, - 0x2C,0xC9,0x23,0x56,0x8A,0x87,0x42,0x30,0x3E,0xD5,0x8D,0x72,0xD5,0x25,0x30,0x09,0x06,0x05,0x2B,0x0E,0x03,0x02,0x1A,0x05, - 0x00,0xA0,0x70,0x30,0x10,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0C,0x31,0x02,0x30,0x00,0x30,0x19,0x06, - 0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x03,0x31,0x0C,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x04, - 0x30,0x1C,0x06,0x0A,0x2B,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0B,0x31,0x0E,0x30,0x0C,0x06,0x0A,0x2B,0x06,0x01,0x04, - 0x01,0x82,0x37,0x02,0x01,0x15,0x30,0x23,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x04,0x31,0x16,0x04,0x14,0x3D, - 0x08,0xC8,0xA3,0xEE,0x05,0x1A,0x61,0xD9,0xFE,0x1A,0x63,0xC0,0x8A,0x6E,0x9D,0xF9,0xC3,0x13,0x98,0x30,0x0D,0x06,0x09,0x2A, - 0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,0x05,0x00,0x04,0x82,0x01,0x00,0x90,0xF9,0xC0,0x7F,0x1D,0x70,0x8C,0x04,0x22,0x82, - 0xB6,0x2D,0x48,0xBF,0x30,0x51,0x29,0xF8,0xE3,0x11,0x39,0xE0,0x64,0x23,0x72,0xE2,0x4C,0x09,0x9F,0x39,0xF2,0x6F,0xDD,0xB9, - 0x5A,0x3D,0xEF,0xEB,0xBE,0xEC,0x3B,0xE6,0x58,0x4C,0xC9,0x4F,0xED,0xCB,0x6E,0x9D,0x67,0x8E,0x89,0x92,0x40,0x39,0xA2,0x5F, - 0xF9,0xEF,0xD3,0xF5,0x24,0x27,0x8D,0xF7,0x3C,0x92,0x66,0x56,0xC8,0x2B,0xEA,0x04,0xA1,0x0E,0xDA,0x89,0x30,0xA7,0x01,0xD8, - 0x0B,0xF8,0xFD,0x99,0xB6,0xC0,0x38,0xB0,0x21,0x50,0x3A,0x86,0x01,0xD0,0xF3,0x86,0x72,0xE3,0x5A,0xBB,0x2A,0x6E,0xBD,0xFB, - 0x22,0xF9,0x42,0xD3,0x04,0xFE,0x8D,0xD8,0x79,0xD1,0xEE,0x61,0xC6,0x48,0x04,0x99,0x9A,0xA2,0x73,0xE5,0xFB,0x24,0x10,0xD5, - 0x6B,0x71,0x80,0x0E,0x09,0xEA,0x85,0x9A,0xBD,0xBB,0xDE,0x99,0x5D,0xA3,0x18,0x4D,0xED,0x20,0x73,0x3E,0x32,0xEF,0x2C,0xAC, - 0x5A,0x83,0x87,0x1F,0x7F,0x19,0x61,0x35,0x53,0xC1,0xAA,0x89,0x97,0xB3,0xDD,0x8D,0xA8,0x67,0x5B,0xC2,0xE2,0x09,0xB7,0xDD, - 0x6A,0xCB,0xD5,0xBF,0xD6,0x08,0xE2,0x23,0x1A,0x41,0x9D,0xD5,0x6A,0x6B,0x8D,0x3C,0x29,0x1B,0xF1,0x3F,0x4E,0x4A,0x8F,0x29, - 0x33,0xF9,0x1C,0x60,0xA0,0x92,0x7E,0x4F,0x35,0xB8,0xDD,0xEB,0xD1,0x68,0x1A,0x9D,0xA2,0xA6,0x97,0x1F,0x5F,0xC6,0x2C,0xFB, - 0xCA,0xDF,0xF7,0x95,0x33,0x95,0xD4,0x79,0x5C,0x73,0x87,0x49,0x1F,0x8C,0x6E,0xCE,0x3E,0x6D,0x3D,0x2B,0x6B,0xD7,0x66,0xE9, - 0x88,0x6F,0xF2,0x83,0xB9,0x9B,0x00,0x00 -}; - static void test_query_object(void) { - WCHAR tmp_path[MAX_PATH]; BOOL ret; CRYPT_DATA_BLOB blob; - DWORD content_type; /* Test the usual invalid arguments */ SetLastError(0xdeadbeef); @@ -308,26 +188,6 @@ static void test_query_object(void) CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED, 0, NULL, NULL, NULL, NULL, NULL, NULL); ok(ret, "CryptQueryObject failed: %08lx\n", GetLastError()); - - GetEnvironmentVariableW( L"TMP", tmp_path, MAX_PATH ); - SetEnvironmentVariableW(L"TMP", L"C:\\nonexistent"); - blob.pbData = (BYTE *)signed_pe_blob; - blob.cbData = sizeof(signed_pe_blob); - ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, - CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &content_type, - NULL, NULL, NULL, NULL); - ok(!ret, "CryptQueryObject succeeded\n"); - ok(GetLastError() == CRYPT_E_NO_MATCH, "Unexpected error %lu.\n", GetLastError()); - SetEnvironmentVariableW(L"TMP", tmp_path); - - blob.pbData = (BYTE *)signed_pe_blob; - blob.cbData = sizeof(signed_pe_blob); - ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob, - CERT_QUERY_CONTENT_FLAG_ALL, CERT_QUERY_FORMAT_FLAG_ALL, 0, NULL, &content_type, - NULL, NULL, NULL, NULL); - ok(ret, "CryptQueryObject failed: %08lx\n", GetLastError()); - ok(content_type == CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED, - "Got unexpected content_type %#lx.\n", content_type); } START_TEST(object) diff --git a/dlls/crypt32/tests/oid.c b/dlls/crypt32/tests/oid.c index 9520e8c5a4a..715d76b9c31 100644 --- wine/dlls/crypt32/tests/oid.c +++ wine/dlls/crypt32/tests/oid.c @@ -152,14 +152,14 @@ static void test_oidFunctionSet(void) ok(ret, "CryptGetDefaultOIDDllList failed: %08lx\n", GetLastError()); if (ret) { - buf = HeapAlloc(GetProcessHeap(), 0, size * sizeof(WCHAR)); + buf = malloc(size * sizeof(WCHAR)); if (buf) { ret = CryptGetDefaultOIDDllList(set1, 0, buf, &size); ok(ret, "CryptGetDefaultOIDDllList failed: %08lx\n", GetLastError()); ok(!*buf, "Expected empty DLL list\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } } } diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c index 00c2cae19e0..20d10a110f9 100644 --- wine/dlls/crypt32/tests/store.c +++ wine/dlls/crypt32/tests/store.c @@ -216,7 +216,7 @@ static void testMemStore(void) ret = CertSerializeCertificateStoreElement(context, 1, NULL, &size); ok(ret, "CertSerializeCertificateStoreElement failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { ret = CertSerializeCertificateStoreElement(context, 0, buf, &size); @@ -224,7 +224,7 @@ static void testMemStore(void) ok(size == sizeof(serializedCert), "Wrong size %ld\n", size); ok(!memcmp(serializedCert, buf, size), "Unexpected serialized cert\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } ret = CertFreeCertificateContext(context); @@ -311,7 +311,7 @@ static void compareStore(HCERTSTORE store, LPCSTR name, const BYTE *pb, todo_wine_if (todo) ok(blob.cbData == cb, "%s: expected size %ld, got %ld\n", name, cb, blob.cbData); - blob.pbData = HeapAlloc(GetProcessHeap(), 0, blob.cbData); + blob.pbData = malloc(blob.cbData); if (blob.pbData) { ret = CertSaveStore(store, X509_ASN_ENCODING, CERT_STORE_SAVE_AS_STORE, @@ -319,7 +319,7 @@ static void compareStore(HCERTSTORE store, LPCSTR name, const BYTE *pb, ok(ret, "CertSaveStore failed: %08lx\n", GetLastError()); todo_wine_if (todo) ok(!memcmp(pb, blob.pbData, cb), "%s: unexpected value\n", name); - HeapFree(GetProcessHeap(), 0, blob.pbData); + free(blob.pbData); } } @@ -1063,7 +1063,7 @@ static void testRegStore(void) size = 0; RegQueryValueExA(subKey, "Blob", NULL, NULL, NULL, &size); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { rc = RegQueryValueExA(subKey, "Blob", NULL, NULL, buf, &size); @@ -1092,7 +1092,7 @@ static void testRegStore(void) hdr->cb), "Unexpected hash in cert property\n"); } } - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } RegCloseKey(subKey); } @@ -2465,7 +2465,7 @@ static void testAddCertificateLink(void) ret = CertSerializeCertificateStoreElement(linked, 0, NULL, &size); ok(ret, "CertSerializeCertificateStoreElement failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { ret = CertSerializeCertificateStoreElement(linked, 0, buf, &size); @@ -2477,7 +2477,7 @@ static void testAddCertificateLink(void) ok(size == sizeof(serializedCert), "Wrong size %ld\n", size); ok(!memcmp(serializedCert, buf, size), "Unexpected serialized cert\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } /* Set a friendly name on the source certificate... */ blob.pbData = (LPBYTE)L"WineTest"; @@ -2491,7 +2491,7 @@ static void testAddCertificateLink(void) CERT_FRIENDLY_NAME_PROP_ID, NULL, &size); ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { ret = CertGetCertificateContextProperty(linked, @@ -2500,7 +2500,7 @@ static void testAddCertificateLink(void) GetLastError()); ok(!lstrcmpW((LPCWSTR)buf, L"WineTest"), "unexpected friendly name\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } CertFreeCertificateContext(linked); } @@ -2541,7 +2541,7 @@ static void testAddCertificateLink(void) ret = CertSerializeCertificateStoreElement(linked, 0, NULL, &size); ok(ret, "CertSerializeCertificateStoreElement failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { ret = CertSerializeCertificateStoreElement(linked, 0, buf, &size); @@ -2552,7 +2552,7 @@ static void testAddCertificateLink(void) ok(size == sizeof(serializedCert), "Wrong size %ld\n", size); ok(!memcmp(serializedCert, buf, size), "Unexpected serialized cert\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } /* Set a friendly name on the source certificate... */ blob.pbData = (LPBYTE)L"WineTest"; @@ -2566,7 +2566,7 @@ static void testAddCertificateLink(void) CERT_FRIENDLY_NAME_PROP_ID, NULL, &size); ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { ret = CertGetCertificateContextProperty(linked, @@ -2574,7 +2574,7 @@ static void testAddCertificateLink(void) ok(ret, "CertGetCertificateContextProperty failed: %08lx\n", GetLastError()); ok(!lstrcmpW((LPCWSTR)buf, L"WineTest"), "unexpected friendly name\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } CertFreeCertificateContext(linked); } @@ -2603,7 +2603,7 @@ static void testAddCertificateLink(void) ret = CertSerializeCertificateStoreElement(linked, 0, NULL, &size); ok(ret, "CertSerializeCertificateStoreElement failed: %08lx\n", GetLastError()); - buf = HeapAlloc(GetProcessHeap(), 0, size); + buf = malloc(size); if (buf) { ret = CertSerializeCertificateStoreElement(linked, 0, buf, &size); @@ -2616,7 +2616,7 @@ static void testAddCertificateLink(void) "Wrong size %ld\n", size); ok(!memcmp(serializedCertWithFriendlyName, buf, size), "Unexpected serialized cert\n"); - HeapFree(GetProcessHeap(), 0, buf); + free(buf); } CertFreeCertificateContext(linked); compareStore(store2, "file store -> file store", diff --git a/dlls/crypt32/tests/str.c b/dlls/crypt32/tests/str.c index a94381591c0..5fb05bdb836 100644 --- wine/dlls/crypt32/tests/str.c +++ wine/dlls/crypt32/tests/str.c @@ -31,7 +31,6 @@ typedef struct _CertRDNAttrEncoding { DWORD dwValueType; CERT_RDN_VALUE_BLOB Value; LPCSTR str; - BOOL todo; } CertRDNAttrEncoding, *PCertRDNAttrEncoding; typedef struct _CertRDNAttrEncodingW { @@ -39,7 +38,6 @@ typedef struct _CertRDNAttrEncodingW { DWORD dwValueType; CERT_RDN_VALUE_BLOB Value; LPCWSTR str; - BOOL todo; } CertRDNAttrEncodingW, *PCertRDNAttrEncodingW; static BYTE bin1[] = { 0x55, 0x53 }; @@ -115,6 +113,103 @@ static const BYTE cert[] = 0x65,0xd3,0xce,0xae,0x26,0x19,0x3,0x2e,0x4f,0x78,0xa5,0xa,0x97,0x7e,0x4f,0xc4, 0x91,0x8a,0xf8,0x5,0xef,0x5b,0x3b,0x49,0xbf,0x5f,0x2b}; +/* +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 5d:79:35:fd:d3:8f:6b:e2:28:3e:94:f4:14:bf:d4:b5:c2:3a:ac:38 + Signature Algorithm: md5WithRSAEncryption + Issuer: C = US, ST = Minnesota, L = Minneapolis, O = CodeWeavers, CN = server_cn.org, emailAddress = test@codeweavers.com + Validity + Not Before: Apr 14 18:56:22 2022 GMT + Not After : Apr 11 18:56:22 2032 GMT + Subject: C = US, ST = Minnesota, L = Minneapolis, O = CodeWeavers, CN = server_cn.org, emailAddress = test@codeweavers.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + RSA Public-Key: (1024 bit) + Modulus: +... + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Alternative Name: + DNS:ex1.org, DNS:*.ex2.org + X509v3 Issuer Alternative Name: + DNS:ex3.org, DNS:*.ex4.org + Signature Algorithm: md5WithRSAEncryption +... +*/ +static BYTE cert_v3[] = { + 0x30, 0x82, 0x02, 0xdf, 0x30, 0x82, 0x02, 0x48, 0xa0, 0x03, 0x02, 0x01, + 0x02, 0x02, 0x14, 0x5d, 0x79, 0x35, 0xfd, 0xd3, 0x8f, 0x6b, 0xe2, 0x28, + 0x3e, 0x94, 0xf4, 0x14, 0xbf, 0xd4, 0xb5, 0xc2, 0x3a, 0xac, 0x38, 0x30, + 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04, + 0x05, 0x00, 0x30, 0x81, 0x8a, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, + 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, + 0x55, 0x04, 0x08, 0x0c, 0x09, 0x4d, 0x69, 0x6e, 0x6e, 0x65, 0x73, 0x6f, + 0x74, 0x61, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x0c, + 0x0b, 0x4d, 0x69, 0x6e, 0x6e, 0x65, 0x61, 0x70, 0x6f, 0x6c, 0x69, 0x73, + 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x0b, 0x43, + 0x6f, 0x64, 0x65, 0x57, 0x65, 0x61, 0x76, 0x65, 0x72, 0x73, 0x31, 0x16, + 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, 0x0c, 0x0d, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x72, 0x5f, 0x63, 0x6e, 0x2e, 0x6f, 0x72, 0x67, 0x31, 0x23, + 0x30, 0x21, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, + 0x01, 0x16, 0x14, 0x74, 0x65, 0x73, 0x74, 0x40, 0x63, 0x6f, 0x64, 0x65, + 0x77, 0x65, 0x61, 0x76, 0x65, 0x72, 0x73, 0x2e, 0x63, 0x6f, 0x6d, 0x30, + 0x1e, 0x17, 0x0d, 0x32, 0x32, 0x30, 0x34, 0x31, 0x34, 0x31, 0x38, 0x35, + 0x36, 0x32, 0x32, 0x5a, 0x17, 0x0d, 0x33, 0x32, 0x30, 0x34, 0x31, 0x31, + 0x31, 0x38, 0x35, 0x36, 0x32, 0x32, 0x5a, 0x30, 0x81, 0x8a, 0x31, 0x0b, + 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, + 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x09, 0x4d, 0x69, + 0x6e, 0x6e, 0x65, 0x73, 0x6f, 0x74, 0x61, 0x31, 0x14, 0x30, 0x12, 0x06, + 0x03, 0x55, 0x04, 0x07, 0x0c, 0x0b, 0x4d, 0x69, 0x6e, 0x6e, 0x65, 0x61, + 0x70, 0x6f, 0x6c, 0x69, 0x73, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, + 0x04, 0x0a, 0x0c, 0x0b, 0x43, 0x6f, 0x64, 0x65, 0x57, 0x65, 0x61, 0x76, + 0x65, 0x72, 0x73, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x03, + 0x0c, 0x0d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x63, 0x6e, 0x2e, + 0x6f, 0x72, 0x67, 0x31, 0x23, 0x30, 0x21, 0x06, 0x09, 0x2a, 0x86, 0x48, + 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01, 0x16, 0x14, 0x74, 0x65, 0x73, 0x74, + 0x40, 0x63, 0x6f, 0x64, 0x65, 0x77, 0x65, 0x61, 0x76, 0x65, 0x72, 0x73, + 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, + 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xcd, 0x7c, 0x05, + 0xba, 0xad, 0xd0, 0xb0, 0x43, 0xcc, 0x47, 0x7d, 0x87, 0xaa, 0xb5, 0x89, + 0x9f, 0x43, 0x94, 0xa0, 0x84, 0xc0, 0xc0, 0x5e, 0x05, 0x6d, 0x2f, 0x05, + 0x21, 0x6b, 0x20, 0x39, 0x88, 0x06, 0x4e, 0xce, 0x76, 0xa7, 0x24, 0x77, + 0x13, 0x71, 0x9b, 0x2a, 0x53, 0x04, 0x4f, 0x0f, 0xfc, 0x3f, 0x4f, 0xb1, + 0x4e, 0xdc, 0xed, 0x96, 0xd4, 0x55, 0xbd, 0xcf, 0x25, 0xa6, 0x7c, 0xe3, + 0x35, 0xbf, 0xeb, 0x30, 0xec, 0xef, 0x7f, 0x8e, 0xa1, 0xc6, 0xd3, 0xb2, + 0x03, 0x62, 0x0a, 0x92, 0x87, 0x17, 0x52, 0x2d, 0x45, 0x2a, 0xdc, 0xdb, + 0x87, 0xa5, 0x32, 0x4a, 0x78, 0x28, 0x4a, 0x51, 0xff, 0xdb, 0xd5, 0x20, + 0x47, 0x7e, 0xc5, 0xbe, 0x1d, 0x01, 0x55, 0x13, 0x9f, 0xfb, 0x8e, 0x39, + 0xd9, 0x1b, 0xe0, 0x34, 0x93, 0x43, 0x9c, 0x02, 0xa3, 0x0f, 0xb5, 0xdc, + 0x9d, 0x86, 0x45, 0xc5, 0x4d, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x40, + 0x30, 0x3e, 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x11, /* Subject Alternative Name OID */ + 0x04, 0x16, 0x30, 0x14, 0x82, 0x07, 0x65, 0x78, 0x31, 0x2e, 0x6f, 0x72, + 0x67, 0x82, 0x09, 0x2a, 0x2e, 0x65, 0x78, 0x32, 0x2e, 0x6f, 0x72, 0x67, + 0x30, 0x1d, 0x06, 0x03, + 0x55, 0x1d, 0x12, /* Issuer Alternative Name OID */ + 0x04, 0x16, 0x30, 0x14, 0x82, 0x07, 0x65, 0x78, 0x33, 0x2e, 0x6f, 0x72, + 0x67, 0x82, 0x09, 0x2a, 0x2e, 0x65, 0x78, 0x34, 0x2e, 0x6f, 0x72, 0x67, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x04, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0xcc, 0xa3, 0x75, 0x67, 0x61, + 0x63, 0x1d, 0x99, 0x16, 0xc6, 0x93, 0x35, 0xa4, 0x31, 0xb6, 0x05, 0x05, + 0x77, 0x12, 0x15, 0x16, 0x78, 0xb3, 0xba, 0x6e, 0xde, 0xfc, 0x73, 0x7c, + 0x5c, 0xdd, 0xdf, 0x92, 0xde, 0xa0, 0x86, 0xff, 0x77, 0x60, 0x99, 0x8f, + 0x4a, 0x40, 0xa8, 0x6a, 0xdb, 0x6f, 0x30, 0xe5, 0xce, 0x82, 0x2f, 0xf7, + 0x09, 0x17, 0xb2, 0xd3, 0x3a, 0x29, 0x9a, 0xd0, 0x73, 0x9c, 0x44, 0xa2, + 0x19, 0xf3, 0x1d, 0x16, 0x1a, 0x45, 0x2c, 0x4b, 0x94, 0xf1, 0xb8, 0xb6, + 0xc9, 0x82, 0x6c, 0x1f, 0xae, 0xbc, 0xd1, 0xbe, 0x78, 0xc9, 0x23, 0xf5, + 0x51, 0x6c, 0x90, 0xbf, 0xa3, 0x5c, 0xa1, 0x3a, 0xd8, 0xe3, 0xcf, 0x82, + 0x31, 0x78, 0x2b, 0xda, 0x99, 0xff, 0x23, 0x5b, 0xea, 0x59, 0xe0, 0x6d, + 0xd1, 0x30, 0xfd, 0x96, 0x6a, 0x4d, 0x36, 0x72, 0x96, 0xd7, 0x4f, 0x01, + 0xa9, 0x4d, 0x8f +}; + +#define CERT_V3_SAN_OID_OFFSET 534 +#define CERT_V3_IAN_OID_OFFSET 565 + static char issuerStr[] = "US, Minnesota, Minneapolis, CodeWeavers, Wine Development, localhost, aric@codeweavers.com"; static char issuerStrSemicolon[] = @@ -134,33 +229,34 @@ static void test_CertRDNValueToStrA(void) { CertRDNAttrEncoding attrs[] = { { "2.5.4.6", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin1), bin1 }, "US", FALSE }, + { sizeof(bin1), bin1 }, "US" }, { "2.5.4.8", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin2), bin2 }, "Minnesota", FALSE }, + { sizeof(bin2), bin2 }, "Minnesota" }, { "2.5.4.7", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin3), bin3 }, "Minneapolis", FALSE }, + { sizeof(bin3), bin3 }, "Minneapolis" }, { "2.5.4.10", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin4), bin4 }, "CodeWeavers", FALSE }, + { sizeof(bin4), bin4 }, "CodeWeavers" }, { "2.5.4.11", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin5), bin5 }, "Wine Development", FALSE }, + { sizeof(bin5), bin5 }, "Wine Development" }, { "2.5.4.3", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin6), bin6 }, "localhost", FALSE }, + { sizeof(bin6), bin6 }, "localhost" }, { "1.2.840.113549.1.9.1", CERT_RDN_IA5_STRING, - { sizeof(bin7), bin7 }, "aric@codeweavers.com", FALSE }, + { sizeof(bin7), bin7 }, "aric@codeweavers.com" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin9), bin9 }, "abc\"def", FALSE }, + { sizeof(bin9), bin9 }, "abc\"def" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin10), bin10 }, "abc'def", FALSE }, + { sizeof(bin10), bin10 }, "abc'def" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin11), bin11 }, "abc, def", FALSE }, + { sizeof(bin11), bin11 }, "abc, def" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin12), bin12 }, " abc ", FALSE }, + { sizeof(bin12), bin12 }, " abc " }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin13), bin13 }, "\"def\"", FALSE }, + { sizeof(bin13), bin13 }, "\"def\"" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin14), bin14 }, "1;3", FALSE }, + { sizeof(bin14), bin14 }, "1;3" }, }; - DWORD i, ret; + unsigned int i; + DWORD ret, len; char buffer[2000]; CERT_RDN_VALUE_BLOB blob = { 0, NULL }; static const char ePKI[] = "ePKI Root Certification Authority"; @@ -178,15 +274,21 @@ static void test_CertRDNValueToStrA(void) for (i = 0; i < ARRAY_SIZE(attrs); i++) { - ret = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value, + len = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value, buffer, sizeof(buffer)); - todo_wine_if (attrs[i].todo) - { - ok(ret == strlen(attrs[i].str) + 1, "Expected length %d, got %ld\n", - lstrlenA(attrs[i].str) + 1, ret); - ok(!strcmp(buffer, attrs[i].str), "Expected %s, got %s\n", - attrs[i].str, buffer); - } + ok(len == strlen(attrs[i].str) + 1, "Expected length %d, got %ld\n", + lstrlenA(attrs[i].str) + 1, ret); + ok(!strcmp(buffer, attrs[i].str), "Expected %s, got %s\n", + attrs[i].str, buffer); + memset(buffer, 0xcc, sizeof(buffer)); + ret = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value, buffer, len - 1); + ok(ret == 1, "Unexpected ret %lu, expected 1, test %u.\n", ret, i); + ok(!buffer[0], "Unexpected value %#x, test %u.\n", buffer[0], i); + ok(!strncmp(buffer + 1, attrs[i].str + 1, len - 2), "Strings do not match, test %u.\n", i); + memset(buffer, 0xcc, sizeof(buffer)); + ret = CertRDNValueToStrA(attrs[i].dwValueType, &attrs[i].Value, buffer, 0); + ok(ret == len, "Unexpected ret %lu, expected %lu, test %u.\n", ret, len, i); + ok((unsigned char)buffer[0] == 0xcc, "Unexpected value %#x, test %u.\n", buffer[0], i); } blob.pbData = bin8; blob.cbData = sizeof(bin8); @@ -202,33 +304,34 @@ static void test_CertRDNValueToStrW(void) static const WCHAR ePKIW[] = L"ePKI Root Certification Authority"; CertRDNAttrEncodingW attrs[] = { { "2.5.4.6", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin1), bin1 }, L"US", FALSE }, + { sizeof(bin1), bin1 }, L"US" }, { "2.5.4.8", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin2), bin2 }, L"Minnesota", FALSE }, + { sizeof(bin2), bin2 }, L"Minnesota" }, { "2.5.4.7", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin3), bin3 }, L"Minneapolis", FALSE }, + { sizeof(bin3), bin3 }, L"Minneapolis" }, { "2.5.4.10", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin4), bin4 }, L"CodeWeavers", FALSE }, + { sizeof(bin4), bin4 }, L"CodeWeavers" }, { "2.5.4.11", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin5), bin5 }, L"Wine Development", FALSE }, + { sizeof(bin5), bin5 }, L"Wine Development" }, { "2.5.4.3", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin6), bin6 }, L"localhost", FALSE }, + { sizeof(bin6), bin6 }, L"localhost" }, { "1.2.840.113549.1.9.1", CERT_RDN_IA5_STRING, - { sizeof(bin7), bin7 }, L"aric@codeweavers.com", FALSE }, + { sizeof(bin7), bin7 }, L"aric@codeweavers.com" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin9), bin9 }, L"abc\"def", FALSE }, + { sizeof(bin9), bin9 }, L"abc\"def" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin10), bin10 }, L"abc'def", FALSE }, + { sizeof(bin10), bin10 }, L"abc'def" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin11), bin11 }, L"abc, def", FALSE }, + { sizeof(bin11), bin11 }, L"abc, def" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin12), bin12 }, L" abc ", FALSE }, + { sizeof(bin12), bin12 }, L" abc " }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin13), bin13 }, L"\"def\"", FALSE }, + { sizeof(bin13), bin13 }, L"\"def\"" }, { "0", CERT_RDN_PRINTABLE_STRING, - { sizeof(bin14), bin14 }, L"1;3", FALSE }, + { sizeof(bin14), bin14 }, L"1;3" }, }; - DWORD i, ret; + unsigned int i; + DWORD ret, len; WCHAR buffer[2000]; CERT_RDN_VALUE_BLOB blob = { 0, NULL }; @@ -245,14 +348,20 @@ static void test_CertRDNValueToStrW(void) for (i = 0; i < ARRAY_SIZE(attrs); i++) { - ret = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, ARRAY_SIZE(buffer)); - todo_wine_if (attrs[i].todo) - { - ok(ret == lstrlenW(attrs[i].str) + 1, - "Expected length %d, got %ld\n", lstrlenW(attrs[i].str) + 1, ret); - ok(!lstrcmpW(buffer, attrs[i].str), "Expected %s, got %s\n", - wine_dbgstr_w(attrs[i].str), wine_dbgstr_w(buffer)); - } + len = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, ARRAY_SIZE(buffer)); + ok(len == lstrlenW(attrs[i].str) + 1, + "Expected length %d, got %ld\n", lstrlenW(attrs[i].str) + 1, ret); + ok(!lstrcmpW(buffer, attrs[i].str), "Expected %s, got %s\n", + wine_dbgstr_w(attrs[i].str), wine_dbgstr_w(buffer)); + memset(buffer, 0xcc, sizeof(buffer)); + ret = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, len - 1); + ok(ret == 1, "Unexpected ret %lu, expected 1, test %u.\n", ret, i); + ok(!buffer[0], "Unexpected value %#x, test %u.\n", buffer[0], i); + ok(buffer[1] == 0xcccc, "Unexpected value %#x, test %u.\n", buffer[1], i); + memset(buffer, 0xcc, sizeof(buffer)); + ret = CertRDNValueToStrW(attrs[i].dwValueType, &attrs[i].Value, buffer, 0); + ok(ret == len, "Unexpected ret %lu, expected %lu, test %u.\n", ret, len, i); + ok(buffer[0] == 0xcccc, "Unexpected value %#x, test %u.\n", buffer[0], i); } blob.pbData = bin8; blob.cbData = sizeof(bin8); @@ -264,24 +373,27 @@ static void test_CertRDNValueToStrW(void) wine_dbgstr_w(ePKIW), wine_dbgstr_w(buffer)); } -static void test_NameToStrConversionA(PCERT_NAME_BLOB pName, DWORD dwStrType, - LPCSTR expected, BOOL todo) +#define test_NameToStrConversionA(a, b, c) test_NameToStrConversionA_(__LINE__, a, b, c) +static void test_NameToStrConversionA_(unsigned int line, PCERT_NAME_BLOB pName, DWORD dwStrType, LPCSTR expected) { - char buffer[2000] = { 0 }; - DWORD i; - - i = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, NULL, 0); - todo_wine_if (todo) - ok(i == strlen(expected) + 1, "Expected %d chars, got %ld\n", - lstrlenA(expected) + 1, i); - i = CertNameToStrA(X509_ASN_ENCODING,pName, dwStrType, buffer, - sizeof(buffer)); - todo_wine_if (todo) - ok(i == strlen(expected) + 1, "Expected %d chars, got %ld\n", - lstrlenA(expected) + 1, i); - todo_wine_if (todo) - ok(!strcmp(buffer, expected), "Expected %s, got %s\n", expected, - buffer); + char buffer[2000]; + DWORD len, retlen; + + len = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, NULL, 0); + ok(len == strlen(expected) + 1, "line %u: Expected %d chars, got %ld.\n", line, lstrlenA(expected) + 1, len); + len = CertNameToStrA(X509_ASN_ENCODING,pName, dwStrType, buffer, sizeof(buffer)); + ok(len == strlen(expected) + 1, "line %u: Expected %d chars, got %ld.\n", line, lstrlenA(expected) + 1, len); + ok(!strcmp(buffer, expected), "line %u: Expected %s, got %s.\n", line, expected, buffer); + + memset(buffer, 0xcc, sizeof(buffer)); + retlen = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, buffer, len - 1); + ok(retlen == 1, "line %u: expected 1, got %lu\n", line, retlen); + ok(!buffer[0], "line %u: string is not zero terminated.\n", line); + + memset(buffer, 0xcc, sizeof(buffer)); + retlen = CertNameToStrA(X509_ASN_ENCODING, pName, dwStrType, buffer, 0); + ok(retlen == len, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen); + ok((unsigned char)buffer[0] == 0xcc, "line %u: got %s\n", line, wine_dbgstr_a(buffer)); } static BYTE encodedSimpleCN[] = { @@ -354,98 +466,98 @@ static void test_CertNameToStrA(void) "Expected positive return and ERROR_SUCCESS, got %ld - %08lx\n", ret, GetLastError()); + test_NameToStrConversionA(&context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, issuerStr); test_NameToStrConversionA(&context->pCertInfo->Issuer, - CERT_SIMPLE_NAME_STR, issuerStr, FALSE); - test_NameToStrConversionA(&context->pCertInfo->Issuer, - CERT_SIMPLE_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, - issuerStrSemicolon, FALSE); + CERT_SIMPLE_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, issuerStrSemicolon); test_NameToStrConversionA(&context->pCertInfo->Issuer, - CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG, - issuerStrCRLF, FALSE); - test_NameToStrConversionA(&context->pCertInfo->Subject, - CERT_OID_NAME_STR, subjectStr, FALSE); + CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG, issuerStrCRLF); + test_NameToStrConversionA(&context->pCertInfo->Subject, CERT_OID_NAME_STR, subjectStr); test_NameToStrConversionA(&context->pCertInfo->Subject, - CERT_OID_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, - subjectStrSemicolon, FALSE); + CERT_OID_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, subjectStrSemicolon); test_NameToStrConversionA(&context->pCertInfo->Subject, - CERT_OID_NAME_STR | CERT_NAME_STR_CRLF_FLAG, - subjectStrCRLF, FALSE); + CERT_OID_NAME_STR | CERT_NAME_STR_CRLF_FLAG, subjectStrCRLF); test_NameToStrConversionA(&context->pCertInfo->Subject, - CERT_X500_NAME_STR, x500SubjectStr, FALSE); + CERT_X500_NAME_STR, x500SubjectStr); test_NameToStrConversionA(&context->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG | CERT_NAME_STR_REVERSE_FLAG, - x500SubjectStrSemicolonReverse, FALSE); + x500SubjectStrSemicolonReverse); CertFreeCertificateContext(context); } blob.pbData = encodedSimpleCN; blob.cbData = sizeof(encodedSimpleCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=1", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=1"); blob.pbData = encodedSingleQuotedCN; blob.cbData = sizeof(encodedSingleQuotedCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN='1'", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "'1'", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN='1'"); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "'1'"); blob.pbData = encodedSpacedCN; blob.cbData = sizeof(encodedSpacedCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\" 1 \"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\" 1 \"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\" 1 \""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\" 1 \""); blob.pbData = encodedQuotedCN; blob.cbData = sizeof(encodedQuotedCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"\"\"1\"\"\"", - FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"\"\"1\"\"\"", - FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"\"\"1\"\"\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"\"\"1\"\"\""); blob.pbData = encodedMultipleAttrCN; blob.cbData = sizeof(encodedMultipleAttrCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"1+2\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"1+2\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"1+2\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"1+2\""); blob.pbData = encodedCommaCN; blob.cbData = sizeof(encodedCommaCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a,b\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a,b\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a,b\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a,b\""); blob.pbData = encodedEqualCN; blob.cbData = sizeof(encodedEqualCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a=b\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a=b\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a=b\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a=b\""); blob.pbData = encodedLessThanCN; blob.cbData = sizeof(encodedLessThanCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"<\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"<\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"<\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"<\""); blob.pbData = encodedGreaterThanCN; blob.cbData = sizeof(encodedGreaterThanCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\">\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\">\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\">\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\">\""); blob.pbData = encodedHashCN; blob.cbData = sizeof(encodedHashCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"#\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"#\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"#\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"#\""); blob.pbData = encodedSemiCN; blob.cbData = sizeof(encodedSemiCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\";\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\";\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\";\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\";\""); blob.pbData = encodedNewlineCN; blob.cbData = sizeof(encodedNewlineCN); - test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a\nb\"", FALSE); - test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a\nb\"", FALSE); + test_NameToStrConversionA(&blob, CERT_X500_NAME_STR, "CN=\"a\nb\""); + test_NameToStrConversionA(&blob, CERT_SIMPLE_NAME_STR, "\"a\nb\""); } -static void test_NameToStrConversionW(PCERT_NAME_BLOB pName, DWORD dwStrType, - LPCWSTR expected, BOOL todo) +#define test_NameToStrConversionW(a, b, c) test_NameToStrConversionW_(__LINE__, a, b, c) +static void test_NameToStrConversionW_(unsigned int line, PCERT_NAME_BLOB pName, DWORD dwStrType, LPCWSTR expected) { - WCHAR buffer[2000] = { 0 }; - DWORD i; - - i = CertNameToStrW(X509_ASN_ENCODING,pName, dwStrType, NULL, 0); - todo_wine_if (todo) - ok(i == lstrlenW(expected) + 1, "Expected %d chars, got %ld\n", - lstrlenW(expected) + 1, i); - i = CertNameToStrW(X509_ASN_ENCODING,pName, dwStrType, buffer, ARRAY_SIZE(buffer)); - todo_wine_if (todo) - ok(i == lstrlenW(expected) + 1, "Expected %d chars, got %ld\n", - lstrlenW(expected) + 1, i); - todo_wine_if (todo) - ok(!lstrcmpW(buffer, expected), "Expected %s, got %s\n", - wine_dbgstr_w(expected), wine_dbgstr_w(buffer)); + DWORD len, retlen, expected_len; + WCHAR buffer[2000]; + + expected_len = wcslen(expected) + 1; + memset(buffer, 0xcc, sizeof(buffer)); + len = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, NULL, 0); + ok(len == expected_len, "line %u: expected %lu chars, got %lu\n", line, expected_len, len); + retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, ARRAY_SIZE(buffer)); + ok(retlen == len, "line %u: expected %lu chars, got %lu.\n", line, len, retlen); + ok(!wcscmp(buffer, expected), "Expected %s, got %s\n", wine_dbgstr_w(expected), wine_dbgstr_w(buffer)); + + memset(buffer, 0xcc, sizeof(buffer)); + retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, len - 1); + ok(retlen == len - 1, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen); + ok(!wcsncmp(buffer, expected, retlen - 1), "line %u: expected %s, got %s\n", + line, wine_dbgstr_w(expected), wine_dbgstr_w(buffer)); + ok(!buffer[retlen - 1], "line %u: string is not zero terminated.\n", line); + + memset(buffer, 0xcc, sizeof(buffer)); + retlen = CertNameToStrW(X509_ASN_ENCODING, pName, dwStrType, buffer, 0); + ok(retlen == len, "line %u: expected %lu chars, got %lu\n", line, len - 1, retlen); + ok(buffer[0] == 0xcccc, "line %u: got %s\n", line, wine_dbgstr_w(buffer)); } static void test_CertNameToStrW(void) @@ -479,95 +591,79 @@ static void test_CertNameToStrW(void) test_NameToStrConversionW(&context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR, - L"US, Minnesota, Minneapolis, CodeWeavers, Wine Development, localhost, aric@codeweavers.com", FALSE); + L"US, Minnesota, Minneapolis, CodeWeavers, Wine Development, localhost, aric@codeweavers.com"); test_NameToStrConversionW(&context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, - L"US; Minnesota; Minneapolis; CodeWeavers; Wine Development; localhost; aric@codeweavers.com", FALSE); + L"US; Minnesota; Minneapolis; CodeWeavers; Wine Development; localhost; aric@codeweavers.com"); test_NameToStrConversionW(&context->pCertInfo->Issuer, CERT_SIMPLE_NAME_STR | CERT_NAME_STR_CRLF_FLAG, - L"US\r\nMinnesota\r\nMinneapolis\r\nCodeWeavers\r\nWine Development\r\nlocalhost\r\naric@codeweavers.com", - FALSE); + L"US\r\nMinnesota\r\nMinneapolis\r\nCodeWeavers\r\nWine Development\r\nlocalhost\r\naric@codeweavers.com"); test_NameToStrConversionW(&context->pCertInfo->Subject, CERT_OID_NAME_STR, L"2.5.4.6=US, 2.5.4.8=Minnesota, 2.5.4.7=Minneapolis, 2.5.4.10=CodeWeavers, 2.5.4.11=Wine Development," - " 2.5.4.3=localhost, 1.2.840.113549.1.9.1=aric@codeweavers.com", FALSE); + " 2.5.4.3=localhost, 1.2.840.113549.1.9.1=aric@codeweavers.com"); test_NameToStrConversionW(&context->pCertInfo->Subject, CERT_OID_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG, L"2.5.4.6=US; 2.5.4.8=Minnesota; 2.5.4.7=Minneapolis; 2.5.4.10=CodeWeavers; 2.5.4.11=Wine Development;" - " 2.5.4.3=localhost; 1.2.840.113549.1.9.1=aric@codeweavers.com", FALSE); + " 2.5.4.3=localhost; 1.2.840.113549.1.9.1=aric@codeweavers.com"); test_NameToStrConversionW(&context->pCertInfo->Subject, CERT_OID_NAME_STR | CERT_NAME_STR_CRLF_FLAG, L"2.5.4.6=US\r\n2.5.4.8=Minnesota\r\n2.5.4.7=Minneapolis\r\n2.5.4.10=CodeWeavers\r\n2.5.4.11=Wine " - "Development\r\n2.5.4.3=localhost\r\n1.2.840.113549.1.9.1=aric@codeweavers.com", FALSE); + "Development\r\n2.5.4.3=localhost\r\n1.2.840.113549.1.9.1=aric@codeweavers.com"); test_NameToStrConversionW(&context->pCertInfo->Subject, CERT_X500_NAME_STR | CERT_NAME_STR_SEMICOLON_FLAG | CERT_NAME_STR_REVERSE_FLAG, L"E=aric@codeweavers.com; CN=localhost; OU=Wine Development; O=CodeWeavers; L=Minneapolis; S=Minnesota; " - "C=US", FALSE); + "C=US"); CertFreeCertificateContext(context); } blob.pbData = encodedSimpleCN; blob.cbData = sizeof(encodedSimpleCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=1", FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=1"); blob.pbData = encodedSingleQuotedCN; blob.cbData = sizeof(encodedSingleQuotedCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN='1'", - FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, - L"'1'", FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN='1'"); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"'1'"); blob.pbData = encodedSpacedCN; blob.cbData = sizeof(encodedSpacedCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\" 1 \"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\" 1 \"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\" 1 \""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\" 1 \""); blob.pbData = encodedQuotedCN; blob.cbData = sizeof(encodedQuotedCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"\"\"1\"\"\"", - FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"\"\"1\"\"\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"\"\"1\"\"\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"\"\"1\"\"\""); blob.pbData = encodedMultipleAttrCN; blob.cbData = sizeof(encodedMultipleAttrCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"1+2\"", - FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, - L"\"1+2\"", FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"1+2\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"1+2\""); blob.pbData = encodedCommaCN; blob.cbData = sizeof(encodedCommaCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a,b\"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a,b\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a,b\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a,b\""); blob.pbData = encodedEqualCN; blob.cbData = sizeof(encodedEqualCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a=b\"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a=b\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a=b\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a=b\""); blob.pbData = encodedLessThanCN; blob.cbData = sizeof(encodedLessThanCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"<\"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"<\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"<\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"<\""); blob.pbData = encodedGreaterThanCN; blob.cbData = sizeof(encodedGreaterThanCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\">\"", - FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, - L"\">\"", FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\">\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\">\""); blob.pbData = encodedHashCN; blob.cbData = sizeof(encodedHashCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"#\"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"#\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"#\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"#\""); blob.pbData = encodedSemiCN; blob.cbData = sizeof(encodedSemiCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\";\"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\";\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\";\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\";\""); blob.pbData = encodedNewlineCN; blob.cbData = sizeof(encodedNewlineCN); - test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a\nb\"", FALSE); - test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a\nb\"", - FALSE); + test_NameToStrConversionW(&blob, CERT_X500_NAME_STR, L"CN=\"a\nb\""); + test_NameToStrConversionW(&blob, CERT_SIMPLE_NAME_STR, L"\"a\nb\""); } struct StrToNameA @@ -747,153 +843,140 @@ static void test_CertStrToNameW(void) } } -static void test_CertGetNameStringA(void) +#define test_CertGetNameString_value(a, b, c, d, e) test_CertGetNameString_value_(__LINE__, a, b, c, d, e) +static void test_CertGetNameString_value_(unsigned int line, PCCERT_CONTEXT context, DWORD type, DWORD flags, + void *type_para, const char *expected) { + DWORD len, retlen, expected_len; + WCHAR expectedW[512]; + WCHAR strW[512]; + char str[512]; + + expected_len = 0; + while(expected[expected_len]) + { + while((expectedW[expected_len] = expected[expected_len])) + ++expected_len; + if (!(flags & CERT_NAME_SEARCH_ALL_NAMES_FLAG)) + break; + expectedW[expected_len++] = 0; + } + expectedW[expected_len++] = 0; + + len = CertGetNameStringA(context, type, flags, type_para, NULL, 0); + ok(len == expected_len, "line %u: unexpected length %ld, expected %ld.\n", line, len, expected_len); + memset(str, 0xcc, len); + retlen = CertGetNameStringA(context, type, flags, type_para, str, len); + ok(retlen == len, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len); + ok(!memcmp(str, expected, expected_len), "line %u: unexpected value %s.\n", line, debugstr_an(str, expected_len)); + str[0] = str[1] = 0xcc; + retlen = CertGetNameStringA(context, type, flags, type_para, str, len - 1); + ok(retlen == 1, "line %u: Unexpected len %lu, expected 1.\n", line, retlen); + if (len == 1) return; + ok(!str[0], "line %u: unexpected str[0] %#x.\n", line, str[0]); + ok(str[1] == expected[1], "line %u: unexpected str[1] %#x.\n", line, str[1]); + ok(!memcmp(str + 1, expected + 1, len - 2), + "line %u: str %s, string data mismatch.\n", line, debugstr_a(str + 1)); + retlen = CertGetNameStringA(context, type, flags, type_para, str, 0); + ok(retlen == len, "line %u: Unexpected len %lu, expected 1.\n", line, retlen); + + memset(strW, 0xcc, len * sizeof(*strW)); + retlen = CertGetNameStringW(context, type, flags, type_para, strW, len); + ok(retlen == expected_len, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, expected_len); + ok(!memcmp(strW, expectedW, len * sizeof(*strW)), "line %u: unexpected value %s.\n", line, debugstr_wn(strW, len)); + strW[0] = strW[1] = 0xcccc; + retlen = CertGetNameStringW(context, type, flags, type_para, strW, len - 1); + ok(retlen == len - 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1); + if (flags & CERT_NAME_SEARCH_ALL_NAMES_FLAG) + { + ok(!memcmp(strW, expectedW, (retlen - 2) * sizeof(*strW)), + "line %u: str %s, string data mismatch.\n", line, debugstr_wn(strW, retlen - 2)); + ok(!strW[retlen - 2], "line %u: string is not zero terminated.\n", line); + ok(!strW[retlen - 1], "line %u: string sequence is not zero terminated.\n", line); + + retlen = CertGetNameStringW(context, type, flags, type_para, strW, 1); + ok(retlen == 1, "line %u: unexpected len %lu, expected %lu.\n", line, retlen, len - 1); + ok(!strW[retlen - 1], "line %u: string sequence is not zero terminated.\n", line); + } + else + { + ok(!memcmp(strW, expectedW, (retlen - 1) * sizeof(*strW)), + "line %u: str %s, string data mismatch.\n", line, debugstr_wn(strW, retlen - 1)); + ok(!strW[retlen - 1], "line %u: string is not zero terminated.\n", line); + } + retlen = CertGetNameStringA(context, type, flags, type_para, NULL, len - 1); + ok(retlen == len, "line %u: unexpected len %lu, expected %lu\n", line, retlen, len); + retlen = CertGetNameStringW(context, type, flags, type_para, NULL, len - 1); + ok(retlen == len, "line %u: unexpected len %lu, expected %lu\n", line, retlen, len); +} + +static void test_CertGetNameString(void) +{ + static const char aric[] = "aric@codeweavers.com"; + static const char localhost[] = "localhost"; PCCERT_CONTEXT context; + DWORD len, type; context = CertCreateCertificateContext(X509_ASN_ENCODING, cert, sizeof(cert)); - ok(context != NULL, "CertCreateCertificateContext failed: %08lx\n", - GetLastError()); - if (context) - { - static const char aric[] = "aric@codeweavers.com"; - static const char localhost[] = "localhost"; - DWORD len, type; - LPSTR str; - - /* Bad string types/types missing from the cert */ - len = CertGetNameStringA(NULL, 0, 0, NULL, NULL, 0); - ok(len == 1, "expected 1, got %ld\n", len); - len = CertGetNameStringA(context, 0, 0, NULL, NULL, 0); - ok(len == 1, "expected 1, got %ld\n", len); - len = CertGetNameStringA(context, CERT_NAME_URL_TYPE, 0, NULL, NULL, - 0); - ok(len == 1, "expected 1, got %ld\n", len); - - len = CertGetNameStringA(context, CERT_NAME_EMAIL_TYPE, 0, NULL, NULL, - 0); - ok(len == strlen(aric) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_EMAIL_TYPE, 0, NULL, - str, len); - ok(!strcmp(str, aric), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - - len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, NULL, NULL, - 0); - ok(len == strlen(issuerStr) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, NULL, - str, len); - ok(!strcmp(str, issuerStr), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - type = 0; - len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type, NULL, - 0); - ok(len == strlen(issuerStr) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type, - str, len); - ok(!strcmp(str, issuerStr), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - type = CERT_OID_NAME_STR; - len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type, NULL, - 0); - ok(len == strlen(subjectStr) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_RDN_TYPE, 0, &type, - str, len); - ok(!strcmp(str, subjectStr), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - - len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, NULL, NULL, - 0); - ok(len == strlen(aric) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, NULL, - str, len); - ok(!strcmp(str, aric), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, - (void *)szOID_RSA_emailAddr, NULL, 0); - ok(len == strlen(aric) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, - (void *)szOID_RSA_emailAddr, str, len); - ok(!strcmp(str, aric), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, - (void *)szOID_COMMON_NAME, NULL, 0); - ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_ATTR_TYPE, 0, - (void *)szOID_COMMON_NAME, str, len); - ok(!strcmp(str, localhost), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - - len = CertGetNameStringA(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, - NULL, NULL, 0); - ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, - 0, NULL, str, len); - ok(!strcmp(str, localhost), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - - len = CertGetNameStringA(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, - NULL, NULL, 0); - ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len); - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, - 0, NULL, str, len); - ok(!strcmp(str, localhost), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - - len = CertGetNameStringA(context, CERT_NAME_DNS_TYPE, 0, NULL, NULL, - 0); - ok(len == strlen(localhost) + 1, "unexpected length %ld\n", len); - if (len > 1) - { - str = HeapAlloc(GetProcessHeap(), 0, len); - if (str) - { - len = CertGetNameStringA(context, CERT_NAME_DNS_TYPE, 0, NULL, - str, len); - ok(!strcmp(str, localhost), "unexpected value %s\n", str); - HeapFree(GetProcessHeap(), 0, str); - } - } - - CertFreeCertificateContext(context); - } + ok(!!context, "CertCreateCertificateContext failed, err %lu\n", GetLastError()); + + /* Bad string types/types missing from the cert */ + len = CertGetNameStringA(NULL, 0, 0, NULL, NULL, 0); + ok(len == 1, "expected 1, got %lu\n", len); + len = CertGetNameStringA(context, 0, 0, NULL, NULL, 0); + ok(len == 1, "expected 1, got %lu\n", len); + len = CertGetNameStringA(context, CERT_NAME_URL_TYPE, 0, NULL, NULL, 0); + ok(len == 1, "expected 1, got %lu\n", len); + + len = CertGetNameStringW(NULL, 0, 0, NULL, NULL, 0); + ok(len == 1, "expected 1, got %lu\n", len); + len = CertGetNameStringW(context, 0, 0, NULL, NULL, 0); + ok(len == 1, "expected 1, got %lu\n", len); + len = CertGetNameStringW(context, CERT_NAME_URL_TYPE, 0, NULL, NULL, 0); + ok(len == 1, "expected 1, got %lu\n", len); + + test_CertGetNameString_value(context, CERT_NAME_EMAIL_TYPE, 0, NULL, aric); + test_CertGetNameString_value(context, CERT_NAME_RDN_TYPE, 0, NULL, issuerStr); + type = 0; + test_CertGetNameString_value(context, CERT_NAME_RDN_TYPE, 0, &type, issuerStr); + type = CERT_OID_NAME_STR; + test_CertGetNameString_value(context, CERT_NAME_RDN_TYPE, 0, &type, subjectStr); + test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, 0, NULL, aric); + test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, 0, (void *)szOID_RSA_emailAddr, aric); + test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, 0, (void *)szOID_COMMON_NAME, localhost); + test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, localhost); + test_CertGetNameString_value(context, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, localhost); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, 0, NULL, localhost); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, NULL, "localhost\0"); + test_CertGetNameString_value(context, CERT_NAME_EMAIL_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, NULL, ""); + test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, NULL, ""); + + CertFreeCertificateContext(context); + + ok(cert_v3[CERT_V3_SAN_OID_OFFSET] == 0x55, "Incorrect CERT_V3_SAN_OID_OFFSET.\n"); + ok(cert_v3[CERT_V3_IAN_OID_OFFSET] == 0x55, "Incorrect CERT_V3_IAN_OID_OFFSET.\n"); + cert_v3[CERT_V3_SAN_OID_OFFSET + 2] = 7; /* legacy OID_SUBJECT_ALT_NAME */ + cert_v3[CERT_V3_IAN_OID_OFFSET + 2] = 8; /* legacy OID_ISSUER_ALT_NAME */ + context = CertCreateCertificateContext(X509_ASN_ENCODING, cert_v3, sizeof(cert_v3)); + ok(!!context, "CertCreateCertificateContext failed, err %lu\n", GetLastError()); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, 0, NULL, "ex1.org"); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_ISSUER_FLAG, NULL, "ex3.org"); + CertFreeCertificateContext(context); + + cert_v3[CERT_V3_SAN_OID_OFFSET + 2] = 17; /* OID_SUBJECT_ALT_NAME2 */ + cert_v3[CERT_V3_IAN_OID_OFFSET + 2] = 18; /* OID_ISSUER_ALT_NAME2 */ + context = CertCreateCertificateContext(X509_ASN_ENCODING, cert_v3, sizeof(cert_v3)); + ok(!!context, "CertCreateCertificateContext failed, err %lu\n", GetLastError()); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, 0, NULL, "ex1.org"); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_ISSUER_FLAG, NULL, "ex3.org"); + test_CertGetNameString_value(context, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, "server_cn.org"); + test_CertGetNameString_value(context, CERT_NAME_ATTR_TYPE, 0, (void *)szOID_SUR_NAME, ""); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG, + NULL, "ex1.org\0*.ex2.org\0"); + test_CertGetNameString_value(context, CERT_NAME_DNS_TYPE, CERT_NAME_SEARCH_ALL_NAMES_FLAG | CERT_NAME_ISSUER_FLAG, + NULL, "ex3.org\0*.ex4.org\0"); + CertFreeCertificateContext(context); } START_TEST(str) @@ -904,5 +987,5 @@ START_TEST(str) test_CertNameToStrW(); test_CertStrToNameA(); test_CertStrToNameW(); - test_CertGetNameStringA(); + test_CertGetNameString(); } diff --git a/dlls/crypt32/unixlib.c b/dlls/crypt32/unixlib.c index 069cb049851..9a36d12f293 100644 --- wine/dlls/crypt32/unixlib.c +++ wine/dlls/crypt32/unixlib.c @@ -95,21 +95,7 @@ static NTSTATUS process_attach( void *args ) setenv("GNUTLS_SYSTEM_PRIORITY_FILE", "/dev/null", 0); } -if (1) { /* CROSSOVER HACK - bug 10151 */ - const char *libgnutls_name_candidates[] = {SONAME_LIBGNUTLS, - "libgnutls.so.30", - "libgnutls.so.28", - "libgnutls-deb0.so.28", - "libgnutls.so.26", - NULL}; - int i; - for (i=0; libgnutls_name_candidates[i] && !libgnutls_handle; i++) - libgnutls_handle = dlopen(libgnutls_name_candidates[i], RTLD_NOW); -} -else - libgnutls_handle = dlopen( SONAME_LIBGNUTLS, RTLD_NOW ); - - if (!libgnutls_handle) + if (!(libgnutls_handle = dlopen( SONAME_LIBGNUTLS, RTLD_NOW ))) { ERR_(winediag)( "failed to load libgnutls, no support for pfx import/export\n" ); return STATUS_DLL_NOT_FOUND; diff --git a/dlls/cryptnet/cryptnet_main.c b/dlls/cryptnet/cryptnet_main.c index 6654ef77c8c..19de1ed2d8e 100644 --- wine/dlls/cryptnet/cryptnet_main.c +++ wine/dlls/cryptnet/cryptnet_main.c @@ -1690,6 +1690,15 @@ static DWORD verify_cert_revocation_from_dist_points_ext(const CRYPT_DATA_BLOB * const CRL_CONTEXT *crl; DWORD timeout = 0; + if (!params || !params->pIssuerCert) + { + TRACE("no issuer certificate\n"); + return CRYPT_E_REVOCATION_OFFLINE; + } + + if (find_cached_revocation_status(&cert->pCertInfo->SerialNumber, time, status)) + return status->dwError; + if (!CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &url_array_size, NULL, NULL)) return GetLastError(); @@ -1918,6 +1927,7 @@ static DWORD verify_signed_ocsp_response_info(const CERT_INFO *cert, const CERT_ HCRYPTPROV prov = 0; HCRYPTHASH hash = 0; HCRYPTKEY key = 0; + DWORD algid; if (!CryptDecodeObjectEx(X509_ASN_ENCODING, OCSP_BASIC_SIGNED_RESPONSE, blob->pbData, blob->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)) return GetLastError(); @@ -1925,7 +1935,7 @@ static DWORD verify_signed_ocsp_response_info(const CERT_INFO *cert, const CERT_ if ((error = check_ocsp_response_info(cert, issuer, &info->ToBeSigned, &status))) goto done; alg = &info->SignatureInfo.SignatureAlgorithm; - if (!alg->pszObjId || strcmp(alg->pszObjId, szOID_RSA_SHA256RSA)) + if (!alg->pszObjId || !(algid = CertOIDToAlgId(alg->pszObjId))) { FIXME("unhandled signature algorithm %s\n", debugstr_a(alg->pszObjId)); error = CRYPT_E_NO_REVOCATION_CHECK; @@ -1933,7 +1943,7 @@ static DWORD verify_signed_ocsp_response_info(const CERT_INFO *cert, const CERT_ } if (!CryptAcquireContextW(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) goto done; - if (!CryptCreateHash(prov, CALG_SHA_256, 0, 0, &hash)) goto done; + if (!CryptCreateHash(prov, algid, 0, 0, &hash)) goto done; if (!CryptHashData(hash, info->ToBeSigned.pbData, info->ToBeSigned.cbData, 0)) goto done; sig = &info->SignatureInfo.Signature; @@ -2136,22 +2146,19 @@ static DWORD verify_cert_revocation(const CERT_CONTEXT *cert, FILETIME *pTime, DWORD error = ERROR_SUCCESS; PCERT_EXTENSION ext; - if (find_cached_revocation_status(&cert->pCertInfo->SerialNumber, pTime, pRevStatus)) - return pRevStatus->dwError; - - if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS, - cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) + if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS, cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) { - error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert, - pTime, dwFlags, pRevPara, pRevStatus); + error = verify_cert_revocation_from_aia_ext(&ext->Value, cert, pTime, dwFlags, pRevPara, pRevStatus); + TRACE("verify_cert_revocation_from_aia_ext() returned %08lx\n", error); + if (error == ERROR_SUCCESS || error == CRYPT_E_REVOKED) return error; } - else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS, - cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) + if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS, cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) { - error = verify_cert_revocation_from_aia_ext(&ext->Value, cert, pTime, - dwFlags, pRevPara, pRevStatus); + error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert, pTime, dwFlags, pRevPara, pRevStatus); + TRACE("verify_cert_revocation_from_dist_points_ext() returned %08lx\n", error); + if (error == ERROR_SUCCESS || error == CRYPT_E_REVOKED) return error; } - else + if (!ext) { if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert) { diff --git a/dlls/wintrust/softpub.c b/dlls/wintrust/softpub.c index 53df5e7fe60..06b178a98b9 100644 --- wine/dlls/wintrust/softpub.c +++ wine/dlls/wintrust/softpub.c @@ -830,16 +830,93 @@ static DWORD WINTRUST_VerifySigner(CRYPT_PROVIDER_DATA *data, DWORD signerIdx) return err; } +static void load_secondary_signatures(CRYPT_PROVIDER_DATA *data, HCRYPTMSG msg) +{ + CRYPT_PROVIDER_SIGSTATE *s = data->pSigState; + CRYPT_ATTRIBUTES *attrs; + unsigned int i, j; + DWORD size; + + if (!CryptMsgGetParam(msg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, NULL, &size)) + return; + + if (!(attrs = data->psPfns->pfnAlloc(size))) + { + ERR("No memory.\n"); + return; + } + if (!CryptMsgGetParam(msg, CMSG_SIGNER_UNAUTH_ATTR_PARAM, 0, attrs, &size)) + goto done; + + for (i = 0; i < attrs->cAttr; ++i) + { + if (strcmp(attrs->rgAttr[i].pszObjId, szOID_NESTED_SIGNATURE)) + continue; + + if (!(s->rhSecondarySigs = data->psPfns->pfnAlloc(attrs->rgAttr[i].cValue * sizeof(*s->rhSecondarySigs)))) + { + ERR("No memory"); + goto done; + } + s->cSecondarySigs = 0; + for (j = 0; j < attrs->rgAttr[i].cValue; ++j) + { + if (!(msg = CryptMsgOpenToDecode(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, 0, 0, NULL, NULL))) + { + ERR("Could not create crypt message.\n"); + goto done; + } + if (!CryptMsgUpdate(msg, attrs->rgAttr[i].rgValue[j].pbData, attrs->rgAttr[i].rgValue[j].cbData, TRUE)) + { + ERR("Could not update crypt message, err %lu.\n", GetLastError()); + CryptMsgClose(msg); + goto done; + } + s->rhSecondarySigs[j] = msg; + ++s->cSecondarySigs; + } + break; + } +done: + data->psPfns->pfnFree(attrs); +} + HRESULT WINAPI SoftpubLoadSignature(CRYPT_PROVIDER_DATA *data) { - DWORD err; + DWORD err = ERROR_SUCCESS; TRACE("(%p)\n", data); if (!data->padwTrustStepErrors) return S_FALSE; - if (data->hMsg) + if (data->pSigState) + { + /* We did not initialize this, probably an unsupported usage. */ + FIXME("pSigState %p already initialized.\n", data->pSigState); + } + if (!(data->pSigState = data->psPfns->pfnAlloc(sizeof(*data->pSigState)))) + { + err = ERROR_OUTOFMEMORY; + } + else + { + data->pSigState->cbStruct = sizeof(*data->pSigState); + data->pSigState->fSupportMultiSig = TRUE; + data->pSigState->dwCryptoPolicySupport = WSS_SIGTRUST_SUPPORT | WSS_OBJTRUST_SUPPORT | WSS_CERTTRUST_SUPPORT; + if (data->hMsg) + { + data->pSigState->hPrimarySig = CryptMsgDuplicate(data->hMsg); + load_secondary_signatures(data, data->pSigState->hPrimarySig); + } + if (data->pSigSettings) + { + if (data->pSigSettings->dwFlags & WSS_GET_SECONDARY_SIG_COUNT) + data->pSigSettings->cSecondarySigs = data->pSigState->cSecondarySigs; + } + } + + if (!err && data->hMsg) { DWORD signerCount, size; @@ -859,8 +936,7 @@ HRESULT WINAPI SoftpubLoadSignature(CRYPT_PROVIDER_DATA *data) else err = TRUST_E_NOSIGNATURE; } - else - err = ERROR_SUCCESS; + if (err) data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_SIGPROV] = err; return !err ? S_OK : S_FALSE; @@ -1375,6 +1451,13 @@ HRESULT WINAPI SoftpubCleanup(CRYPT_PROVIDER_DATA *data) data->psPfns->pfnFree(data->u.pPDSip->psIndirectData); } + if (WVT_ISINSTRUCT(CRYPT_PROVIDER_DATA, data->cbStruct, pSigState) && data->pSigState) + { + CryptMsgClose(data->pSigState->hPrimarySig); + for (i = 0; i < data->pSigState->cSecondarySigs; ++i) + CryptMsgClose(data->pSigState->rhSecondarySigs[i]); + data->psPfns->pfnFree(data->pSigState); + } CryptMsgClose(data->hMsg); if (data->fOpenedFile && diff --git a/dlls/wintrust/tests/softpub.c b/dlls/wintrust/tests/softpub.c index 8195e6006b1..77e15b2feb4 100644 --- wine/dlls/wintrust/tests/softpub.c +++ wine/dlls/wintrust/tests/softpub.c @@ -1123,6 +1123,476 @@ static const BYTE SelfSignedFile64[] = 0x9C,0x68,0x1A,0x5D,0x92,0xCD,0xD0,0x5F,0x02,0xA1,0x2C,0xD9,0x56,0x20,0x00,0x00 }; +/* Self-signed 32 bit .exe, built with mingw-gcc, stripped, signed with signtool + * (certificates generated with a self-signed CA). + * + * small.c: + * int _start() + * { + * return 0; + * } + * + * i686-w64-mingw32-gcc -s -nodefaultlibs -fno-PIC ./small.c -o sign_3certs.exe + * strip -R .idata -R .rdata -R .edata -R .eh_fram ./sign_3certs.exe + * signtool.exe sign /v /f cert1.pfx /fd SHA256 /t http://timestamp.digicert.com sign_3certs.exe + * signtool.exe sign /v /f cert2.pfx /as /fd SHA256 sign_3certs.exe + * signtool.exe sign /v /f cert3.pfx /as /fd SHA256 sign_3certs.exe */ + +static const BYTE self_signed_3certs[] = +{ + 0x4d,0x5a,0x90,0x00,0x03,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0xff,0xff,0x00,0x00,0xb8,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x40,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x0e,0x1f,0xba,0x0e,0x00,0xb4,0x09,0xcd, + 0x21,0xb8,0x01,0x4c,0xcd,0x21,0x54,0x68,0x69,0x73,0x20,0x70,0x72,0x6f,0x67,0x72,0x61,0x6d,0x20,0x63,0x61,0x6e,0x6e,0x6f, + 0x74,0x20,0x62,0x65,0x20,0x72,0x75,0x6e,0x20,0x69,0x6e,0x20,0x44,0x4f,0x53,0x20,0x6d,0x6f,0x64,0x65,0x2e,0x0d,0x0d,0x0a, + 0x24,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x50,0x45,0x00,0x00,0x4c,0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0xe0,0x00,0x0e,0x03,0x0b,0x01,0x02,0x25,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x10,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00, + 0x04,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x02,0x00,0x00, + 0x76,0x3e,0x00,0x00,0x03,0x00,0x40,0x01,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x10,0x00,0x00, + 0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x14,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x48,0x26,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x2e,0x74,0x65,0x78,0x74,0x00,0x00,0x00, + 0x1c,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x55,0x89,0xe5,0xb8,0x00,0x00,0x00,0x00,0x5d,0xc3,0x90,0x90,0xff,0xff,0xff,0xff, + 0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x26,0x00,0x00,0x00,0x02,0x02,0x00, + 0x30,0x82,0x26,0x36,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x02,0xa0,0x82,0x26,0x27,0x30,0x82,0x26,0x23,0x02, + 0x01,0x01,0x31,0x0f,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x30,0x5c,0x06,0x0a,0x2b, + 0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x04,0xa0,0x4e,0x30,0x4c,0x30,0x17,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37, + 0x02,0x01,0x0f,0x30,0x09,0x03,0x01,0x00,0xa0,0x04,0xa2,0x02,0x80,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01, + 0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20,0xdd,0x8b,0xd7,0x29,0x3b,0xae,0x16,0xec,0xbb,0x81,0x80,0x55,0x15,0xd8,0x87, + 0xa5,0x3e,0xeb,0x0b,0x74,0x59,0xb6,0x56,0xf1,0x0b,0x2e,0xe1,0xb4,0x42,0x4d,0x8b,0x18,0xa0,0x82,0x16,0x0c,0x30,0x82,0x03, + 0x01,0x30,0x82,0x01,0xe9,0xa0,0x03,0x02,0x01,0x02,0x02,0x10,0xd1,0x73,0x97,0xaa,0xa7,0x3a,0x31,0xa2,0x44,0xc0,0x4b,0x40, + 0x69,0x40,0x4b,0xfa,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x30,0x12,0x31,0x10,0x30, + 0x0e,0x06,0x03,0x55,0x04,0x03,0x13,0x07,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x30,0x1e,0x17,0x0d,0x32,0x32,0x30,0x39,0x33, + 0x30,0x31,0x37,0x31,0x39,0x33,0x32,0x5a,0x17,0x0d,0x33,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5a,0x30, + 0x10,0x31,0x0e,0x30,0x0c,0x06,0x03,0x55,0x04,0x03,0x13,0x05,0x63,0x65,0x72,0x74,0x31,0x30,0x82,0x01,0x22,0x30,0x0d,0x06, + 0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01, + 0x01,0x00,0xca,0x9c,0xd9,0xd4,0x25,0xb6,0x45,0x61,0x22,0x8d,0xdf,0xe9,0x11,0x0f,0xa1,0x7e,0x45,0xc5,0x0b,0xd0,0x42,0xfc, + 0x1f,0x3e,0xce,0x20,0xfc,0x1b,0x37,0xe4,0x0d,0x06,0x83,0x1c,0x3a,0x71,0x0f,0x75,0xf5,0xe5,0x06,0x33,0x01,0x77,0xda,0xc5, + 0xe9,0x2e,0xe3,0x37,0x1e,0x51,0x6e,0x08,0xe2,0x02,0xa1,0x8c,0x11,0xc6,0xfc,0x43,0xa2,0xf5,0x7d,0x74,0x5d,0x5a,0xcc,0x85, + 0x27,0x38,0xd4,0xfa,0xad,0xd7,0xf9,0x77,0xe4,0xef,0xdd,0xb0,0xb1,0x3e,0xdc,0xf5,0x5d,0x7e,0x62,0xdf,0x16,0x01,0x88,0xcd, + 0xb0,0xfa,0x06,0x24,0xd7,0xce,0xdc,0xe2,0x27,0xab,0xc3,0x0e,0x44,0x59,0x39,0x38,0xae,0x0a,0x5a,0xbd,0x5c,0xfd,0x11,0xed, + 0x5e,0xb8,0xd3,0x09,0x9c,0x84,0x80,0x6f,0x38,0xdf,0xd2,0xed,0x12,0x33,0xc9,0x66,0x3e,0x77,0x95,0x40,0xca,0xbb,0x63,0xd8, + 0x44,0x62,0x1d,0x60,0xc1,0x0d,0x92,0x18,0x68,0x4c,0xc7,0x26,0x83,0x5b,0x38,0x45,0xda,0x8d,0xe6,0x11,0xd0,0x08,0x79,0x0c, + 0x13,0xb8,0xe0,0xab,0xf5,0x78,0xe2,0x45,0xfd,0x42,0x7f,0x33,0xab,0x6d,0x53,0x10,0xa3,0x02,0x3c,0xd3,0x6f,0xaf,0x50,0x2f, + 0x20,0xfc,0x92,0xd1,0xab,0x68,0xe8,0x00,0xa0,0x1c,0x4b,0x6f,0x02,0x5a,0xf4,0x1a,0xf1,0x06,0x79,0xa1,0x34,0x8d,0x04,0x5c, + 0x0d,0xfe,0x2d,0x3c,0x53,0xb6,0xae,0x80,0x7d,0x98,0xb9,0x02,0x60,0x15,0x2c,0xb2,0xe5,0xc7,0x9b,0xcf,0x78,0x53,0x37,0xd9, + 0xbf,0x84,0x04,0xb0,0x61,0x1c,0xea,0x24,0x7b,0xf7,0xcd,0x71,0x45,0x1a,0x00,0x22,0x21,0xa9,0x02,0x03,0x01,0x00,0x01,0xa3, + 0x55,0x30,0x53,0x30,0x0c,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x43,0x06,0x03,0x55,0x1d,0x01, + 0x04,0x3c,0x30,0x3a,0x80,0x10,0x88,0x17,0xf7,0x38,0x65,0x8b,0x78,0x78,0xf6,0x77,0xe3,0x25,0x47,0x54,0x33,0x4c,0xa1,0x14, + 0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04,0x03,0x13,0x07,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x82,0x10,0x2b,0x59, + 0xb4,0xc7,0xe2,0xce,0x08,0x97,0x46,0x48,0x32,0x17,0x0f,0x97,0xc5,0x08,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d, + 0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x9d,0x05,0x0e,0xc5,0xa0,0x5e,0x47,0x18,0x31,0x60,0xf2,0x1b,0x37,0xa4, + 0x89,0xf7,0x05,0x3e,0xea,0xc2,0x00,0x9f,0xcb,0xdd,0x28,0xba,0xc9,0x1f,0xfa,0x7a,0x9b,0x24,0x3d,0xb6,0x47,0x80,0xc1,0xa6, + 0x67,0x4d,0x48,0x3d,0xe0,0x0b,0x32,0x6a,0xa7,0x93,0xf3,0x40,0x20,0x8a,0xff,0x0f,0x9a,0xe2,0x00,0x95,0xa3,0xb3,0x57,0xc7, + 0x11,0xe1,0x28,0xc5,0x63,0x01,0xdf,0x4a,0xd2,0x37,0xb2,0x53,0x09,0x5c,0x4e,0x50,0x4e,0x14,0xb8,0x3e,0xb4,0x52,0xfe,0xa5, + 0x5d,0x14,0x3f,0x07,0x4f,0xda,0x9a,0xb9,0xbe,0x40,0xc5,0x3b,0x90,0x54,0x03,0x2e,0x79,0x0e,0x9b,0xf7,0xa9,0x74,0xeb,0x7c, + 0x6b,0x71,0x12,0xf2,0xce,0x9f,0xc0,0x3e,0x8a,0x09,0xa4,0x91,0x91,0x93,0x64,0x11,0xcc,0x96,0x7b,0xf9,0xac,0x65,0x6b,0xc3, + 0x02,0x1d,0xf8,0x0c,0x82,0x72,0x04,0x19,0x05,0x06,0x33,0x44,0x48,0x4f,0x34,0x13,0x04,0x1e,0x6c,0x11,0xc0,0x7b,0x63,0x32, + 0x1e,0xb3,0x4f,0x79,0xfe,0x9d,0xe6,0x3a,0xbe,0x8e,0xa7,0x5f,0x67,0x1d,0xae,0xad,0x58,0x0e,0x53,0xb8,0x15,0xe3,0x85,0x6e, + 0x91,0xfe,0x2d,0x81,0x84,0xb9,0xc3,0x23,0x13,0xa0,0x3f,0x72,0xb7,0xb3,0x26,0xda,0x08,0xcf,0x10,0x65,0x1e,0xd5,0x3b,0xf4, + 0x8f,0x18,0xe0,0xab,0xe7,0x5e,0xfc,0x62,0x9e,0x7e,0x54,0xf9,0x35,0x5a,0xf8,0xfa,0x1f,0x10,0x6f,0x63,0x3d,0xa2,0xe9,0x8a, + 0xd6,0x49,0xc0,0x40,0x0b,0xa1,0x5e,0x83,0xb0,0x01,0xb6,0x03,0x66,0xa5,0x8a,0xb4,0x29,0x06,0xea,0x27,0x0c,0x28,0x88,0xf3, + 0x38,0x5e,0x30,0x82,0x05,0x8d,0x30,0x82,0x04,0x75,0xa0,0x03,0x02,0x01,0x02,0x02,0x10,0x0e,0x9b,0x18,0x8e,0xf9,0xd0,0x2d, + 0xe7,0xef,0xdb,0x50,0xe2,0x08,0x40,0x18,0x5a,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0c,0x05,0x00, + 0x30,0x65,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0a, + 0x13,0x0c,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6e,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0b,0x13, + 0x10,0x77,0x77,0x77,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x31,0x24,0x30,0x22,0x06,0x03,0x55, + 0x04,0x03,0x13,0x1b,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x20,0x49,0x44,0x20, + 0x52,0x6f,0x6f,0x74,0x20,0x43,0x41,0x30,0x1e,0x17,0x0d,0x32,0x32,0x30,0x38,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a, + 0x17,0x0d,0x33,0x31,0x31,0x31,0x30,0x39,0x32,0x33,0x35,0x39,0x35,0x39,0x5a,0x30,0x62,0x31,0x0b,0x30,0x09,0x06,0x03,0x55, + 0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0a,0x13,0x0c,0x44,0x69,0x67,0x69,0x43,0x65,0x72, + 0x74,0x20,0x49,0x6e,0x63,0x31,0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0b,0x13,0x10,0x77,0x77,0x77,0x2e,0x64,0x69,0x67,0x69, + 0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x31,0x21,0x30,0x1f,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x44,0x69,0x67,0x69,0x43, + 0x65,0x72,0x74,0x20,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x52,0x6f,0x6f,0x74,0x20,0x47,0x34,0x30,0x82,0x02,0x22,0x30, + 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02,0x0f,0x00,0x30,0x82,0x02,0x0a,0x02, + 0x82,0x02,0x01,0x00,0xbf,0xe6,0x90,0x73,0x68,0xde,0xbb,0xe4,0x5d,0x4a,0x3c,0x30,0x22,0x30,0x69,0x33,0xec,0xc2,0xa7,0x25, + 0x2e,0xc9,0x21,0x3d,0xf2,0x8a,0xd8,0x59,0xc2,0xe1,0x29,0xa7,0x3d,0x58,0xab,0x76,0x9a,0xcd,0xae,0x7b,0x1b,0x84,0x0d,0xc4, + 0x30,0x1f,0xf3,0x1b,0xa4,0x38,0x16,0xeb,0x56,0xc6,0x97,0x6d,0x1d,0xab,0xb2,0x79,0xf2,0xca,0x11,0xd2,0xe4,0x5f,0xd6,0x05, + 0x3c,0x52,0x0f,0x52,0x1f,0xc6,0x9e,0x15,0xa5,0x7e,0xbe,0x9f,0xa9,0x57,0x16,0x59,0x55,0x72,0xaf,0x68,0x93,0x70,0xc2,0xb2, + 0xba,0x75,0x99,0x6a,0x73,0x32,0x94,0xd1,0x10,0x44,0x10,0x2e,0xdf,0x82,0xf3,0x07,0x84,0xe6,0x74,0x3b,0x6d,0x71,0xe2,0x2d, + 0x0c,0x1b,0xee,0x20,0xd5,0xc9,0x20,0x1d,0x63,0x29,0x2d,0xce,0xec,0x5e,0x4e,0xc8,0x93,0xf8,0x21,0x61,0x9b,0x34,0xeb,0x05, + 0xc6,0x5e,0xec,0x5b,0x1a,0xbc,0xeb,0xc9,0xcf,0xcd,0xac,0x34,0x40,0x5f,0xb1,0x7a,0x66,0xee,0x77,0xc8,0x48,0xa8,0x66,0x57, + 0x57,0x9f,0x54,0x58,0x8e,0x0c,0x2b,0xb7,0x4f,0xa7,0x30,0xd9,0x56,0xee,0xca,0x7b,0x5d,0xe3,0xad,0xc9,0x4f,0x5e,0xe5,0x35, + 0xe7,0x31,0xcb,0xda,0x93,0x5e,0xdc,0x8e,0x8f,0x80,0xda,0xb6,0x91,0x98,0x40,0x90,0x79,0xc3,0x78,0xc7,0xb6,0xb1,0xc4,0xb5, + 0x6a,0x18,0x38,0x03,0x10,0x8d,0xd8,0xd4,0x37,0xa4,0x2e,0x05,0x7d,0x88,0xf5,0x82,0x3e,0x10,0x91,0x70,0xab,0x55,0x82,0x41, + 0x32,0xd7,0xdb,0x04,0x73,0x2a,0x6e,0x91,0x01,0x7c,0x21,0x4c,0xd4,0xbc,0xae,0x1b,0x03,0x75,0x5d,0x78,0x66,0xd9,0x3a,0x31, + 0x44,0x9a,0x33,0x40,0xbf,0x08,0xd7,0x5a,0x49,0xa4,0xc2,0xe6,0xa9,0xa0,0x67,0xdd,0xa4,0x27,0xbc,0xa1,0x4f,0x39,0xb5,0x11, + 0x58,0x17,0xf7,0x24,0x5c,0x46,0x8f,0x64,0xf7,0xc1,0x69,0x88,0x76,0x98,0x76,0x3d,0x59,0x5d,0x42,0x76,0x87,0x89,0x97,0x69, + 0x7a,0x48,0xf0,0xe0,0xa2,0x12,0x1b,0x66,0x9a,0x74,0xca,0xde,0x4b,0x1e,0xe7,0x0e,0x63,0xae,0xe6,0xd4,0xef,0x92,0x92,0x3a, + 0x9e,0x3d,0xdc,0x00,0xe4,0x45,0x25,0x89,0xb6,0x9a,0x44,0x19,0x2b,0x7e,0xc0,0x94,0xb4,0xd2,0x61,0x6d,0xeb,0x33,0xd9,0xc5, + 0xdf,0x4b,0x04,0x00,0xcc,0x7d,0x1c,0x95,0xc3,0x8f,0xf7,0x21,0xb2,0xb2,0x11,0xb7,0xbb,0x7f,0xf2,0xd5,0x8c,0x70,0x2c,0x41, + 0x60,0xaa,0xb1,0x63,0x18,0x44,0x95,0x1a,0x76,0x62,0x7e,0xf6,0x80,0xb0,0xfb,0xe8,0x64,0xa6,0x33,0xd1,0x89,0x07,0xe1,0xbd, + 0xb7,0xe6,0x43,0xa4,0x18,0xb8,0xa6,0x77,0x01,0xe1,0x0f,0x94,0x0c,0x21,0x1d,0xb2,0x54,0x29,0x25,0x89,0x6c,0xe5,0x0e,0x52, + 0x51,0x47,0x74,0xbe,0x26,0xac,0xb6,0x41,0x75,0xde,0x7a,0xac,0x5f,0x8d,0x3f,0xc9,0xbc,0xd3,0x41,0x11,0x12,0x5b,0xe5,0x10, + 0x50,0xeb,0x31,0xc5,0xca,0x72,0x16,0x22,0x09,0xdf,0x7c,0x4c,0x75,0x3f,0x63,0xec,0x21,0x5f,0xc4,0x20,0x51,0x6b,0x6f,0xb1, + 0xab,0x86,0x8b,0x4f,0xc2,0xd6,0x45,0x5f,0x9d,0x20,0xfc,0xa1,0x1e,0xc5,0xc0,0x8f,0xa2,0xb1,0x7e,0x0a,0x26,0x99,0xf5,0xe4, + 0x69,0x2f,0x98,0x1d,0x2d,0xf5,0xd9,0xa9,0xb2,0x1d,0xe5,0x1b,0x02,0x03,0x01,0x00,0x01,0xa3,0x82,0x01,0x3a,0x30,0x82,0x01, + 0x36,0x30,0x0f,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x05,0x30,0x03,0x01,0x01,0xff,0x30,0x1d,0x06,0x03,0x55,0x1d, + 0x0e,0x04,0x16,0x04,0x14,0xec,0xd7,0xe3,0x82,0xd2,0x71,0x5d,0x64,0x4c,0xdf,0x2e,0x67,0x3f,0xe7,0xba,0x98,0xae,0x1c,0x0f, + 0x4f,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0x45,0xeb,0xa2,0xaf,0xf4,0x92,0xcb,0x82,0x31,0x2d, + 0x51,0x8b,0xa7,0xa7,0x21,0x9d,0xf3,0x6d,0xc8,0x0f,0x30,0x0e,0x06,0x03,0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02, + 0x01,0x86,0x30,0x79,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x6d,0x30,0x6b,0x30,0x24,0x06,0x08,0x2b,0x06, + 0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6f,0x63,0x73,0x70,0x2e,0x64,0x69,0x67,0x69, + 0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x30,0x43,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x02,0x86,0x37,0x68,0x74, + 0x74,0x70,0x3a,0x2f,0x2f,0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63,0x6f, + 0x6d,0x2f,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x49,0x44,0x52,0x6f,0x6f,0x74,0x43, + 0x41,0x2e,0x63,0x72,0x74,0x30,0x45,0x06,0x03,0x55,0x1d,0x1f,0x04,0x3e,0x30,0x3c,0x30,0x3a,0xa0,0x38,0xa0,0x36,0x86,0x34, + 0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x63,0x72,0x6c,0x33,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d, + 0x2f,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x41,0x73,0x73,0x75,0x72,0x65,0x64,0x49,0x44,0x52,0x6f,0x6f,0x74,0x43,0x41, + 0x2e,0x63,0x72,0x6c,0x30,0x11,0x06,0x03,0x55,0x1d,0x20,0x04,0x0a,0x30,0x08,0x30,0x06,0x06,0x04,0x55,0x1d,0x20,0x00,0x30, + 0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0c,0x05,0x00,0x03,0x82,0x01,0x01,0x00,0x70,0xa0,0xbf,0x43,0x5c, + 0x55,0xe7,0x38,0x5f,0xa0,0xa3,0x74,0x1b,0x3d,0xb6,0x16,0xd7,0xf7,0xbf,0x57,0x07,0xbd,0x9a,0xac,0xa1,0x87,0x2c,0xec,0x85, + 0x5e,0xa9,0x1a,0xbb,0x22,0xf8,0x87,0x1a,0x69,0x54,0x22,0xed,0xa4,0x88,0x77,0x6d,0xbd,0x1a,0x14,0xf4,0x13,0x4a,0x7a,0x2f, + 0x2d,0xb7,0x38,0xef,0xf4,0xff,0x80,0xb9,0xf8,0xa1,0xf7,0xf2,0x72,0xde,0x24,0xbc,0x52,0x03,0xc8,0x4e,0xd0,0x2a,0xde,0xfa, + 0x2d,0x56,0xcf,0xf9,0xf4,0xf7,0xac,0x30,0x7a,0x9a,0x8b,0xb2,0x5e,0xd4,0xcf,0xd1,0x43,0x44,0x9b,0x43,0x21,0xeb,0x96,0x72, + 0xa1,0x48,0xb4,0x99,0xcb,0x9d,0x4f,0xa7,0x06,0x03,0x13,0x77,0x27,0x44,0xd4,0xe7,0x7f,0xe8,0x59,0xa8,0xf0,0xbf,0x2f,0x0b, + 0xa6,0xe9,0xf2,0x34,0x3c,0xec,0xf7,0x03,0xc7,0x87,0xa8,0xd2,0x4c,0x40,0x19,0x35,0x46,0x6a,0x69,0x54,0xb0,0xb8,0xa1,0x56, + 0x8e,0xec,0xa4,0xd5,0x3d,0xe8,0xb1,0xdc,0xfd,0x1c,0xd8,0xf4,0x77,0x5a,0x5c,0x54,0x8c,0x6f,0xef,0xa1,0x50,0x3d,0xfc,0x76, + 0x09,0x68,0x84,0x9f,0x6f,0xca,0xdb,0x20,0x8d,0x35,0x60,0x1c,0x02,0x03,0xcb,0x20,0xb0,0xac,0x58,0xa0,0x0e,0x40,0x63,0xc5, + 0x98,0x22,0xc1,0xb2,0x59,0xf5,0x55,0x6b,0xcf,0x27,0xab,0x6c,0x76,0xce,0x6f,0x23,0x2d,0xf4,0x7e,0x71,0x6a,0x23,0x6b,0x22, + 0xff,0x12,0xb8,0x54,0x2d,0x27,0x7e,0xd8,0x3a,0xd9,0xf0,0xb6,0x87,0x96,0xfd,0x5b,0xd1,0x5c,0xac,0x18,0xc3,0x4d,0x9f,0x73, + 0xb7,0x01,0xa9,0x9f,0x57,0xaa,0x5e,0x28,0xe2,0xb9,0x94,0x30,0x82,0x06,0xae,0x30,0x82,0x04,0x96,0xa0,0x03,0x02,0x01,0x02, + 0x02,0x10,0x07,0x36,0x37,0xb7,0x24,0x54,0x7c,0xd8,0x47,0xac,0xfd,0x28,0x66,0x2a,0x5e,0x5b,0x30,0x0d,0x06,0x09,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x30,0x62,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53, + 0x31,0x15,0x30,0x13,0x06,0x03,0x55,0x04,0x0a,0x13,0x0c,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x49,0x6e,0x63,0x31, + 0x19,0x30,0x17,0x06,0x03,0x55,0x04,0x0b,0x13,0x10,0x77,0x77,0x77,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63, + 0x6f,0x6d,0x31,0x21,0x30,0x1f,0x06,0x03,0x55,0x04,0x03,0x13,0x18,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x54,0x72, + 0x75,0x73,0x74,0x65,0x64,0x20,0x52,0x6f,0x6f,0x74,0x20,0x47,0x34,0x30,0x1e,0x17,0x0d,0x32,0x32,0x30,0x33,0x32,0x33,0x30, + 0x30,0x30,0x30,0x30,0x30,0x5a,0x17,0x0d,0x33,0x37,0x30,0x33,0x32,0x32,0x32,0x33,0x35,0x39,0x35,0x39,0x5a,0x30,0x63,0x31, + 0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0a,0x13,0x0e,0x44, + 0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x2c,0x20,0x49,0x6e,0x63,0x2e,0x31,0x3b,0x30,0x39,0x06,0x03,0x55,0x04,0x03,0x13,0x32, + 0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x47,0x34,0x20,0x52,0x53,0x41,0x34, + 0x30,0x39,0x36,0x20,0x53,0x48,0x41,0x32,0x35,0x36,0x20,0x54,0x69,0x6d,0x65,0x53,0x74,0x61,0x6d,0x70,0x69,0x6e,0x67,0x20, + 0x43,0x41,0x30,0x82,0x02,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x03,0x82,0x02, + 0x0f,0x00,0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xc6,0x86,0x35,0x06,0x49,0xb3,0xc1,0x3d,0x72,0x49,0x51,0x55,0xc7, + 0x25,0x03,0xc4,0xf2,0x91,0x37,0xa9,0x97,0x51,0xa1,0xd6,0xd2,0x83,0xd1,0x9e,0x4c,0xa2,0x6d,0xa0,0xb0,0xcc,0x83,0xf9,0x5a, + 0xf6,0x11,0xa1,0x44,0x15,0x42,0x5f,0xa4,0x88,0xf3,0x68,0xfa,0x7d,0xf3,0x9c,0x89,0x0b,0x7f,0x9d,0x1f,0x9e,0x0f,0x33,0x1f, + 0x50,0x13,0x0b,0x26,0x73,0x96,0x6d,0xf8,0x57,0xa8,0x02,0x7d,0xfd,0x43,0xb4,0x84,0xda,0x11,0xf1,0x73,0xb1,0xb3,0xee,0x2b, + 0x80,0x84,0x8a,0x22,0x18,0xdf,0xeb,0xda,0x3d,0xc4,0x17,0x7f,0xab,0x19,0x2b,0x3e,0x42,0xdc,0x67,0x8e,0xea,0x51,0x3d,0xf0, + 0xd6,0x56,0xd4,0xe7,0x28,0x2d,0xeb,0xd3,0xb1,0xb5,0x75,0xe7,0x1f,0x06,0x65,0x8d,0x94,0x29,0xd3,0xd9,0xec,0x69,0xdf,0xd9, + 0x90,0x87,0x46,0x00,0x7b,0xdb,0x44,0x41,0x89,0xdc,0x7c,0x6a,0x57,0x7a,0xf0,0x37,0x79,0x9f,0x5d,0xac,0xcb,0xe8,0x84,0x64, + 0xb4,0x52,0xf2,0x76,0x47,0xf7,0x61,0x83,0x19,0xdd,0x5f,0xb4,0x54,0x0b,0x21,0x68,0x6e,0x37,0x21,0xbb,0x40,0xac,0x5f,0xb2, + 0xde,0x4a,0x7d,0xce,0xf5,0x39,0x12,0x67,0xef,0x0e,0xa5,0x63,0x6c,0xe4,0xa6,0xc5,0x1d,0xcd,0x36,0x0d,0x5c,0xd5,0xe6,0x1b, + 0xa8,0xc1,0x64,0x74,0x40,0xa7,0xc0,0x72,0xc5,0xba,0x4e,0x1f,0xb1,0xb5,0x58,0x4d,0x79,0xfe,0xd7,0x8f,0x73,0x93,0xac,0x2c, + 0x39,0xe2,0xa5,0x48,0xd6,0xf0,0xb0,0x31,0x13,0xa9,0x57,0x29,0x96,0x27,0x2e,0xf5,0x87,0xa6,0x8f,0x4e,0x76,0x15,0x55,0x26, + 0x70,0x98,0x26,0x7f,0xa0,0x1a,0x47,0x20,0x43,0xe3,0x43,0x63,0x80,0x7b,0x75,0x6e,0x27,0x25,0x90,0x98,0x3a,0x38,0x11,0xb3, + 0xf6,0xf6,0x9e,0xe6,0x3b,0x5b,0xec,0x81,0xde,0x22,0x14,0xd9,0x82,0x2a,0xc7,0x92,0xbf,0xa0,0xde,0xe3,0x3e,0xa2,0x73,0xfa, + 0xe7,0x1f,0x5a,0x6c,0x94,0xf2,0x52,0x95,0x11,0x2b,0x58,0x74,0x40,0x28,0xab,0x73,0x43,0xce,0xdf,0x4a,0xa1,0x1c,0x6b,0x38, + 0xc5,0x29,0xf3,0xca,0xaa,0x96,0x73,0x42,0x68,0x9f,0xb6,0x46,0xb3,0x9d,0x3a,0xa3,0xd5,0x03,0xe0,0xbf,0xf0,0xa2,0x3c,0xca, + 0x42,0xdc,0x18,0x48,0x7f,0x14,0x34,0xcf,0xd2,0x4c,0xab,0xef,0x9b,0x3d,0xfe,0x0e,0xb8,0x64,0x2a,0xfa,0x75,0x28,0x24,0x41, + 0xed,0x42,0xbf,0x05,0x9c,0x66,0x49,0x52,0x50,0xf4,0x51,0xf3,0x36,0x49,0x4d,0x8b,0x20,0xd2,0x2c,0x57,0x35,0x79,0x2b,0xa8, + 0xf3,0x45,0x60,0xbc,0x23,0x8d,0x58,0xf7,0xdc,0x61,0xde,0x93,0xfe,0x39,0xc0,0xf9,0xb2,0x30,0xa5,0x4c,0xd7,0xe9,0x98,0x4a, + 0x58,0x3e,0xd3,0x03,0x88,0xfe,0xb3,0x8f,0xd3,0x5e,0x4b,0x76,0x12,0x51,0x93,0xc9,0x8c,0x0c,0x3b,0x5b,0x8a,0x22,0xa8,0xc1, + 0x26,0x08,0xf9,0x14,0x10,0x12,0x03,0x7d,0x5f,0x23,0xbb,0x64,0xe3,0x63,0xe0,0xa6,0xe1,0x3e,0xf6,0xc2,0x74,0xb2,0x3f,0x1e, + 0x09,0x76,0xec,0xab,0x5d,0x46,0x75,0xe2,0x60,0xa3,0x58,0x09,0x01,0x28,0x00,0x0e,0x84,0x54,0xee,0xce,0xe9,0x5d,0xc8,0x5e, + 0x30,0x12,0xbd,0x46,0x9e,0xb5,0xd3,0x76,0xb9,0xd2,0x0e,0x6b,0x99,0x0c,0xd2,0x33,0xb4,0xcd,0xb1,0x02,0x03,0x01,0x00,0x01, + 0xa3,0x82,0x01,0x5d,0x30,0x82,0x01,0x59,0x30,0x12,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01, + 0xff,0x02,0x01,0x00,0x30,0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16,0x04,0x14,0xba,0x16,0xd9,0x6d,0x4d,0x85,0x2f,0x73,0x29, + 0x76,0x9a,0x2f,0x75,0x8c,0x6a,0x20,0x8f,0x9e,0xc8,0x6f,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14, + 0xec,0xd7,0xe3,0x82,0xd2,0x71,0x5d,0x64,0x4c,0xdf,0x2e,0x67,0x3f,0xe7,0xba,0x98,0xae,0x1c,0x0f,0x4f,0x30,0x0e,0x06,0x03, + 0x55,0x1d,0x0f,0x01,0x01,0xff,0x04,0x04,0x03,0x02,0x01,0x86,0x30,0x13,0x06,0x03,0x55,0x1d,0x25,0x04,0x0c,0x30,0x0a,0x06, + 0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x03,0x08,0x30,0x77,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x6b,0x30, + 0x69,0x30,0x24,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6f,0x63, + 0x73,0x70,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x30,0x41,0x06,0x08,0x2b,0x06,0x01,0x05,0x05, + 0x07,0x30,0x02,0x86,0x35,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2e,0x64,0x69,0x67,0x69, + 0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x2f,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x54,0x72,0x75,0x73,0x74,0x65,0x64, + 0x52,0x6f,0x6f,0x74,0x47,0x34,0x2e,0x63,0x72,0x74,0x30,0x43,0x06,0x03,0x55,0x1d,0x1f,0x04,0x3c,0x30,0x3a,0x30,0x38,0xa0, + 0x36,0xa0,0x34,0x86,0x32,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x63,0x72,0x6c,0x33,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72, + 0x74,0x2e,0x63,0x6f,0x6d,0x2f,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x52,0x6f,0x6f, + 0x74,0x47,0x34,0x2e,0x63,0x72,0x6c,0x30,0x20,0x06,0x03,0x55,0x1d,0x20,0x04,0x19,0x30,0x17,0x30,0x08,0x06,0x06,0x67,0x81, + 0x0c,0x01,0x04,0x02,0x30,0x0b,0x06,0x09,0x60,0x86,0x48,0x01,0x86,0xfd,0x6c,0x07,0x01,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48, + 0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x7d,0x59,0x8e,0xc0,0x93,0xb6,0x6f,0x98,0xa9,0x44,0x22, + 0x01,0x7e,0x66,0xd6,0xd8,0x21,0x42,0xe1,0xb0,0x18,0x2e,0x10,0x4d,0x13,0xcf,0x30,0x53,0xce,0xbf,0x18,0xfb,0xc7,0x50,0x5d, + 0xe2,0x4b,0x29,0xfb,0x70,0x8a,0x0d,0xaa,0x29,0x69,0xfc,0x69,0xc1,0xcf,0x1d,0x07,0xe9,0x3e,0x60,0xc8,0xd8,0x0b,0xe5,0x5c, + 0x5b,0xd7,0x6d,0x87,0xfa,0x84,0x20,0x25,0x34,0x31,0x67,0xcd,0xb6,0x12,0x96,0x6f,0xc4,0x50,0x4c,0x62,0x1d,0x0c,0x08,0x82, + 0xa8,0x16,0xbd,0xa9,0x56,0xcf,0x15,0x73,0x8d,0x01,0x22,0x25,0xce,0x95,0x69,0x3f,0x47,0x77,0xfb,0x72,0x74,0x14,0xd7,0xff, + 0xab,0x4f,0x8a,0x2c,0x7a,0xab,0x85,0xcd,0x43,0x5f,0xed,0x60,0xb6,0xaa,0x4f,0x91,0x66,0x9e,0x2c,0x9e,0xe0,0x8a,0xac,0xe5, + 0xfd,0x8c,0xbc,0x64,0x26,0x87,0x6c,0x92,0xbd,0x9d,0x7c,0xd0,0x70,0x0a,0x7c,0xef,0xa8,0xbc,0x75,0x4f,0xba,0x5a,0xf7,0xa9, + 0x10,0xb2,0x5d,0xe9,0xff,0x28,0x54,0x89,0xf0,0xd5,0x8a,0x71,0x76,0x65,0xda,0xcc,0xf0,0x72,0xa3,0x23,0xfa,0xc0,0x27,0x82, + 0x44,0xae,0x99,0x27,0x1b,0xab,0x24,0x1e,0x26,0xc1,0xb7,0xde,0x2a,0xeb,0xf6,0x9e,0xb1,0x79,0x99,0x81,0xa3,0x56,0x86,0xab, + 0x0a,0x45,0xc9,0xdf,0xc4,0x8d,0xa0,0xe7,0x98,0xfb,0xfb,0xa6,0x9d,0x72,0xaf,0xc4,0xc7,0xc1,0xc1,0x6a,0x71,0xd9,0xc6,0x13, + 0x80,0x09,0xc4,0xb6,0x9f,0xcd,0x87,0x87,0x24,0xbb,0x4f,0xa3,0x49,0xb9,0x77,0x66,0x91,0xf1,0x72,0x9c,0xe9,0x4b,0x02,0x52, + 0xa7,0x37,0x7e,0x93,0x53,0xac,0x3b,0x1d,0x08,0x49,0x0f,0x94,0xcd,0x39,0x7a,0xdd,0xff,0x25,0x63,0x99,0x27,0x2c,0x3d,0x3f, + 0x6b,0xa7,0xf1,0x66,0xc3,0x41,0xcd,0x4f,0xb6,0x40,0x9b,0x21,0x21,0x40,0xd0,0xb7,0x13,0x24,0xcd,0xdc,0x1d,0x78,0x3a,0xe4, + 0x9e,0xad,0xe5,0x34,0x71,0x92,0xd7,0x26,0x6b,0xe4,0x38,0x73,0xab,0xa6,0x01,0x4f,0xbd,0x3f,0x3b,0x78,0xad,0x4c,0xad,0xfb, + 0xc4,0x95,0x7b,0xed,0x0a,0x5f,0x33,0x39,0x87,0x41,0x78,0x7a,0x38,0xe9,0x9c,0xe1,0xdd,0x23,0xfd,0x1d,0x28,0xd3,0xc7,0xf9, + 0xe8,0xf1,0x98,0x5f,0xfb,0x2b,0xd8,0x7e,0xf2,0x46,0x9d,0x75,0x2c,0x1e,0x27,0x2c,0x26,0xdb,0x6f,0x15,0x7b,0x1e,0x19,0x8b, + 0x36,0xb8,0x93,0xd4,0xe6,0xf2,0x17,0x99,0x59,0xca,0x70,0xf0,0x37,0xbf,0x98,0x00,0xdf,0x20,0x16,0x4f,0x27,0xfb,0x60,0x67, + 0x16,0xa1,0x66,0xba,0xdd,0x55,0xc0,0x3a,0x29,0x86,0xb0,0x98,0xa0,0x2b,0xed,0x95,0x41,0xb7,0x3a,0xd5,0x15,0x98,0x31,0xb4, + 0x62,0x09,0x0f,0x0a,0xbd,0x81,0xd9,0x13,0xfe,0xbf,0xa4,0xd1,0xf3,0x57,0xd9,0xbc,0x04,0xfa,0x82,0xde,0x32,0xdf,0x04,0x89, + 0xf0,0x00,0xcd,0x5d,0xc2,0xf9,0xd0,0x23,0x7f,0x00,0x0b,0xe4,0x76,0x02,0x26,0xd9,0xf0,0x65,0x76,0x42,0xa6,0x29,0x87,0x09, + 0x47,0x2b,0xe6,0x7f,0x1a,0xa4,0x85,0x0f,0xfc,0x98,0x96,0xf6,0x55,0x54,0x2b,0x1f,0x80,0xfa,0xc0,0xf2,0x0e,0x2b,0xe5,0xd6, + 0xfb,0xa9,0x2f,0x44,0x15,0x4a,0xe7,0x13,0x0e,0x1d,0xdb,0x37,0x38,0x1a,0xa1,0x2b,0xf6,0xed,0xd6,0x7c,0xfc,0x30,0x82,0x06, + 0xc0,0x30,0x82,0x04,0xa8,0xa0,0x03,0x02,0x01,0x02,0x02,0x10,0x0c,0x4d,0x69,0x72,0x4b,0x94,0xfa,0x3c,0x2a,0x4a,0x3d,0x29, + 0x07,0x80,0x3d,0x5a,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x30,0x63,0x31,0x0b,0x30, + 0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0a,0x13,0x0e,0x44,0x69,0x67, + 0x69,0x43,0x65,0x72,0x74,0x2c,0x20,0x49,0x6e,0x63,0x2e,0x31,0x3b,0x30,0x39,0x06,0x03,0x55,0x04,0x03,0x13,0x32,0x44,0x69, + 0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x47,0x34,0x20,0x52,0x53,0x41,0x34,0x30,0x39, + 0x36,0x20,0x53,0x48,0x41,0x32,0x35,0x36,0x20,0x54,0x69,0x6d,0x65,0x53,0x74,0x61,0x6d,0x70,0x69,0x6e,0x67,0x20,0x43,0x41, + 0x30,0x1e,0x17,0x0d,0x32,0x32,0x30,0x39,0x32,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x17,0x0d,0x33,0x33,0x31,0x31,0x32, + 0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5a,0x30,0x46,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31, + 0x11,0x30,0x0f,0x06,0x03,0x55,0x04,0x0a,0x13,0x08,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x31,0x24,0x30,0x22,0x06,0x03, + 0x55,0x04,0x03,0x13,0x1b,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74,0x20,0x54,0x69,0x6d,0x65,0x73,0x74,0x61,0x6d,0x70,0x20, + 0x32,0x30,0x32,0x32,0x20,0x2d,0x20,0x32,0x30,0x82,0x02,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01, + 0x01,0x05,0x00,0x03,0x82,0x02,0x0f,0x00,0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xcf,0xec,0xa5,0x26,0x3a,0xc6,0xa9, + 0xf2,0x6b,0xbb,0x8d,0xc1,0x0d,0x9a,0xdb,0xa1,0xe8,0x14,0x85,0x74,0x33,0x1a,0x26,0xac,0xd0,0x1c,0x55,0x1e,0x1e,0x36,0x6d, + 0xbc,0x92,0x55,0x0c,0x61,0xf4,0x9d,0x09,0x77,0x3d,0x15,0x96,0x08,0x2f,0x6b,0x64,0xa4,0xfd,0x06,0x83,0x16,0xd7,0x91,0x92, + 0x38,0x1c,0x31,0x02,0x96,0xfb,0x72,0xb1,0x97,0x3a,0x55,0xaf,0x33,0xec,0x61,0x8a,0xe9,0xa6,0x28,0xdb,0x90,0x63,0x5c,0xbd, + 0x89,0x53,0xe0,0x3a,0x2d,0x8c,0x87,0x42,0xae,0x26,0xa4,0xe4,0xbb,0x78,0x78,0xb9,0x7a,0x16,0xe1,0x56,0xc6,0xc0,0xba,0x64, + 0x53,0xbb,0x2a,0x16,0xe7,0x50,0x48,0xbb,0x88,0x69,0x0c,0x88,0xc6,0xf1,0xbe,0xe0,0x2f,0x7d,0x3b,0xb1,0xca,0x53,0x8d,0x40, + 0x83,0x1e,0xe7,0xcb,0x72,0x49,0x28,0x1e,0x4c,0x80,0x1e,0x85,0x56,0xe7,0x85,0xed,0xf2,0x61,0xbc,0xaa,0x3a,0x07,0x7d,0xf6, + 0xab,0x6e,0xe5,0x66,0xdd,0xe2,0x5c,0xf5,0x2f,0xed,0x8d,0xd4,0x4d,0x95,0x84,0x68,0xe3,0x80,0xcb,0x6a,0x79,0xd1,0xd2,0x10, + 0x91,0x46,0x29,0xeb,0x3e,0x26,0xf2,0xb4,0x8c,0xcd,0x4c,0xb9,0x66,0xc8,0xbb,0xaa,0x50,0x38,0x0d,0xe5,0x8c,0x94,0x5d,0x19, + 0x5a,0xbf,0xf5,0x7b,0x40,0x6e,0x6f,0x16,0xa8,0x9a,0x9c,0x95,0x47,0x86,0x85,0x79,0x3e,0x0c,0x5e,0x66,0x8c,0x1a,0x0a,0x24, + 0xbe,0x9c,0xaa,0xd2,0x9c,0xb6,0xf7,0x4f,0x6e,0x78,0xc4,0x28,0x3f,0xa3,0x1c,0x0f,0x50,0x06,0x37,0xba,0x08,0xd9,0x35,0xa6, + 0xb5,0x1e,0xda,0x78,0x58,0x1d,0x39,0xe8,0xf8,0x4c,0x91,0x10,0x96,0x7e,0x4d,0xe1,0xdd,0xc2,0xad,0xa5,0x7e,0xf8,0x2d,0x1b, + 0x1f,0xec,0x2b,0x46,0x18,0xa3,0x19,0xf6,0x39,0xf7,0xf5,0xc1,0x4f,0x71,0x2e,0x89,0x03,0x11,0xa2,0x4b,0xbb,0x98,0xbf,0xfa, + 0x4f,0xe4,0x7b,0x36,0xef,0x06,0x44,0xe4,0x55,0xff,0x36,0xea,0xe5,0x7c,0x31,0xe7,0xf3,0xc2,0x52,0xc4,0xe6,0x16,0x7b,0x5a, + 0x7e,0xa5,0x25,0x73,0xdb,0xc0,0x6a,0x99,0x21,0x2d,0x63,0xe5,0x59,0xf5,0x4d,0x2f,0x90,0x1f,0x27,0xb7,0xd2,0xab,0x14,0xe5, + 0x38,0x66,0x87,0x51,0x08,0x6b,0xfb,0x53,0x43,0x39,0xd0,0x64,0xfa,0x56,0xcf,0xe0,0xf4,0x0a,0xe6,0x14,0x6d,0x64,0x78,0xbb, + 0x98,0xfd,0x94,0xc3,0x73,0x21,0xf3,0x2f,0xc2,0x2e,0x20,0xd7,0x81,0xac,0xd3,0xf1,0x07,0xd4,0xe1,0xbd,0xd9,0x5d,0x4b,0x6e, + 0x31,0x94,0x29,0x8b,0xe6,0x41,0xa4,0x65,0x94,0xc0,0x58,0xe5,0xe5,0x2e,0x29,0x90,0xa6,0xb7,0x61,0x64,0xfa,0xd9,0x20,0x6c, + 0x18,0x51,0x60,0xba,0xa6,0x81,0x0f,0x09,0x25,0x53,0xf1,0xbf,0x3b,0xe9,0xab,0x07,0x0e,0x6a,0x07,0x39,0x62,0x19,0xc9,0xd6, + 0x85,0x7f,0x13,0xd9,0x8d,0x79,0xcf,0x62,0xc5,0xec,0xe1,0x7b,0xb9,0xcc,0x67,0x13,0x07,0x9a,0xc1,0x78,0xed,0xc6,0x88,0xc8, + 0xb0,0x6e,0x32,0x79,0xc7,0x0b,0x59,0x83,0x8d,0xc6,0xee,0xf5,0x2c,0x7c,0x7b,0x8e,0xcb,0x64,0x89,0xf1,0xb1,0xc4,0xb8,0xe7, + 0x53,0x5e,0x5f,0x55,0xd2,0x7d,0x19,0x29,0x59,0x03,0x4e,0xfa,0x5d,0xea,0x45,0x73,0x1c,0x84,0x7e,0xd7,0xce,0xe2,0xd4,0x3a, + 0x77,0x02,0x03,0x01,0x00,0x01,0xa3,0x82,0x01,0x8b,0x30,0x82,0x01,0x87,0x30,0x0e,0x06,0x03,0x55,0x1d,0x0f,0x01,0x01,0xff, + 0x04,0x04,0x03,0x02,0x07,0x80,0x30,0x0c,0x06,0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x16,0x06,0x03, + 0x55,0x1d,0x25,0x01,0x01,0xff,0x04,0x0c,0x30,0x0a,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x03,0x08,0x30,0x20,0x06,0x03, + 0x55,0x1d,0x20,0x04,0x19,0x30,0x17,0x30,0x08,0x06,0x06,0x67,0x81,0x0c,0x01,0x04,0x02,0x30,0x0b,0x06,0x09,0x60,0x86,0x48, + 0x01,0x86,0xfd,0x6c,0x07,0x01,0x30,0x1f,0x06,0x03,0x55,0x1d,0x23,0x04,0x18,0x30,0x16,0x80,0x14,0xba,0x16,0xd9,0x6d,0x4d, + 0x85,0x2f,0x73,0x29,0x76,0x9a,0x2f,0x75,0x8c,0x6a,0x20,0x8f,0x9e,0xc8,0x6f,0x30,0x1d,0x06,0x03,0x55,0x1d,0x0e,0x04,0x16, + 0x04,0x14,0x62,0x8a,0xde,0xd0,0x61,0xfc,0x8f,0x31,0x14,0xed,0x97,0x0b,0xcd,0x3d,0x2a,0x94,0x14,0xdf,0x52,0x9c,0x30,0x5a, + 0x06,0x03,0x55,0x1d,0x1f,0x04,0x53,0x30,0x51,0x30,0x4f,0xa0,0x4d,0xa0,0x4b,0x86,0x49,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f, + 0x63,0x72,0x6c,0x33,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x2f,0x44,0x69,0x67,0x69,0x43,0x65, + 0x72,0x74,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x47,0x34,0x52,0x53,0x41,0x34,0x30,0x39,0x36,0x53,0x48,0x41,0x32,0x35,0x36, + 0x54,0x69,0x6d,0x65,0x53,0x74,0x61,0x6d,0x70,0x69,0x6e,0x67,0x43,0x41,0x2e,0x63,0x72,0x6c,0x30,0x81,0x90,0x06,0x08,0x2b, + 0x06,0x01,0x05,0x05,0x07,0x01,0x01,0x04,0x81,0x83,0x30,0x81,0x80,0x30,0x24,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30, + 0x01,0x86,0x18,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f,0x6f,0x63,0x73,0x70,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e, + 0x63,0x6f,0x6d,0x30,0x58,0x06,0x08,0x2b,0x06,0x01,0x05,0x05,0x07,0x30,0x02,0x86,0x4c,0x68,0x74,0x74,0x70,0x3a,0x2f,0x2f, + 0x63,0x61,0x63,0x65,0x72,0x74,0x73,0x2e,0x64,0x69,0x67,0x69,0x63,0x65,0x72,0x74,0x2e,0x63,0x6f,0x6d,0x2f,0x44,0x69,0x67, + 0x69,0x43,0x65,0x72,0x74,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x47,0x34,0x52,0x53,0x41,0x34,0x30,0x39,0x36,0x53,0x48,0x41, + 0x32,0x35,0x36,0x54,0x69,0x6d,0x65,0x53,0x74,0x61,0x6d,0x70,0x69,0x6e,0x67,0x43,0x41,0x2e,0x63,0x72,0x74,0x30,0x0d,0x06, + 0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x02,0x01,0x00,0x55,0xaa,0x2a,0x1a,0xf3,0x46,0xf3, + 0x78,0x57,0x37,0x30,0xfc,0x75,0xe3,0x4f,0xd6,0x85,0x23,0xf1,0xfc,0xf9,0x95,0x39,0x9b,0x25,0xe6,0xf7,0x72,0x8a,0x98,0xc3, + 0x77,0xd4,0x64,0xfc,0x15,0xfb,0x36,0xc2,0x49,0x51,0x2c,0x78,0x88,0x63,0x55,0x09,0x46,0x39,0x00,0xfc,0x69,0xd4,0xca,0x9b, + 0x29,0xfb,0xa3,0x3f,0xc0,0xc9,0x00,0x9b,0x13,0x1d,0xb0,0x98,0x89,0xdc,0x78,0xf2,0xcd,0x7c,0x85,0xcd,0x53,0x9d,0xaf,0x62, + 0xe2,0x61,0x66,0xa3,0x14,0x2a,0x45,0x87,0x4a,0x98,0x42,0x2b,0x50,0xfc,0x1b,0xb5,0x9e,0x08,0x30,0x09,0xfa,0xe4,0x2d,0xd7, + 0x09,0x89,0x79,0xf9,0x09,0xe6,0x88,0xce,0x7d,0x1b,0xb8,0x6a,0xa2,0x9b,0xc1,0x53,0x60,0x09,0xe8,0xa3,0xb8,0x9d,0xd7,0xad, + 0x1f,0x1c,0xb8,0xec,0x98,0x41,0xf0,0xf6,0x0e,0x80,0xfb,0xe4,0xff,0xdf,0x9d,0x10,0xa7,0xeb,0x00,0xba,0x5f,0x4a,0x8f,0x1a, + 0x3a,0x52,0xb4,0xea,0xbf,0x09,0x49,0x15,0x35,0x36,0x59,0x9a,0x0f,0x54,0xd2,0xb2,0x1b,0x7f,0x7e,0x5e,0x09,0xad,0x76,0x54, + 0x8a,0x74,0x6d,0xca,0xd2,0x05,0x67,0x2b,0x76,0xeb,0xff,0x98,0xb2,0x26,0x95,0x38,0x19,0x88,0x44,0x14,0xe5,0x0a,0x59,0xa2, + 0x6b,0xe7,0x22,0x3e,0x44,0x21,0xd2,0x3f,0x1c,0xc0,0x9b,0xed,0x7c,0x48,0xb2,0xd8,0x92,0x0c,0x91,0x4f,0x3c,0x66,0x94,0xaf, + 0x5d,0x02,0x53,0xeb,0x9e,0xe2,0x9e,0xe4,0xd3,0x1f,0x86,0x01,0x64,0x9c,0x00,0xc2,0xe9,0x5a,0x74,0x75,0x0d,0x3d,0xe1,0x79, + 0x88,0xbf,0x1c,0x01,0x97,0xc9,0x19,0x23,0x80,0xd7,0x36,0x5a,0x5f,0x96,0x16,0xb1,0x63,0x0c,0xc6,0x46,0x40,0x3b,0xce,0x5d, + 0x35,0xd4,0x59,0x3e,0x43,0x9a,0x18,0xae,0xc3,0xc9,0xcb,0xc3,0xfb,0x9b,0x13,0x5f,0x6a,0xb5,0xc7,0xe0,0xf3,0x05,0xc3,0x59, + 0xdf,0x27,0x62,0x2b,0xde,0x41,0xc9,0x53,0xb9,0xff,0x34,0x10,0x67,0xf6,0x26,0x32,0x98,0x7b,0xfe,0x5c,0x42,0x94,0x81,0x94, + 0x82,0x9d,0xac,0x0a,0x8b,0xc6,0x4b,0x15,0x4a,0xd3,0x98,0x90,0x45,0x60,0x33,0x80,0xe0,0x23,0xde,0xf8,0x03,0xa4,0xf6,0x45, + 0x47,0xe5,0xce,0xb8,0x03,0x42,0x47,0xe8,0x41,0x36,0x71,0x77,0xad,0xfd,0xa2,0xe8,0x97,0x74,0x4e,0x2e,0xda,0x1e,0x1d,0x8c, + 0x5a,0xc8,0x1e,0x9a,0xd5,0xc2,0xf0,0xc6,0x22,0xa8,0x4f,0x9b,0xbd,0xd8,0x1c,0x9a,0x51,0xc4,0x2f,0x9a,0xf6,0x5f,0xa7,0x27, + 0x97,0xba,0x96,0x2e,0x85,0x57,0xc0,0x60,0xe7,0x78,0x56,0x7f,0x6a,0xef,0xc2,0x95,0x9a,0x4b,0x11,0x02,0xc8,0x82,0x9c,0xc9, + 0x1a,0x05,0x7c,0xba,0x71,0xb5,0x4e,0x7a,0x99,0x6c,0xf4,0xe8,0x9e,0xd4,0x5a,0x98,0xc8,0x9f,0xbf,0x8d,0xbb,0x18,0x5c,0x43, + 0xf5,0xd0,0x2a,0xe8,0xe2,0x62,0xee,0x78,0x04,0xdb,0xbd,0xd1,0xfb,0x5b,0x0a,0xa8,0x70,0x7e,0xf0,0x97,0x84,0x78,0xe3,0x08, + 0x03,0x5d,0x47,0x2c,0x63,0xa8,0x25,0x38,0x97,0x01,0xd2,0x3f,0x3a,0xda,0xe5,0xe5,0xf6,0xe6,0x9b,0xdc,0x7e,0x2c,0xcc,0xff, + 0x17,0x4c,0x4d,0x00,0xa2,0xd8,0xd6,0x01,0x0e,0xb8,0x8b,0xee,0xe6,0xe0,0x72,0x55,0x89,0x2c,0x27,0x19,0x61,0xf6,0x77,0x01, + 0x8c,0x31,0x82,0x0f,0x9d,0x30,0x82,0x0f,0x99,0x02,0x01,0x01,0x30,0x26,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04, + 0x03,0x13,0x07,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x02,0x10,0xd1,0x73,0x97,0xaa,0xa7,0x3a,0x31,0xa2,0x44,0xc0,0x4b,0x40, + 0x69,0x40,0x4b,0xfa,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0xa0,0x5e,0x30,0x10,0x06, + 0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0c,0x31,0x02,0x30,0x00,0x30,0x19,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7, + 0x0d,0x01,0x09,0x03,0x31,0x0c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x04,0x30,0x2f,0x06,0x09,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x01,0x09,0x04,0x31,0x22,0x04,0x20,0xcb,0xa1,0x2f,0x7b,0x0f,0x6a,0x14,0x06,0x26,0x77,0x7b,0xee,0xd1, + 0x90,0x34,0x1c,0xa7,0xcd,0x94,0x65,0xe0,0xe4,0x24,0x12,0x0c,0x4e,0xa2,0x89,0xeb,0x2d,0xe5,0xbb,0x30,0x0d,0x06,0x09,0x2a, + 0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x04,0x82,0x01,0x00,0x73,0xb7,0xdf,0xb4,0xd2,0x60,0x31,0x2d,0x14,0x63, + 0x71,0x87,0x5b,0xd9,0xee,0x66,0x85,0x88,0xbe,0x1e,0xc3,0x5e,0x30,0x25,0x62,0x05,0x66,0x6e,0x9b,0x51,0xaa,0x5b,0xee,0xed, + 0xe4,0x4c,0x83,0x19,0x7e,0x7b,0x78,0xe0,0x04,0xb5,0xdb,0xb3,0x48,0xba,0xa3,0xa4,0x54,0xb2,0x3e,0xe4,0x99,0xf1,0xb2,0x6e, + 0xb7,0x7a,0x44,0x76,0x11,0xb4,0x51,0xae,0xe3,0xec,0xc2,0x43,0x65,0xf5,0x95,0x1c,0x57,0x9e,0x4d,0x49,0x4d,0x4c,0xee,0x09, + 0xdd,0xdb,0xab,0xf3,0x14,0x89,0xec,0x6e,0x94,0xd2,0xac,0xd8,0xed,0xe3,0xbb,0x8a,0xf0,0x05,0x9d,0x1d,0xbe,0x8b,0x7f,0x34, + 0x63,0xe7,0x87,0x04,0x25,0x5a,0xff,0xc9,0xca,0xa9,0xab,0xd5,0xf2,0x16,0xf6,0x26,0x5d,0xf9,0xf9,0xbf,0xb4,0xce,0x86,0x55, + 0xd4,0x95,0x91,0x1e,0x12,0x25,0x36,0x43,0x37,0x6e,0x93,0x14,0xf0,0x86,0xad,0xc6,0x7d,0x07,0x86,0xe5,0x18,0x4f,0x3d,0xe3, + 0x92,0x67,0x7b,0x74,0xbf,0xa9,0x71,0x5a,0x49,0xcf,0xf5,0x60,0xf4,0x09,0x65,0x38,0xfd,0x13,0xe2,0x03,0x8a,0x84,0x17,0xc7, + 0x83,0x7a,0xd5,0x42,0x95,0x47,0xd8,0x9d,0x76,0x52,0xb5,0xbc,0x11,0x63,0x78,0x53,0x66,0x0e,0x95,0xc8,0xd4,0xfa,0x6e,0x3d, + 0x3b,0x1b,0x56,0xf2,0x98,0xc3,0x4c,0xc8,0xdc,0x1f,0x7e,0xa3,0x8b,0x1a,0x3b,0xa6,0x4d,0xed,0x70,0x75,0x55,0xcd,0x0c,0xf9, + 0x4e,0xfb,0xa6,0x84,0xcb,0xbc,0xf5,0x28,0x89,0x2e,0x30,0x07,0x8f,0x1d,0x8d,0x10,0x03,0x20,0xe9,0xb0,0x56,0x53,0x9d,0xf9, + 0x43,0xfb,0xba,0xb7,0x25,0x5d,0xa1,0x82,0x0d,0xe8,0x30,0x82,0x03,0x1c,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09, + 0x06,0x31,0x82,0x03,0x0d,0x30,0x82,0x03,0x09,0x02,0x01,0x01,0x30,0x77,0x30,0x63,0x31,0x0b,0x30,0x09,0x06,0x03,0x55,0x04, + 0x06,0x13,0x02,0x55,0x53,0x31,0x17,0x30,0x15,0x06,0x03,0x55,0x04,0x0a,0x13,0x0e,0x44,0x69,0x67,0x69,0x43,0x65,0x72,0x74, + 0x2c,0x20,0x49,0x6e,0x63,0x2e,0x31,0x3b,0x30,0x39,0x06,0x03,0x55,0x04,0x03,0x13,0x32,0x44,0x69,0x67,0x69,0x43,0x65,0x72, + 0x74,0x20,0x54,0x72,0x75,0x73,0x74,0x65,0x64,0x20,0x47,0x34,0x20,0x52,0x53,0x41,0x34,0x30,0x39,0x36,0x20,0x53,0x48,0x41, + 0x32,0x35,0x36,0x20,0x54,0x69,0x6d,0x65,0x53,0x74,0x61,0x6d,0x70,0x69,0x6e,0x67,0x20,0x43,0x41,0x02,0x10,0x0c,0x4d,0x69, + 0x72,0x4b,0x94,0xfa,0x3c,0x2a,0x4a,0x3d,0x29,0x07,0x80,0x3d,0x5a,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04, + 0x02,0x01,0x05,0x00,0xa0,0x69,0x30,0x18,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x03,0x31,0x0b,0x06,0x09,0x2a, + 0x86,0x48,0x86,0xf7,0x0d,0x01,0x07,0x01,0x30,0x1c,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x05,0x31,0x0f,0x17, + 0x0d,0x32,0x32,0x30,0x39,0x33,0x30,0x31,0x37,0x35,0x33,0x34,0x33,0x5a,0x30,0x2f,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d, + 0x01,0x09,0x04,0x31,0x22,0x04,0x20,0xbf,0x5c,0x03,0xc6,0x50,0xb0,0xde,0xd1,0x96,0x6d,0x74,0x64,0xa4,0xda,0x0f,0x51,0x71, + 0x0f,0x5a,0x87,0x97,0x78,0x2e,0x17,0x99,0xc6,0xa2,0x7b,0xa7,0x9b,0x75,0xeb,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7, + 0x0d,0x01,0x01,0x01,0x05,0x00,0x04,0x82,0x02,0x00,0x0e,0xb2,0xbf,0xf0,0xf2,0x35,0x55,0xc7,0x9d,0x49,0xce,0x0a,0xac,0x3a, + 0x6a,0xd8,0x85,0xa0,0x2b,0x81,0x97,0x85,0xc2,0x16,0x4d,0x6d,0x77,0x72,0xdf,0x2e,0x0a,0x6b,0x2e,0xbd,0xd6,0xb4,0x66,0x22, + 0x7b,0x43,0x1b,0x7e,0x7a,0x1b,0xdb,0x72,0xfb,0x1d,0xd5,0xe9,0x84,0x01,0xe1,0x64,0x15,0x05,0x0d,0xb5,0x85,0x1d,0x93,0xf0, + 0xcf,0x72,0x77,0x07,0x30,0x82,0xa9,0x6e,0x9c,0x5d,0xc9,0x39,0xda,0x19,0x9b,0xca,0x34,0x05,0xf0,0xe4,0xd7,0x02,0xbe,0x8a, + 0x5f,0x74,0x39,0xe6,0xe9,0xb4,0xdf,0x00,0x4a,0xeb,0xb4,0x0d,0xf6,0xb2,0x5b,0x7f,0x10,0xb1,0xef,0x05,0x53,0xfc,0x74,0x41, + 0x5b,0x83,0xeb,0xf9,0x37,0x9f,0x03,0xc2,0x1a,0x98,0x13,0xc9,0x6c,0x1c,0xd8,0x82,0xd8,0xd7,0xbf,0x13,0x07,0x20,0xf9,0xf2, + 0xea,0x96,0x46,0xb7,0x2d,0x51,0x2f,0xc3,0x40,0x12,0x3f,0x35,0xeb,0x88,0xfa,0x3c,0x66,0x30,0xd9,0x16,0xe2,0x4b,0x05,0x14, + 0x11,0x07,0x9a,0x64,0xc5,0x92,0x01,0xec,0xdb,0x5d,0x01,0x91,0x08,0x4f,0x1e,0xec,0x12,0xdd,0x7d,0xa0,0x32,0x5a,0x84,0x2a, + 0x7c,0xb9,0x25,0x34,0x57,0x12,0x0c,0x86,0x2d,0xcd,0x01,0x26,0x85,0x17,0x8c,0x7d,0x07,0x3f,0x2c,0x08,0xb6,0xac,0xc3,0xca, + 0x72,0x17,0xfe,0x4c,0xef,0xbb,0x69,0x20,0x11,0x33,0x63,0xf8,0xca,0x0a,0xe1,0x8c,0xf5,0x31,0x4e,0x83,0x87,0xfb,0xa9,0xd2, + 0x29,0x4b,0x82,0x84,0x27,0xfa,0x01,0x31,0x48,0xc1,0x25,0xe5,0x51,0x49,0xf5,0x52,0x60,0xdb,0x64,0x08,0x36,0xd2,0x2f,0x4e, + 0x7a,0x5d,0x92,0xee,0x06,0xec,0xe4,0x3b,0x5d,0xdc,0xc3,0x49,0xa1,0x8e,0x96,0xa6,0x17,0x00,0x2d,0x0d,0xee,0x6e,0x88,0x16, + 0x1e,0xea,0x9c,0x9b,0x55,0x9d,0xea,0x4a,0xa5,0xbb,0x54,0x33,0xaa,0x72,0x8c,0x5f,0xb7,0xe6,0x22,0xe9,0x1d,0xca,0xbc,0x6d, + 0xdf,0x7b,0x68,0xb5,0x71,0x3b,0x9f,0xfd,0x2b,0x53,0x35,0xa6,0xbb,0x78,0xf2,0x5a,0x41,0x69,0x52,0x5c,0x47,0xdc,0xf0,0x3e, + 0x06,0xef,0x60,0xb6,0xd3,0xb1,0x30,0xda,0x41,0xee,0x5a,0x2e,0x8e,0x24,0x0e,0x1e,0xcd,0x6c,0xa6,0x2e,0x9b,0x3c,0x77,0x02, + 0x21,0x86,0x16,0x68,0xfc,0xd6,0x64,0x84,0x46,0x1e,0xd1,0xe6,0x86,0x6f,0xee,0x5f,0x5c,0xca,0xb6,0xc9,0x9b,0xb8,0x03,0x46, + 0x1b,0x67,0x52,0x48,0x48,0x1c,0x07,0xad,0xbd,0x3c,0x2f,0x1b,0xd1,0xe3,0xb5,0xba,0x21,0x20,0x3b,0x6d,0xfb,0x30,0xa6,0x6d, + 0x2d,0xd7,0xb0,0x67,0xa4,0x19,0x0f,0x38,0xff,0xb0,0xad,0xf0,0xc3,0xb3,0xd7,0x7c,0x92,0xa0,0xe8,0x9c,0x22,0x60,0x0f,0x88, + 0x08,0xa7,0xf0,0xfa,0x90,0x45,0x2c,0x26,0xa6,0x88,0x25,0x24,0x7b,0x69,0x23,0x0b,0x20,0x04,0x89,0xd4,0x66,0x89,0x7d,0xb6, + 0x50,0x0e,0xb3,0xfe,0xf9,0xd0,0x91,0x2b,0x1d,0x17,0x62,0x6d,0xad,0x8e,0xf9,0x52,0x63,0x8f,0xe9,0x93,0xe6,0xea,0xb8,0xe6, + 0xce,0x0c,0x69,0xc9,0x47,0xd7,0x7e,0xb4,0x0d,0x49,0xbe,0xcb,0x05,0xe4,0xac,0x0b,0xd4,0xa1,0x6b,0x66,0x0f,0xff,0xe7,0x01, + 0x5a,0x91,0x05,0xa8,0xc0,0x2b,0xda,0xe9,0x5c,0x18,0xfb,0xe3,0x4d,0x60,0x0f,0x34,0x19,0x68,0x30,0x82,0x0a,0xc4,0x06,0x0a, + 0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x04,0x01,0x31,0x82,0x0a,0xb4,0x30,0x82,0x05,0x56,0x06,0x09,0x2a,0x86,0x48,0x86, + 0xf7,0x0d,0x01,0x07,0x02,0xa0,0x82,0x05,0x47,0x30,0x82,0x05,0x43,0x02,0x01,0x01,0x31,0x0f,0x30,0x0d,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x30,0x5c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x04,0xa0, + 0x4e,0x30,0x4c,0x30,0x17,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0f,0x30,0x09,0x03,0x01,0x00,0xa0,0x04, + 0xa2,0x02,0x80,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20,0xdd, + 0x8b,0xd7,0x29,0x3b,0xae,0x16,0xec,0xbb,0x81,0x80,0x55,0x15,0xd8,0x87,0xa5,0x3e,0xeb,0x0b,0x74,0x59,0xb6,0x56,0xf1,0x0b, + 0x2e,0xe1,0xb4,0x42,0x4d,0x8b,0x18,0xa0,0x82,0x03,0x05,0x30,0x82,0x03,0x01,0x30,0x82,0x01,0xe9,0xa0,0x03,0x02,0x01,0x02, + 0x02,0x10,0x8d,0x01,0xec,0xa9,0x68,0x41,0x93,0x8f,0x40,0x42,0x93,0x4a,0x72,0x6b,0x03,0xcc,0x30,0x0d,0x06,0x09,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04,0x03,0x13,0x07,0x54,0x65, + 0x73,0x74,0x20,0x43,0x41,0x30,0x1e,0x17,0x0d,0x32,0x32,0x30,0x39,0x33,0x30,0x31,0x37,0x32,0x34,0x31,0x39,0x5a,0x17,0x0d, + 0x33,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5a,0x30,0x10,0x31,0x0e,0x30,0x0c,0x06,0x03,0x55,0x04,0x03, + 0x13,0x05,0x63,0x65,0x72,0x74,0x33,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01, + 0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xf4,0x21,0x90,0xec,0x4a,0x19,0xca,0xa7, + 0x1f,0x25,0x80,0x0b,0x92,0x21,0x2d,0x53,0xb4,0x25,0xa3,0x37,0x40,0xae,0xce,0xf1,0x46,0xf6,0xda,0x21,0xd3,0x54,0x8e,0x02, + 0x95,0xfe,0x37,0x3a,0xff,0xb9,0x36,0x88,0x70,0xf2,0x33,0x0c,0x5c,0x60,0x36,0xbf,0x8d,0x51,0xef,0x18,0x5d,0x47,0x68,0xbc, + 0xda,0xd8,0xf8,0x94,0xaf,0xcf,0xa6,0x48,0x61,0x78,0x04,0xcd,0x73,0x03,0xa0,0x6c,0xdb,0x24,0x94,0x20,0x54,0xbb,0x19,0xf2, + 0xb6,0xf9,0x0d,0xf4,0x31,0xbd,0x79,0xb5,0x98,0x12,0xc4,0x62,0x06,0xfb,0x16,0x08,0x51,0xa2,0xbf,0x12,0xcf,0x88,0xf4,0x4c, + 0x26,0xec,0x80,0xc7,0xa7,0xeb,0x89,0x24,0x6a,0xe4,0x1e,0x3a,0x1d,0x4a,0x7c,0x42,0xaa,0x03,0x5e,0x47,0xde,0x21,0xc4,0x06, + 0x86,0x57,0x0d,0xce,0xe0,0x27,0xf2,0x43,0x56,0x2a,0x60,0x1b,0x18,0xf5,0x03,0x91,0xa4,0xda,0x86,0x1c,0xcb,0x2c,0x2d,0x21, + 0x54,0xc5,0xd8,0xe2,0xe2,0x02,0x19,0xb0,0x73,0xe0,0xf8,0x95,0x5c,0x4f,0x1d,0x8f,0xd2,0xe0,0x4a,0x7c,0x20,0x9e,0x29,0xec, + 0x05,0x34,0xb0,0x4c,0x1a,0xaf,0x2a,0x01,0xf1,0x0f,0x26,0xdc,0x38,0x68,0x85,0xbd,0xc0,0x73,0xec,0x10,0x17,0x3a,0xd8,0x6d, + 0x33,0x3b,0xab,0xd3,0x79,0x61,0x2c,0xac,0x11,0xe1,0x09,0x5e,0xe0,0x7b,0xd3,0xc0,0xb8,0xf8,0xb5,0x9a,0x20,0x68,0xa7,0xf0, + 0x66,0x9b,0xc2,0xe8,0x83,0x14,0x7d,0x3c,0x7f,0x7a,0x85,0x95,0xc9,0x05,0x74,0xf4,0xee,0xa9,0x83,0x6a,0x55,0xa5,0x78,0xa8, + 0xa8,0xb4,0x6d,0xb1,0xab,0x49,0x50,0xd1,0x02,0x03,0x01,0x00,0x01,0xa3,0x55,0x30,0x53,0x30,0x0c,0x06,0x03,0x55,0x1d,0x13, + 0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x43,0x06,0x03,0x55,0x1d,0x01,0x04,0x3c,0x30,0x3a,0x80,0x10,0x88,0x17,0xf7,0x38, + 0x65,0x8b,0x78,0x78,0xf6,0x77,0xe3,0x25,0x47,0x54,0x33,0x4c,0xa1,0x14,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04, + 0x03,0x13,0x07,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x82,0x10,0x2b,0x59,0xb4,0xc7,0xe2,0xce,0x08,0x97,0x46,0x48,0x32,0x17, + 0x0f,0x97,0xc5,0x08,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01,0x01,0x00, + 0x8e,0xe0,0xc9,0x58,0x24,0x1b,0x16,0x6a,0x8a,0x4f,0x67,0xac,0xd7,0x75,0x62,0x53,0x94,0xfc,0xeb,0x8b,0x36,0x2a,0x9f,0x9c, + 0x8b,0x8f,0x60,0x42,0xdd,0x37,0x13,0x10,0x5f,0x5a,0x52,0xc8,0xee,0x51,0x92,0x18,0xaf,0x84,0x18,0x5f,0x27,0x69,0xf4,0xde, + 0x22,0x4b,0x9c,0xaa,0x18,0x9e,0xde,0x04,0xc0,0xc4,0xfd,0x74,0x08,0x25,0x43,0xbf,0x00,0x1d,0xc2,0xd6,0xb2,0x4e,0xa4,0x4a, + 0x73,0xa1,0xff,0x71,0x3d,0xa5,0xf1,0x21,0xcf,0x4d,0xb4,0x5c,0x55,0x54,0x6f,0x94,0x50,0x21,0xbb,0x85,0xcb,0x54,0xeb,0x07, + 0xaf,0x74,0x62,0x21,0xf5,0x89,0x43,0xcb,0x10,0x62,0xd6,0xbe,0xc0,0x3a,0xb3,0x6b,0x9f,0x80,0xde,0xe0,0xc0,0x6e,0x8a,0x0a, + 0xe7,0x1f,0x08,0x9b,0x89,0x38,0xc2,0x30,0xfa,0xd9,0xc2,0x8c,0xf7,0xbd,0xbd,0xd4,0x6b,0x99,0xbd,0x5f,0x0e,0xb1,0x76,0xd6, + 0x5b,0x1f,0x1a,0xd7,0x27,0x5d,0x5b,0x19,0x1c,0x6d,0x5a,0x91,0x81,0x06,0x83,0x82,0x6d,0xaf,0x48,0x70,0x72,0x8b,0x7c,0x8e, + 0x57,0xcd,0x35,0x5d,0x7d,0x96,0xe5,0x2d,0x31,0xd2,0xa9,0xf8,0xad,0x9d,0x13,0xb4,0x89,0x75,0x7e,0xbc,0x39,0x11,0x27,0x9d, + 0xc3,0x7b,0xf1,0x40,0x2a,0x23,0xed,0x50,0xee,0x10,0x1b,0x97,0x13,0x71,0x1c,0x5a,0x7f,0x06,0xab,0x9b,0x51,0x2b,0x1f,0x85, + 0x27,0x99,0x11,0x18,0x62,0x97,0xcb,0x08,0x24,0x6d,0xf5,0x08,0x56,0xb5,0x36,0x00,0xf3,0x6b,0x3d,0xa9,0x13,0xd3,0xcf,0x0c, + 0x94,0x6f,0x9e,0x05,0x62,0xeb,0x9c,0x52,0x9d,0x0c,0x15,0x81,0x4d,0x7d,0xe3,0x2f,0x31,0x82,0x01,0xc4,0x30,0x82,0x01,0xc0, + 0x02,0x01,0x01,0x30,0x26,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04,0x03,0x13,0x07,0x54,0x65,0x73,0x74,0x20,0x43, + 0x41,0x02,0x10,0x8d,0x01,0xec,0xa9,0x68,0x41,0x93,0x8f,0x40,0x42,0x93,0x4a,0x72,0x6b,0x03,0xcc,0x30,0x0d,0x06,0x09,0x60, + 0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0xa0,0x71,0x30,0x10,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02, + 0x01,0x0c,0x31,0x02,0x30,0x00,0x30,0x11,0x06,0x0a,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x19,0x04,0x31,0x03,0x02,0x01, + 0x02,0x30,0x19,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x03,0x31,0x0c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82, + 0x37,0x02,0x01,0x04,0x30,0x2f,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x04,0x31,0x22,0x04,0x20,0xcb,0xa1,0x2f, + 0x7b,0x0f,0x6a,0x14,0x06,0x26,0x77,0x7b,0xee,0xd1,0x90,0x34,0x1c,0xa7,0xcd,0x94,0x65,0xe0,0xe4,0x24,0x12,0x0c,0x4e,0xa2, + 0x89,0xeb,0x2d,0xe5,0xbb,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x04,0x82,0x01,0x00, + 0x68,0xfc,0x34,0xac,0x24,0xe1,0x93,0x00,0xec,0x8d,0x29,0xe1,0x32,0x03,0xe6,0x8a,0xd6,0x39,0x79,0x07,0x85,0x97,0xf4,0xb8, + 0x0c,0xf1,0x24,0xa0,0x16,0x09,0x0c,0xf8,0x02,0x40,0x94,0xdb,0x19,0x7a,0x6f,0x91,0xee,0x24,0x36,0x77,0x32,0x02,0xb5,0xd1, + 0xf9,0xe0,0xa2,0xdd,0xe0,0xe1,0x14,0x30,0x11,0xe1,0x25,0x02,0x0a,0x7c,0x10,0xdb,0xbd,0xb0,0x4e,0xbb,0x36,0xef,0x87,0xf0, + 0x17,0x49,0x77,0x45,0xa0,0x9e,0x47,0x70,0x1a,0xe2,0x87,0x39,0x41,0x24,0x1c,0xe0,0x09,0xb0,0xe0,0xfa,0xc5,0xf3,0xba,0xba, + 0x03,0x65,0x64,0xf9,0xa8,0x7d,0xe5,0x0e,0x84,0xc8,0xd1,0xe2,0xf5,0x44,0xa4,0x6f,0x33,0xac,0xbb,0x15,0x3b,0x0a,0x1a,0x04, + 0x6e,0xc2,0x54,0xa7,0x78,0x77,0x7d,0x32,0x21,0x4d,0x0c,0x3f,0x7b,0x0a,0x61,0x18,0x58,0xdb,0x59,0x02,0x3f,0xcf,0xb2,0xd0, + 0x5c,0xa5,0xea,0x96,0xd4,0x5c,0xd2,0x09,0xd3,0x18,0x61,0x73,0x6e,0x9f,0xdf,0xcb,0x17,0x4f,0xd1,0xc0,0xa2,0x2d,0x8b,0xf5, + 0x46,0xdf,0xf8,0xb8,0x4f,0x47,0x98,0xf4,0x44,0xa6,0xa1,0x5b,0xcb,0xfa,0xc1,0x31,0x4e,0xc4,0x03,0xea,0x06,0x1b,0x9b,0x94, + 0xa6,0xc8,0x1c,0x7a,0x69,0x3b,0x8d,0x8d,0x83,0x20,0x56,0x18,0xf1,0xe0,0xd2,0xfb,0xbc,0xaf,0xf7,0xdc,0x17,0x3b,0xcd,0xac, + 0x2b,0x07,0x86,0xc6,0x7f,0x25,0xc3,0xa2,0x6c,0x7c,0x49,0xa9,0xc1,0xe2,0x5e,0x40,0x05,0xfb,0x2f,0xab,0xd5,0x98,0x3a,0x69, + 0xbb,0x83,0x1c,0xbd,0xde,0x55,0xc0,0x74,0x71,0x8d,0xdb,0xc7,0x95,0xf4,0xf5,0xca,0x30,0x82,0x05,0x56,0x06,0x09,0x2a,0x86, + 0x48,0x86,0xf7,0x0d,0x01,0x07,0x02,0xa0,0x82,0x05,0x47,0x30,0x82,0x05,0x43,0x02,0x01,0x01,0x31,0x0f,0x30,0x0d,0x06,0x09, + 0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x30,0x5c,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01, + 0x04,0xa0,0x4e,0x30,0x4c,0x30,0x17,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82,0x37,0x02,0x01,0x0f,0x30,0x09,0x03,0x01,0x00, + 0xa0,0x04,0xa2,0x02,0x80,0x00,0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04, + 0x20,0xdd,0x8b,0xd7,0x29,0x3b,0xae,0x16,0xec,0xbb,0x81,0x80,0x55,0x15,0xd8,0x87,0xa5,0x3e,0xeb,0x0b,0x74,0x59,0xb6,0x56, + 0xf1,0x0b,0x2e,0xe1,0xb4,0x42,0x4d,0x8b,0x18,0xa0,0x82,0x03,0x05,0x30,0x82,0x03,0x01,0x30,0x82,0x01,0xe9,0xa0,0x03,0x02, + 0x01,0x02,0x02,0x10,0xae,0xfb,0x3e,0x08,0x15,0xa4,0xe3,0xa7,0x4d,0x91,0x6a,0x85,0x68,0x5b,0x58,0xa1,0x30,0x0d,0x06,0x09, + 0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04,0x03,0x13,0x07, + 0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x30,0x1e,0x17,0x0d,0x32,0x32,0x30,0x39,0x33,0x30,0x31,0x37,0x32,0x33,0x30,0x33,0x5a, + 0x17,0x0d,0x33,0x39,0x31,0x32,0x33,0x31,0x32,0x33,0x35,0x39,0x35,0x39,0x5a,0x30,0x10,0x31,0x0e,0x30,0x0c,0x06,0x03,0x55, + 0x04,0x03,0x13,0x05,0x63,0x65,0x72,0x74,0x32,0x30,0x82,0x01,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01, + 0x01,0x01,0x05,0x00,0x03,0x82,0x01,0x0f,0x00,0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xca,0x01,0x2f,0x98,0x69,0x71, + 0x64,0x63,0x56,0x38,0xd7,0xc4,0xad,0x64,0x02,0x34,0xd4,0x31,0xc2,0x27,0x5d,0xd7,0x8f,0x72,0xd0,0x70,0x95,0xd9,0x75,0x65, + 0x2e,0x8c,0x5b,0x76,0xcd,0x54,0x3f,0xd9,0x0a,0xcc,0x3f,0x03,0x8f,0x74,0x2b,0x8c,0x3d,0x3d,0x4c,0xd3,0xaa,0x3c,0x97,0xf1, + 0x44,0x46,0x57,0x92,0xa9,0xdd,0xd9,0xf0,0xc7,0x8b,0x39,0xf5,0x8d,0x28,0x41,0x18,0xaf,0xca,0x99,0xd1,0xf1,0xe4,0xab,0x93, + 0x0a,0xb6,0xd4,0xad,0x2b,0x9f,0x60,0x27,0x4c,0xf2,0xc9,0x14,0xde,0xf2,0xc6,0xbe,0x82,0x14,0x83,0x65,0x13,0x9f,0x9c,0x8d, + 0xfa,0xac,0x95,0x12,0x00,0xd0,0xa4,0x36,0x4d,0xf0,0x8f,0xfc,0x1a,0x43,0x47,0xc3,0xff,0xce,0x1b,0x24,0xd6,0xcf,0x63,0xd1, + 0x41,0x23,0xb8,0x62,0x5f,0x31,0x4e,0x30,0x3f,0x63,0x64,0xff,0x72,0xb5,0x9d,0xe5,0xaa,0x22,0xbc,0x1d,0xb3,0x23,0xc9,0x16, + 0x49,0x10,0xed,0x51,0x02,0xd2,0x90,0xc6,0x86,0x47,0x40,0x7e,0xf1,0xcf,0xc1,0x17,0xa0,0x72,0xaf,0x40,0xb1,0x23,0x3d,0x5a, + 0xa1,0xf9,0xed,0xc8,0xb6,0x66,0xa7,0x94,0x39,0x09,0x03,0x6d,0x16,0x4e,0xc4,0x2a,0x4b,0x1f,0x5b,0x22,0x39,0xf7,0x60,0x1c, + 0x71,0x65,0x4c,0x11,0x29,0x59,0x96,0x5e,0x9e,0xfe,0xaf,0x23,0xd1,0xe3,0x2c,0xce,0xd2,0x31,0x8c,0x80,0x29,0x6c,0x82,0x99, + 0xe8,0x68,0xbd,0x7e,0x66,0xaa,0x35,0x0c,0xae,0x61,0xde,0x59,0x7d,0x5b,0x16,0x09,0x07,0x52,0x6a,0x14,0x26,0x3c,0x48,0x3e, + 0x03,0xdb,0xd4,0x8a,0xea,0x0e,0x46,0x1a,0x24,0xbd,0x02,0x03,0x01,0x00,0x01,0xa3,0x55,0x30,0x53,0x30,0x0c,0x06,0x03,0x55, + 0x1d,0x13,0x01,0x01,0xff,0x04,0x02,0x30,0x00,0x30,0x43,0x06,0x03,0x55,0x1d,0x01,0x04,0x3c,0x30,0x3a,0x80,0x10,0x88,0x17, + 0xf7,0x38,0x65,0x8b,0x78,0x78,0xf6,0x77,0xe3,0x25,0x47,0x54,0x33,0x4c,0xa1,0x14,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03, + 0x55,0x04,0x03,0x13,0x07,0x54,0x65,0x73,0x74,0x20,0x43,0x41,0x82,0x10,0x2b,0x59,0xb4,0xc7,0xe2,0xce,0x08,0x97,0x46,0x48, + 0x32,0x17,0x0f,0x97,0xc5,0x08,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x0b,0x05,0x00,0x03,0x82,0x01, + 0x01,0x00,0xd1,0x0d,0xdd,0x83,0x0c,0xd8,0xf9,0x0c,0x71,0xe3,0x2f,0x7c,0xc9,0xd7,0x8e,0x33,0x27,0xb6,0x6b,0x34,0x3c,0x41, + 0xf0,0x13,0x03,0xd6,0x5a,0xe2,0x55,0x12,0x42,0x06,0x20,0x03,0xb1,0x74,0xc7,0xc0,0x08,0x00,0x21,0xbe,0x90,0xe7,0xfd,0xac, + 0xe0,0x67,0x42,0xe7,0x53,0x86,0xcf,0x53,0x55,0x40,0xf1,0xbc,0xfc,0x87,0xab,0x67,0xb6,0x09,0xe1,0xf1,0xa2,0xce,0xf6,0xbf, + 0xe6,0x1d,0x43,0x4f,0x41,0xf0,0xf5,0xc0,0xfa,0xc5,0xd2,0x14,0x2d,0xd9,0x23,0x8e,0x9c,0xeb,0x68,0xff,0x3c,0x5f,0x18,0xca, + 0x4b,0x09,0xad,0xcd,0xbd,0x23,0x62,0x33,0x4e,0x02,0x10,0xf9,0xe3,0x68,0x6f,0x22,0xb0,0x86,0x0b,0x5a,0xbe,0xd3,0xee,0x8a, + 0x0b,0x4c,0x92,0x9e,0x06,0x31,0x1f,0x95,0x4f,0xbf,0x27,0x7f,0x1f,0xcd,0xcc,0x9c,0x70,0xa1,0x51,0x07,0x7a,0x09,0x36,0x3f, + 0x0a,0x2f,0x16,0x77,0x26,0x9b,0xb4,0xc9,0x1e,0x86,0xe3,0xb3,0xb7,0xc3,0xcc,0xf1,0x44,0x6e,0x2e,0xf4,0xc9,0x5b,0x23,0x08, + 0x0a,0xc0,0xdb,0xc1,0x1a,0x37,0xb3,0xb1,0x91,0xce,0x24,0x26,0x56,0x7f,0x26,0x37,0x88,0xa0,0x02,0x37,0x6e,0x9c,0xca,0xc1, + 0x8c,0x19,0x99,0xca,0x6c,0x9a,0x98,0x75,0x89,0xfc,0x6d,0x92,0xfc,0xb5,0x12,0x5b,0x29,0xb1,0x88,0x68,0x3b,0xef,0xf0,0xc0, + 0x8f,0x82,0x5e,0x33,0xf9,0x67,0x6b,0xe8,0x60,0x1b,0x14,0xec,0x9c,0xdf,0x21,0x38,0xbb,0x0d,0x3f,0xd9,0xbc,0xd2,0x01,0x2a, + 0x92,0x0c,0xc2,0x97,0x2e,0x12,0x22,0x54,0x76,0xeb,0x80,0x51,0x99,0x9d,0x0f,0x26,0x12,0xb7,0x31,0x82,0x01,0xc4,0x30,0x82, + 0x01,0xc0,0x02,0x01,0x01,0x30,0x26,0x30,0x12,0x31,0x10,0x30,0x0e,0x06,0x03,0x55,0x04,0x03,0x13,0x07,0x54,0x65,0x73,0x74, + 0x20,0x43,0x41,0x02,0x10,0xae,0xfb,0x3e,0x08,0x15,0xa4,0xe3,0xa7,0x4d,0x91,0x6a,0x85,0x68,0x5b,0x58,0xa1,0x30,0x0d,0x06, + 0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0xa0,0x71,0x30,0x10,0x06,0x0a,0x2b,0x06,0x01,0x04,0x01,0x82, + 0x37,0x02,0x01,0x0c,0x31,0x02,0x30,0x00,0x30,0x11,0x06,0x0a,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x19,0x04,0x31,0x03, + 0x02,0x01,0x01,0x30,0x19,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x03,0x31,0x0c,0x06,0x0a,0x2b,0x06,0x01,0x04, + 0x01,0x82,0x37,0x02,0x01,0x04,0x30,0x2f,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x09,0x04,0x31,0x22,0x04,0x20,0xcb, + 0xa1,0x2f,0x7b,0x0f,0x6a,0x14,0x06,0x26,0x77,0x7b,0xee,0xd1,0x90,0x34,0x1c,0xa7,0xcd,0x94,0x65,0xe0,0xe4,0x24,0x12,0x0c, + 0x4e,0xa2,0x89,0xeb,0x2d,0xe5,0xbb,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01,0x01,0x01,0x05,0x00,0x04,0x82, + 0x01,0x00,0x26,0x76,0xb3,0xf2,0xc9,0xb1,0x73,0x13,0xb5,0xd2,0xc5,0xb7,0x01,0x5c,0xc6,0x94,0x38,0x9f,0xc7,0x57,0x56,0x95, + 0xb0,0xf4,0x6d,0xc2,0xd4,0x6a,0xf1,0x4d,0x09,0xa1,0x51,0xa6,0x91,0xf0,0x0e,0x84,0xc0,0x2c,0x74,0xa3,0x97,0x1f,0x41,0xe0, + 0x4a,0xfa,0x1a,0x78,0xa9,0xd5,0x3c,0x85,0x29,0x2b,0xaf,0xbb,0xc3,0x61,0x0d,0x50,0x20,0x20,0xf5,0x80,0x0d,0x6a,0x15,0x4b, + 0x38,0x6c,0x55,0xd9,0xf9,0xd0,0x44,0x22,0x46,0x98,0xe6,0x07,0xd4,0xba,0x3d,0x9d,0x50,0xa7,0x8e,0x1f,0xa8,0x82,0x25,0x7e, + 0x39,0xda,0xe1,0x49,0xc7,0x24,0x3f,0x31,0xfb,0x4b,0xba,0x75,0xdb,0x10,0x0a,0xbe,0xc5,0xad,0x3e,0x30,0x16,0x9b,0x15,0xbb, + 0xc0,0x59,0xf2,0xf5,0x4f,0xf5,0x56,0xc6,0x28,0xd0,0x1e,0x7d,0x8f,0x2e,0x2b,0xb6,0x76,0x94,0x52,0x87,0x99,0xa3,0x66,0x3d, + 0x94,0x0d,0x73,0xb0,0xd5,0xd4,0x76,0x5b,0x69,0x95,0x0a,0x16,0x4f,0x5c,0xf4,0x95,0x5b,0x42,0x45,0x04,0x5c,0x53,0xb7,0x1a, + 0x61,0x6c,0x82,0xdc,0x95,0x94,0x38,0x64,0x34,0x01,0x98,0x2e,0xf8,0xcf,0xf8,0x66,0xae,0xba,0xf8,0x70,0x9e,0x9e,0xde,0xa2, + 0x7f,0x56,0x8d,0xd9,0x6a,0x7b,0x41,0x02,0x46,0x05,0x5c,0xba,0xed,0x43,0x98,0x56,0x39,0x52,0xc0,0x0b,0x3c,0xe1,0x7d,0x1b, + 0xf5,0xac,0x03,0x5b,0xbb,0x7a,0x65,0x80,0x4b,0xcb,0xb7,0x51,0xa7,0x19,0x8a,0x38,0x75,0x76,0x75,0xc2,0x1f,0x12,0xc4,0x68, + 0x96,0xe0,0x89,0x9f,0x37,0x3d,0xab,0xfd,0x6b,0x03,0xb3,0xa1,0x51,0xf8,0x69,0x17,0xea,0xff,0x00,0x00,0x00,0x00,0x00,0x00, +}; + static void call_winverify(WCHAR *pathW, LONG *status, BOOL hash_only) { static GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; @@ -1312,6 +1782,116 @@ static void test_get_known_usages(void) "expected ERROR_INVALID_PARAMETER, got %ld\n", GetLastError()); } +static void test_multiple_signatures(void) +{ + static const BYTE serials[][16] = + { + { 0xfa, 0x4b, 0x40, 0x69, 0x40, 0x4b, 0xc0, 0x44, 0xa2, 0x31, 0x3a, 0xa7, 0xaa, 0x97, 0x73, 0xd1, }, + { 0xcc, 0x03, 0x6b, 0x72, 0x4a, 0x93, 0x42, 0x40, 0x8f, 0x93, 0x41, 0x68, 0xa9, 0xec, 0x01, 0x8d, }, + { 0xa1, 0x58, 0x5b, 0x68, 0x85, 0x6a, 0x91, 0x4d, 0xa7, 0xe3, 0xa4, 0x15, 0x08, 0x3e, 0xfb, 0xae, }, + }; + static GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2; + WINTRUST_SIGNATURE_SETTINGS settings = { sizeof(settings) }; + WINTRUST_FILE_INFO file_info = { sizeof(file_info) }; + WINTRUST_DATA data = { sizeof(data) }; + CRYPT_PROVIDER_DATA *prov; + WCHAR pathW[MAX_PATH]; + CERT_INFO *cert_info; + unsigned int i; + BYTE buf[4096]; + DWORD written; + LONG status; + HANDLE file; + DWORD size; + BOOL bret; + + file = create_temp_file(pathW); + ok(file != INVALID_HANDLE_VALUE, "Failed to create temporary file.\n"); + bret = WriteFile(file, self_signed_3certs, sizeof(self_signed_3certs), &written, NULL); + ok(bret, "Failed, err %lu.\n", GetLastError()); + CloseHandle(file); + + file_info.pcwszFilePath = pathW; + data.dwUIChoice = WTD_UI_NONE; + data.fdwRevocationChecks = WTD_REVOKE_NONE; + data.dwUnionChoice = WTD_CHOICE_FILE; + data.pFile = &file_info; + data.dwStateAction = WTD_STATEACTION_VERIFY; + data.dwProvFlags = 0; + data.pSignatureSettings = &settings; + + settings.cSecondarySigs = 0xcccccccc; + settings.dwVerifiedSigIndex = 0xcccccccc; + status = WinVerifyTrust(NULL, &WVTPolicyGUID, &data); + todo_wine ok(status == CERT_E_UNTRUSTEDROOT || status == CERT_E_CHAINING, "Failed, ret %#lx\n", status); + ok(settings.cSecondarySigs == 0xcccccccc, "Got %lu.\n", settings.cSecondarySigs); + todo_wine ok(settings.dwVerifiedSigIndex == 2, "Got %lu.\n", settings.dwVerifiedSigIndex); + + prov = (CRYPT_PROVIDER_DATA *)data.hWVTStateData; + ok(prov->cbStruct == sizeof(*prov), "Got size %lu.\n", prov->cbStruct); + ok(prov->csSigners == 1, "Got %lu.\n", prov->csSigners); + ok(prov->pSigSettings == &settings, "Got %p, expected %p.\n", prov->pSigSettings, &settings); + ok(!!prov->pSigState, "Got %p, expected %p.\n", prov->pSigSettings, &settings); + if (prov->cbStruct == sizeof(*prov) && prov->pSigState) + { + ok(prov->pSigState->cbStruct == sizeof(*prov->pSigState) + || broken(prov->pSigState->cbStruct == offsetof(CRYPT_PROVIDER_SIGSTATE, iAttemptCount)) /* Win7 */, + "Got %lu.\n", prov->pSigState->cbStruct); + ok(prov->pSigState->fSupportMultiSig, "Got %d.\n", prov->pSigState->fSupportMultiSig); + ok(prov->pSigState->dwCryptoPolicySupport == (WSS_SIGTRUST_SUPPORT | WSS_OBJTRUST_SUPPORT + | WSS_CERTTRUST_SUPPORT), "Got %#lx.\n", prov->pSigState->dwCryptoPolicySupport); + ok(prov->pSigState->cSecondarySigs == 2, "Got %lu.\n", prov->pSigState->cSecondarySigs); + + size = sizeof(buf); + bret = CryptMsgGetParam(prov->pSigState->hPrimarySig, CMSG_SIGNER_CERT_INFO_PARAM, 0, buf, &size); + ok(bret, "Failed, err %#lx.\n", GetLastError()); + cert_info = (CERT_INFO *)buf; + ok(cert_info->SerialNumber.cbData == sizeof(serials[0]), "Got %lu.\n", cert_info->SerialNumber.cbData); + ok(!memcmp(cert_info->SerialNumber.pbData, serials[0], sizeof(serials[0])), "Data does not match.\n"); + for (i = 0; i < prov->pSigState->cSecondarySigs; ++i) + { + bret = CryptMsgGetParam(prov->pSigState->rhSecondarySigs[i], CMSG_SIGNER_CERT_INFO_PARAM, 0, buf, &size); + ok(bret, "Failed, err %#lx.\n", GetLastError()); + ok(cert_info->SerialNumber.cbData == sizeof(serials[0]), "Got %lu.\n", cert_info->SerialNumber.cbData); + ok(!memcmp(cert_info->SerialNumber.pbData, serials[i + 1], sizeof(serials[0])), "Data does not match.\n"); + } + } + + data.dwStateAction = WTD_STATEACTION_CLOSE; + status = WinVerifyTrust(NULL, &WVTPolicyGUID, &data); + ok(status == S_OK, "Failed, ret %#lx\n", status); + + data.dwStateAction = WTD_STATEACTION_VERIFY; + settings.dwFlags = WSS_GET_SECONDARY_SIG_COUNT; + settings.cSecondarySigs = 0xcccccccc; + settings.dwVerifiedSigIndex = 0xcccccccc; + status = WinVerifyTrust(NULL, &WVTPolicyGUID, &data); + todo_wine ok(status == CERT_E_UNTRUSTEDROOT || status == CERT_E_CHAINING, "Failed, ret %#lx\n", status); + ok(settings.cSecondarySigs == 2, "Got %lu.\n", settings.cSecondarySigs); + todo_wine ok(settings.dwVerifiedSigIndex == 2, "Got %lu.\n", settings.dwVerifiedSigIndex); + + data.dwStateAction = WTD_STATEACTION_CLOSE; + status = WinVerifyTrust(NULL, &WVTPolicyGUID, &data); + ok(status == S_OK, "Failed, ret %#lx\n", status); + + data.dwStateAction = WTD_STATEACTION_VERIFY; + settings.dwFlags = WSS_VERIFY_SPECIFIC | WSS_GET_SECONDARY_SIG_COUNT; + settings.cSecondarySigs = 0xcccccccc; + settings.dwVerifiedSigIndex = 0xcccccccc; + settings.dwIndex = 1; + status = WinVerifyTrust(NULL, &WVTPolicyGUID, &data); + todo_wine ok(status == CERT_E_UNTRUSTEDROOT || status == CERT_E_CHAINING, "Failed, ret %#lx\n", status); + ok(settings.cSecondarySigs == 2, "Got %lu.\n", settings.cSecondarySigs); + todo_wine ok(settings.dwVerifiedSigIndex == 1, "Got %lu.\n", settings.dwVerifiedSigIndex); + settings.dwIndex = 0; + + data.dwStateAction = WTD_STATEACTION_CLOSE; + status = WinVerifyTrust(NULL, &WVTPolicyGUID, &data); + ok(status == S_OK, "Failed, ret %#lx\n", status); + + DeleteFileW(pathW); +} + START_TEST(softpub) { InitFunctionPtrs(); @@ -1320,4 +1900,5 @@ START_TEST(softpub) test_wintrust(); test_wintrust_digest(); test_get_known_usages(); + test_multiple_signatures(); } diff --git a/dlls/wintrust/wintrust_main.c b/dlls/wintrust/wintrust_main.c index 33695008b24..925ae7ca85a 100644 --- wine/dlls/wintrust/wintrust_main.c +++ wine/dlls/wintrust/wintrust_main.c @@ -294,6 +294,10 @@ static LONG WINTRUST_DefaultVerify(HWND hwnd, GUID *actionID, data->hWVTStateData = provData; provData->pWintrustData = data; + + if (WVT_ISINSTRUCT(WINTRUST_DATA, data->cbStruct, pSignatureSettings)) + provData->pSigSettings = data->pSignatureSettings; + if (hwnd == INVALID_HANDLE_VALUE) provData->hWndParent = GetDesktopWindow(); else diff --git a/include/wincrypt.h b/include/wincrypt.h index db2c30c7d68..29735f6225d 100644 --- wine/include/wincrypt.h +++ wine/include/wincrypt.h @@ -21,8 +21,6 @@ #ifndef __WINE_WINCRYPT_H #define __WINE_WINCRYPT_H -#include "wine/winheader_enter.h" - #ifdef __cplusplus extern "C" { #endif @@ -1088,6 +1086,7 @@ typedef struct _CERT_CHAIN_POLICY_STATUS { #define CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG 0x00004000 #define CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG 0x00008000 #define MICROSOFT_ROOT_CERT_CHAIN_POLICY_ENABLE_TEST_ROOT_FLAG 0x00010000 +#define MICROSOFT_ROOT_CERT_CHAIN_POLICY_CHECK_APPLICATION_ROOT_FLAG 0x00020000 typedef struct _AUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA { DWORD cbSize; @@ -3353,8 +3352,10 @@ typedef struct _CTL_FIND_SUBJECT_PARA #define CERT_NAME_URL_TYPE 7 #define CERT_NAME_UPN_TYPE 8 -#define CERT_NAME_ISSUER_FLAG 0x00000001 -#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000 +#define CERT_NAME_ISSUER_FLAG 0x00000001 +#define CERT_NAME_SEARCH_ALL_NAMES_FLAG 0x00000002 +#define CERT_NAME_DISABLE_IE4_UTF8_FLAG 0x00010000 +#define CERT_NAME_STR_ENABLE_PUNYCODE_FLAG 0x00200000 /* CryptFormatObject flags */ #define CRYPT_FORMAT_STR_MULTI_LINE 0x0001 @@ -4695,6 +4696,4 @@ HRESULT WINAPI FindCertsByIssuer(PCERT_CHAIN pCertChains, DWORD *pcbCertChains, } #endif -#include "wine/winheader_exit.h" - #endif diff --git a/include/wintrust.h b/include/wintrust.h index 28df37c1626..eeb149822b4 100644 --- wine/include/wintrust.h +++ wine/include/wintrust.h @@ -19,8 +19,6 @@ #ifndef __WINE_WINTRUST_H #define __WINE_WINTRUST_H -#include "wine/winheader_enter.h" - #include @@ -477,6 +475,8 @@ CRYPT_PROVIDER_SGNR * WINAPI WTHelperGetProvSignerFromChain( CRYPT_PROVIDER_DATA * WINAPI WTHelperProvDataFromStateData(HANDLE hStateData); CRYPT_PROVIDER_PRIVDATA * WINAPI WTHelperGetProvPrivateDataFromChain(CRYPT_PROVIDER_DATA *,GUID *); +#define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1" + #define SPC_INDIRECT_DATA_OBJID "1.3.6.1.4.1.311.2.1.4" #define SPC_SP_AGENCY_INFO_OBJID "1.3.6.1.4.1.311.2.1.10" #define SPC_STATEMENT_TYPE_OBJID "1.3.6.1.4.1.311.2.1.11" @@ -664,6 +664,4 @@ typedef struct _WIN_TRUST_SUBJECT_FILE_AND_DISPLAY } #endif -#include "wine/winheader_exit.h" - #endif -- 2.39.2 (Apple Git-144) diff --git a/configure b/configure index 24f958073a0..cdf99fc287d 100755 --- wine/configure +++ wine/configure @@ -950,6 +950,7 @@ enable_amstream enable_apisetschema enable_apphelp enable_appwiz_cpl +enable_atiadlxx enable_api_ms_win_core_psm_appnotify_l1_1_0 enable_api_ms_win_power_base_l1_1_0 enable_atl @@ -21778,6 +21779,7 @@ wine_fn_config_makefile dlls/apisetschema enable_apisetschema wine_fn_config_makefile dlls/apphelp enable_apphelp wine_fn_config_makefile dlls/apphelp/tests enable_tests wine_fn_config_makefile dlls/appwiz.cpl enable_appwiz_cpl +wine_fn_config_makefile dlls/atiadlxx enable_atiadlxx wine_fn_config_makefile dlls/api-ms-win-core-psm-appnotify-l1-1-0 enable_api_ms_win_core_psm_appnotify_l1_1_0 wine_fn_config_makefile dlls/api-ms-win-power-base-l1-1-0 enable_api_ms_win_power_base_l1_1_0 wine_fn_config_makefile dlls/atl enable_atl diff --git a/configure.ac b/configure.ac index 58063421cce..c05d5b6f539 100644 --- wine/configure.ac +++ wine/configure.ac @@ -2424,6 +2424,7 @@ WINE_CONFIG_MAKEFILE(dlls/apisetschema) WINE_CONFIG_MAKEFILE(dlls/apphelp) WINE_CONFIG_MAKEFILE(dlls/apphelp/tests) WINE_CONFIG_MAKEFILE(dlls/appwiz.cpl) +WINE_CONFIG_MAKEFILE(dlls/atiadlxx) WINE_CONFIG_MAKEFILE(dlls/api-ms-win-power-base-l1-1-0) WINE_CONFIG_MAKEFILE(dlls/atl) WINE_CONFIG_MAKEFILE(dlls/atl/tests) diff --git a/dlls/atiadlxx/Makefile.in b/dlls/atiadlxx/Makefile.in new file mode 100644 index 00000000000..fd9b8abf626 --- /dev/null +++ wine/dlls/atiadlxx/Makefile.in @@ -0,0 +1,8 @@ +EXTRADEFS = -DWINE_NO_LONG_TYPES +MODULE = atiadlxx.dll +IMPORTS = dxgi + +EXTRADLLFLAGS = -mno-cygwin -Wb,--prefer-native + +C_SRCS = \ + atiadlxx_main.c diff --git a/dlls/atiadlxx/atiadlxx.spec b/dlls/atiadlxx/atiadlxx.spec new file mode 100644 index 00000000000..1e447f38ded --- /dev/null +++ wine/dlls/atiadlxx/atiadlxx.spec @@ -0,0 +1,1138 @@ +@ stub ADL2_ADC_CurrentProfileFromDrv_Get +@ stub ADL2_ADC_Display_AdapterDeviceProfileEx_Get +@ stub ADL2_ADC_DrvDataToProfile_Copy +@ stub ADL2_ADC_FindClosestMode_Get +@ stub ADL2_ADC_IsDevModeEqual_Get +@ stub ADL2_ADC_Profile_Apply +@ stub ADL2_APO_AudioDelayAdjustmentInfo_Get +@ stub ADL2_APO_AudioDelay_Restore +@ stub ADL2_APO_AudioDelay_Set +@ stub ADL2_AdapterLimitation_Caps +@ stub ADL2_AdapterX2_Caps +@ stub ADL2_Adapter_AMDAndNonAMDDIsplayClone_Get +@ stub ADL2_Adapter_ASICFamilyType_Get +@ stub ADL2_Adapter_ASICInfo_Get +@ stub ADL2_Adapter_Accessibility_Get +@ stub ADL2_Adapter_AceDefaults_Restore +@ stub ADL2_Adapter_Active_Get +@ stub ADL2_Adapter_Active_Set +@ stub ADL2_Adapter_Active_SetPrefer +@ stub ADL2_Adapter_AdapterInfoX2_Get +@ stub ADL2_Adapter_AdapterInfoX3_Get +@ stub ADL2_Adapter_AdapterInfoX4_Get +@ stub ADL2_Adapter_AdapterInfo_Get +@ stub ADL2_Adapter_AdapterList_Disable +@ stub ADL2_Adapter_AdapterLocationPath_Get +@ stub ADL2_Adapter_Aspects_Get +@ stub ADL2_Adapter_AudioChannelSplitConfiguration_Get +@ stub ADL2_Adapter_AudioChannelSplit_Disable +@ stub ADL2_Adapter_AudioChannelSplit_Enable +@ stub ADL2_Adapter_BigSw_Info_Get +@ stub ADL2_Adapter_BlackAndWhiteLevelSupport_Get +@ stub ADL2_Adapter_BlackAndWhiteLevel_Get +@ stub ADL2_Adapter_BlackAndWhiteLevel_Set +@ stub ADL2_Adapter_BoardLayout_Get +@ stub ADL2_Adapter_Caps +@ stub ADL2_Adapter_ChipSetInfo_Get +@ stub ADL2_Adapter_CloneTypes_Get +@ stub ADL2_Adapter_ConfigMemory_Cap +@ stub ADL2_Adapter_ConfigMemory_Get +@ stub ADL2_Adapter_ConfigureState_Get +@ stub ADL2_Adapter_ConnectionData_Get +@ stub ADL2_Adapter_ConnectionData_Remove +@ stub ADL2_Adapter_ConnectionData_Set +@ stub ADL2_Adapter_ConnectionState_Get +@ stub ADL2_Adapter_CrossDisplayPlatformInfo_Get +@ stub ADL2_Adapter_CrossGPUClone_Disable +@ stub ADL2_Adapter_CrossdisplayAdapterRole_Caps +@ stub ADL2_Adapter_CrossdisplayInfoX2_Set +@ stub ADL2_Adapter_CrossdisplayInfo_Get +@ stub ADL2_Adapter_CrossdisplayInfo_Set +@ stub ADL2_Adapter_CrossfireX2_Get +@ stub ADL2_Adapter_Crossfire_Caps +@ stub ADL2_Adapter_Crossfire_Get +@ stub ADL2_Adapter_Crossfire_Set +@ stub ADL2_Adapter_DefaultAudioChannelTable_Load +@ stub ADL2_Adapter_Desktop_Caps +@ stub ADL2_Adapter_Desktop_SupportedSLSGridTypes_Get +@ stub ADL2_Adapter_DeviceID_Get +@ stub ADL2_Adapter_DisplayAudioEndpoint_Enable +@ stub ADL2_Adapter_DisplayAudioEndpoint_Mute +@ stub ADL2_Adapter_DisplayAudioInfo_Get +@ stub ADL2_Adapter_DisplayGTCCaps_Get +@ stub ADL2_Adapter_Display_Caps +@ stub ADL2_Adapter_DriverSettings_Get +@ stub ADL2_Adapter_DriverSettings_Set +@ stub ADL2_Adapter_ECC_ErrorInjection_Set +@ stub ADL2_Adapter_ECC_ErrorRecords_Get +@ stub ADL2_Adapter_EDC_ErrorInjection_Set +@ stub ADL2_Adapter_EDC_ErrorRecords_Get +@ stub ADL2_Adapter_EDIDManagement_Caps +@ stub ADL2_Adapter_EmulationMode_Set +@ stub ADL2_Adapter_ExtInfo_Get +@ stub ADL2_Adapter_Feature_Caps +@ stub ADL2_Adapter_FrameMetrics_Caps +@ stub ADL2_Adapter_FrameMetrics_FrameDuration_Disable +@ stub ADL2_Adapter_FrameMetrics_FrameDuration_Enable +@ stub ADL2_Adapter_FrameMetrics_FrameDuration_Get +@ stub ADL2_Adapter_FrameMetrics_FrameDuration_Start +@ stub ADL2_Adapter_FrameMetrics_FrameDuration_Stop +@ stub ADL2_Adapter_FrameMetrics_Get +@ stub ADL2_Adapter_FrameMetrics_Start +@ stub ADL2_Adapter_FrameMetrics_Stop +@ stub ADL2_Adapter_Gamma_Get +@ stub ADL2_Adapter_Gamma_Set +@ stub ADL2_Adapter_Graphic_Core_Info_Get +@ stub ADL2_Adapter_HBC_Caps +@ stub ADL2_Adapter_HBM_ECC_UC_Check +@ stub ADL2_Adapter_Headless_Get +@ stub ADL2_Adapter_ID_Get +@ stub ADL2_Adapter_IsGamingDriver_Info_Get +@ stub ADL2_Adapter_LocalDisplayConfig_Get +@ stub ADL2_Adapter_LocalDisplayConfig_Set +@ stub ADL2_Adapter_LocalDisplayState_Get +@ stub ADL2_Adapter_MVPU_Set +@ stub ADL2_Adapter_MaxCursorSize_Get +@ stub ADL2_Adapter_MemoryInfo2_Get +@ stub ADL2_Adapter_MemoryInfo_Get +@ stub ADL2_Adapter_MirabilisSupport_Get +@ stub ADL2_Adapter_ModeSwitch +@ stub ADL2_Adapter_ModeTimingOverride_Caps +@ stub ADL2_Adapter_Modes_ReEnumerate +@ stub ADL2_Adapter_NumberOfActivatableSources_Get +@ stdcall ADL2_Adapter_NumberOfAdapters_Get(ptr ptr) +@ stub ADL2_Adapter_ObservedClockInfo_Get +@ stub ADL2_Adapter_PMLog_Start +@ stub ADL2_Adapter_PMLog_Stop +@ stub ADL2_Adapter_PMLog_Support_Get +@ stub ADL2_Adapter_PreFlipPostProcessing_Disable +@ stub ADL2_Adapter_PreFlipPostProcessing_Enable +@ stub ADL2_Adapter_PreFlipPostProcessing_Get_Status +@ stub ADL2_Adapter_PreFlipPostProcessing_Select_LUT_Algorithm +@ stub ADL2_Adapter_PreFlipPostProcessing_Select_LUT_Buffer +@ stub ADL2_Adapter_PreFlipPostProcessing_Unselect_LUT_Buffer +@ stub ADL2_Adapter_Primary_Get +@ stub ADL2_Adapter_Primary_Set +@ stub ADL2_Adapter_RAS_ErrorInjection_Set +@ stub ADL2_Adapter_RegValueInt_Get +@ stub ADL2_Adapter_RegValueInt_Set +@ stub ADL2_Adapter_RegValueString_Get +@ stub ADL2_Adapter_RegValueString_Set +@ stub ADL2_Adapter_SWInfo_Get +@ stub ADL2_Adapter_Speed_Caps +@ stub ADL2_Adapter_Speed_Get +@ stub ADL2_Adapter_Speed_Set +@ stub ADL2_Adapter_SupportedConnections_Get +@ stub ADL2_Adapter_TRNG_Get +@ stub ADL2_Adapter_Tear_Free_Cap +@ stub ADL2_Adapter_VRAMUsage_Get +@ stub ADL2_Adapter_VariBrightEnable_Set +@ stub ADL2_Adapter_VariBrightLevel_Get +@ stub ADL2_Adapter_VariBrightLevel_Set +@ stub ADL2_Adapter_VariBright_Caps +@ stub ADL2_Adapter_VerndorID_Int_get +@ stub ADL2_Adapter_VideoBiosInfo_Get +@ stub ADL2_Adapter_VideoTheaterModeInfo_Get +@ stub ADL2_Adapter_VideoTheaterModeInfo_Set +@ stub ADL2_Adapter_XConnectSupport_Get +@ stub ADL2_ApplicationProfilesX2_AppInterceptionList_Set +@ stub ADL2_ApplicationProfilesX2_AppStartStopInfo_Get +@ stub ADL2_ApplicationProfiles_AppInterceptionList_Set +@ stub ADL2_ApplicationProfiles_AppInterception_Set +@ stub ADL2_ApplicationProfiles_AppStartStopInfo_Get +@ stub ADL2_ApplicationProfiles_AppStartStop_Resume +@ stub ADL2_ApplicationProfiles_Applications_Get +@ stub ADL2_ApplicationProfiles_ConvertToCompact +@ stub ADL2_ApplicationProfiles_DriverAreaPrivacy_Get +@ stub ADL2_ApplicationProfiles_GetCustomization +@ stub ADL2_ApplicationProfiles_HitListsX2_Get +@ stub ADL2_ApplicationProfiles_HitListsX3_Get +@ stub ADL2_ApplicationProfiles_HitLists_Get +@ stub ADL2_ApplicationProfiles_ProfileApplicationX2_Assign +@ stub ADL2_ApplicationProfiles_ProfileApplication_Assign +@ stub ADL2_ApplicationProfiles_ProfileOfAnApplicationX2_Search +@ stub ADL2_ApplicationProfiles_ProfileOfAnApplication_InMemorySearch +@ stub ADL2_ApplicationProfiles_ProfileOfAnApplication_Search +@ stub ADL2_ApplicationProfiles_Profile_Create +@ stub ADL2_ApplicationProfiles_Profile_Exist +@ stub ADL2_ApplicationProfiles_Profile_Remove +@ stub ADL2_ApplicationProfiles_PropertyType_Get +@ stub ADL2_ApplicationProfiles_Release_Get +@ stub ADL2_ApplicationProfiles_RemoveApplication +@ stub ADL2_ApplicationProfiles_StatusInfo_Get +@ stub ADL2_ApplicationProfiles_System_Reload +@ stub ADL2_ApplicationProfiles_User_Load +@ stub ADL2_ApplicationProfiles_User_Unload +@ stub ADL2_Audio_CurrentSampleRate_Get +@ stub ADL2_AutoTuningResult_Get +@ stub ADL2_BOOST_Settings_Get +@ stub ADL2_BOOST_Settings_Set +@ stub ADL2_Blockchain_BlockchainMode_Caps +@ stub ADL2_Blockchain_BlockchainMode_Get +@ stub ADL2_Blockchain_BlockchainMode_Set +@ stub ADL2_Blockchain_Hashrate_Set +@ stub ADL2_CDS_UnsafeMode_Set +@ stub ADL2_CHILL_SettingsX2_Get +@ stub ADL2_CHILL_SettingsX2_Set +@ stub ADL2_CV_DongleSettings_Get +@ stub ADL2_CV_DongleSettings_Reset +@ stub ADL2_CV_DongleSettings_Set +@ stub ADL2_Chill_Caps_Get +@ stub ADL2_Chill_Settings_Get +@ stub ADL2_Chill_Settings_Notify +@ stub ADL2_Chill_Settings_Set +@ stub ADL2_CustomFan_Caps +@ stub ADL2_CustomFan_Get +@ stub ADL2_CustomFan_Set +@ stub ADL2_DELAG_Settings_Get +@ stub ADL2_DELAG_Settings_Set +@ stub ADL2_DFP_AllowOnlyCETimings_Get +@ stub ADL2_DFP_AllowOnlyCETimings_Set +@ stub ADL2_DFP_BaseAudioSupport_Get +@ stub ADL2_DFP_GPUScalingEnable_Get +@ stub ADL2_DFP_GPUScalingEnable_Set +@ stub ADL2_DFP_HDMISupport_Get +@ stub ADL2_DFP_MVPUAnalogSupport_Get +@ stub ADL2_DFP_PixelFormat_Caps +@ stub ADL2_DFP_PixelFormat_Get +@ stub ADL2_DFP_PixelFormat_Set +@ stub ADL2_DVRSupport_Get +@ stub ADL2_Desktop_DOPP_Enable +@ stub ADL2_Desktop_DOPP_EnableX2 +@ stub ADL2_Desktop_Detach +@ stub ADL2_Desktop_Device_Create +@ stub ADL2_Desktop_Device_Destroy +@ stub ADL2_Desktop_ExclusiveModeX2_Get +@ stub ADL2_Desktop_HardwareCursor_SetBitmap +@ stub ADL2_Desktop_HardwareCursor_SetPosition +@ stub ADL2_Desktop_HardwareCursor_Toggle +@ stub ADL2_Desktop_PFPAComplete_Set +@ stub ADL2_Desktop_PFPAState_Get +@ stub ADL2_Desktop_PrimaryInfo_Get +@ stub ADL2_Desktop_TextureState_Get +@ stub ADL2_Desktop_Texture_Enable +@ stub ADL2_Device_PMLog_Device_Create +@ stub ADL2_Device_PMLog_Device_Destroy +@ stub ADL2_DisplayScaling_Set +@ stub ADL2_Display_AdapterID_Get +@ stub ADL2_Display_AdjustCaps_Get +@ stub ADL2_Display_AdjustmentCoherent_Get +@ stub ADL2_Display_AdjustmentCoherent_Set +@ stub ADL2_Display_AudioMappingInfo_Get +@ stub ADL2_Display_AvivoColor_Get +@ stub ADL2_Display_AvivoCurrentColor_Set +@ stub ADL2_Display_AvivoDefaultColor_Set +@ stub ADL2_Display_BackLight_Get +@ stub ADL2_Display_BackLight_Set +@ stub ADL2_Display_BezelOffsetSteppingSize_Get +@ stub ADL2_Display_BezelOffset_Set +@ stub ADL2_Display_BezelSupported_Validate +@ stub ADL2_Display_Capabilities_Get +@ stub ADL2_Display_ColorCaps_Get +@ stub ADL2_Display_ColorDepth_Get +@ stub ADL2_Display_ColorDepth_Set +@ stub ADL2_Display_ColorTemperatureSourceDefault_Get +@ stub ADL2_Display_ColorTemperatureSource_Get +@ stub ADL2_Display_ColorTemperatureSource_Set +@ stub ADL2_Display_Color_Get +@ stub ADL2_Display_Color_Set +@ stub ADL2_Display_ConnectedDisplays_Get +@ stub ADL2_Display_ContainerID_Get +@ stub ADL2_Display_ControllerOverlayAdjustmentCaps_Get +@ stub ADL2_Display_ControllerOverlayAdjustmentData_Get +@ stub ADL2_Display_ControllerOverlayAdjustmentData_Set +@ stub ADL2_Display_CustomizedModeListNum_Get +@ stub ADL2_Display_CustomizedModeList_Get +@ stub ADL2_Display_CustomizedMode_Add +@ stub ADL2_Display_CustomizedMode_Delete +@ stub ADL2_Display_CustomizedMode_Validate +@ stub ADL2_Display_DCE_Get +@ stub ADL2_Display_DCE_Set +@ stub ADL2_Display_DDCBlockAccess_Get +@ stub ADL2_Display_DDCInfo2_Get +@ stub ADL2_Display_DDCInfo_Get +@ stub ADL2_Display_Deflicker_Get +@ stub ADL2_Display_Deflicker_Set +@ stub ADL2_Display_DeviceConfig_Get +@ stub ADL2_Display_DisplayContent_Cap +@ stub ADL2_Display_DisplayContent_Get +@ stub ADL2_Display_DisplayContent_Set +@ stub ADL2_Display_DisplayInfo_Get +@ stub ADL2_Display_DisplayMapConfigX2_Set +@ stub ADL2_Display_DisplayMapConfig_Get +@ stub ADL2_Display_DisplayMapConfig_PossibleAddAndRemove +@ stub ADL2_Display_DisplayMapConfig_Set +@ stub ADL2_Display_DisplayMapConfig_Validate +@ stub ADL2_Display_DitherState_Get +@ stub ADL2_Display_DitherState_Set +@ stub ADL2_Display_Downscaling_Caps +@ stub ADL2_Display_DpMstAuxMsg_Get +@ stub ADL2_Display_DpMstInfo_Get +@ stub ADL2_Display_DummyVirtual_Destroy +@ stub ADL2_Display_DummyVirtual_Get +@ stub ADL2_Display_EdidData_Get +@ stub ADL2_Display_EdidData_Set +@ stub ADL2_Display_EnumDisplays_Get +@ stub ADL2_Display_FilterSVideo_Get +@ stub ADL2_Display_FilterSVideo_Set +@ stub ADL2_Display_ForcibleDisplay_Get +@ stub ADL2_Display_ForcibleDisplay_Set +@ stub ADL2_Display_FormatsOverride_Get +@ stub ADL2_Display_FormatsOverride_Set +@ stub ADL2_Display_FreeSyncState_Get +@ stub ADL2_Display_FreeSyncState_Set +@ stub ADL2_Display_FreeSync_Cap +@ stub ADL2_Display_GamutMapping_Get +@ stub ADL2_Display_GamutMapping_Reset +@ stub ADL2_Display_GamutMapping_Set +@ stub ADL2_Display_Gamut_Caps +@ stub ADL2_Display_Gamut_Get +@ stub ADL2_Display_Gamut_Set +@ stub ADL2_Display_HDCP_Get +@ stub ADL2_Display_HDCP_Set +@ stub ADL2_Display_HDRState_Get +@ stub ADL2_Display_HDRState_Set +@ stub ADL2_Display_ImageExpansion_Get +@ stub ADL2_Display_ImageExpansion_Set +@ stub ADL2_Display_InfoPacket_Get +@ stub ADL2_Display_InfoPacket_Set +@ stub ADL2_Display_IsVirtual_Get +@ stub ADL2_Display_LCDRefreshRateCapability_Get +@ stub ADL2_Display_LCDRefreshRateOptions_Get +@ stub ADL2_Display_LCDRefreshRateOptions_Set +@ stub ADL2_Display_LCDRefreshRate_Get +@ stub ADL2_Display_LCDRefreshRate_Set +@ stub ADL2_Display_Limits_Get +@ stub ADL2_Display_MVPUCaps_Get +@ stub ADL2_Display_MVPUStatus_Get +@ stub ADL2_Display_ModeTimingOverrideInfo_Get +@ stub ADL2_Display_ModeTimingOverrideListX2_Get +@ stub ADL2_Display_ModeTimingOverrideListX3_Get +@ stub ADL2_Display_ModeTimingOverrideList_Get +@ stub ADL2_Display_ModeTimingOverrideX2_Get +@ stub ADL2_Display_ModeTimingOverrideX2_Set +@ stub ADL2_Display_ModeTimingOverrideX3_Get +@ stub ADL2_Display_ModeTimingOverride_Delete +@ stub ADL2_Display_ModeTimingOverride_Get +@ stub ADL2_Display_ModeTimingOverride_Set +@ stub ADL2_Display_Modes_Get +@ stub ADL2_Display_Modes_Set +@ stub ADL2_Display_Modes_X2_Get +@ stub ADL2_Display_MonitorPowerState_Set +@ stub ADL2_Display_NativeAUXChannel_Access +@ stub ADL2_Display_NeedWorkaroundFor5Clone_Get +@ stub ADL2_Display_NumberOfDisplays_Get +@ stub ADL2_Display_ODClockConfig_Set +@ stub ADL2_Display_ODClockInfo_Get +@ stub ADL2_Display_Overlap_NotifyAdjustment +@ stub ADL2_Display_Overlap_Set +@ stub ADL2_Display_Overscan_Get +@ stub ADL2_Display_Overscan_Set +@ stub ADL2_Display_PixelFormatDefault_Get +@ stub ADL2_Display_PixelFormat_Get +@ stub ADL2_Display_PixelFormat_Set +@ stub ADL2_Display_Position_Get +@ stub ADL2_Display_Position_Set +@ stub ADL2_Display_PossibleMapping_Get +@ stub ADL2_Display_PossibleMode_Get +@ stub ADL2_Display_PowerXpressActiveGPU_Get +@ stub ADL2_Display_PowerXpressActiveGPU_Set +@ stub ADL2_Display_PowerXpressActvieGPUR2_Get +@ stub ADL2_Display_PowerXpressVersion_Get +@ stub ADL2_Display_PowerXpress_AutoSwitchConfig_Get +@ stub ADL2_Display_PowerXpress_AutoSwitchConfig_Set +@ stub ADL2_Display_PreferredMode_Get +@ stub ADL2_Display_PreservedAspectRatio_Get +@ stub ADL2_Display_PreservedAspectRatio_Set +@ stub ADL2_Display_Property_Get +@ stub ADL2_Display_Property_Set +@ stub ADL2_Display_RcDisplayAdjustment +@ stub ADL2_Display_ReGammaCoefficients_Get +@ stub ADL2_Display_ReGammaCoefficients_Set +@ stub ADL2_Display_ReducedBlanking_Get +@ stub ADL2_Display_ReducedBlanking_Set +@ stub ADL2_Display_RegammaR1_Get +@ stub ADL2_Display_RegammaR1_Set +@ stub ADL2_Display_Regamma_Get +@ stub ADL2_Display_Regamma_Set +@ stub ADL2_Display_SLSBuilder_CommonMode_Get +@ stub ADL2_Display_SLSBuilder_Create +@ stub ADL2_Display_SLSBuilder_DisplaysCanBeNextCandidateInSLS_Get +@ stub ADL2_Display_SLSBuilder_DisplaysCanBeNextCandidateToEnabled_Get +@ stub ADL2_Display_SLSBuilder_Get +@ stub ADL2_Display_SLSBuilder_IsActive_Notify +@ stub ADL2_Display_SLSBuilder_MaxSLSLayoutSize_Get +@ stub ADL2_Display_SLSBuilder_TimeOut_Get +@ stub ADL2_Display_SLSBuilder_Update +@ stub ADL2_Display_SLSGrid_Caps +@ stub ADL2_Display_SLSMapConfigX2_Delete +@ stub ADL2_Display_SLSMapConfigX2_Get +@ stub ADL2_Display_SLSMapConfig_Create +@ stub ADL2_Display_SLSMapConfig_Delete +@ stub ADL2_Display_SLSMapConfig_Get +@ stub ADL2_Display_SLSMapConfig_ImageCropType_Set +@ stub ADL2_Display_SLSMapConfig_Rearrange +@ stub ADL2_Display_SLSMapConfig_SetState +@ stub ADL2_Display_SLSMapConfig_SupportedImageCropType_Get +@ stub ADL2_Display_SLSMapConfig_Valid +@ stub ADL2_Display_SLSMapIndexList_Get +@ stub ADL2_Display_SLSMapIndex_Get +@ stub ADL2_Display_SLSMiddleMode_Get +@ stub ADL2_Display_SLSMiddleMode_Set +@ stub ADL2_Display_SLSRecords_Get +@ stub ADL2_Display_Sharpness_Caps +@ stub ADL2_Display_Sharpness_Get +@ stub ADL2_Display_Sharpness_Info_Get +@ stub ADL2_Display_Sharpness_Set +@ stub ADL2_Display_Size_Get +@ stub ADL2_Display_Size_Set +@ stub ADL2_Display_SourceContentAttribute_Get +@ stub ADL2_Display_SourceContentAttribute_Set +@ stub ADL2_Display_SplitDisplay_Caps +@ stub ADL2_Display_SplitDisplay_Get +@ stub ADL2_Display_SplitDisplay_RestoreDesktopConfiguration +@ stub ADL2_Display_SplitDisplay_Set +@ stub ADL2_Display_SupportedColorDepth_Get +@ stub ADL2_Display_SupportedPixelFormat_Get +@ stub ADL2_Display_SwitchingCapability_Get +@ stub ADL2_Display_TVCaps_Get +@ stub ADL2_Display_TargetTimingX2_Get +@ stub ADL2_Display_TargetTiming_Get +@ stub ADL2_Display_UnderScan_Auto_Get +@ stub ADL2_Display_UnderScan_Auto_Set +@ stub ADL2_Display_UnderscanState_Get +@ stub ADL2_Display_UnderscanState_Set +@ stub ADL2_Display_UnderscanSupport_Get +@ stub ADL2_Display_Underscan_Get +@ stub ADL2_Display_Underscan_Set +@ stub ADL2_Display_Vector_Get +@ stub ADL2_Display_ViewPort_Cap +@ stub ADL2_Display_ViewPort_Get +@ stub ADL2_Display_ViewPort_Set +@ stub ADL2_Display_VirtualType_Get +@ stub ADL2_Display_WriteAndReadI2C +@ stub ADL2_Display_WriteAndReadI2CLargePayload +@ stub ADL2_Display_WriteAndReadI2CRev_Get +@ stub ADL2_ElmCompatibilityMode_Caps +@ stub ADL2_ElmCompatibilityMode_Status_Get +@ stub ADL2_ElmCompatibilityMode_Status_Set +@ stub ADL2_ExclusiveModeGet +@ stub ADL2_FPS_Caps +@ stub ADL2_FPS_Settings_Get +@ stub ADL2_FPS_Settings_Reset +@ stub ADL2_FPS_Settings_Set +@ stub ADL2_Feature_Settings_Get +@ stub ADL2_Feature_Settings_Set +@ stub ADL2_Flush_Driver_Data +@ stub ADL2_GPUVMPageSize_Info_Get +@ stub ADL2_GPUVMPageSize_Info_Set +@ stub ADL2_GPUVerInfo_Get +@ stub ADL2_GcnAsicInfo_Get +@ stub ADL2_Graphics_IsDetachableGraphicsPlatform_Get +@ stub ADL2_Graphics_IsGfx9AndAbove +@ stub ADL2_Graphics_MantleVersion_Get +@ stub ADL2_Graphics_Platform_Get +@ stdcall ADL2_Graphics_VersionsX2_Get(ptr ptr) +@ stub ADL2_Graphics_Versions_Get +@ stub ADL2_Graphics_VulkanVersion_Get +@ stub ADL2_HybridGraphicsGPU_Set +@ stub ADL2_MGPUSLS_Status_Set +@ stub ADL2_MMD_FeatureList_Get +@ stub ADL2_MMD_FeatureValuesX2_Get +@ stub ADL2_MMD_FeatureValuesX2_Set +@ stub ADL2_MMD_FeatureValues_Get +@ stub ADL2_MMD_FeatureValues_Set +@ stub ADL2_MMD_FeaturesX2_Caps +@ stub ADL2_MMD_Features_Caps +@ stub ADL2_MMD_VideoAdjustInfo_Get +@ stub ADL2_MMD_VideoAdjustInfo_Set +@ stub ADL2_MMD_VideoColor_Caps +@ stub ADL2_MMD_VideoColor_Get +@ stub ADL2_MMD_VideoColor_Set +@ stub ADL2_MMD_Video_Caps +@ stub ADL2_Main_ControlX2_Create +@ stdcall ADL2_Main_Control_Create(ptr long ptr) +@ stub ADL2_Main_Control_Destroy +@ stub ADL2_Main_Control_GetProcAddress +@ stub ADL2_Main_Control_IsFunctionValid +@ stub ADL2_Main_Control_Refresh +@ stub ADL2_Main_LogDebug_Set +@ stub ADL2_Main_LogError_Set +@ stub ADL2_New_QueryPMLogData_Get +@ stub ADL2_Overdrive5_CurrentActivity_Get +@ stub ADL2_Overdrive5_FanSpeedInfo_Get +@ stub ADL2_Overdrive5_FanSpeedToDefault_Set +@ stub ADL2_Overdrive5_FanSpeed_Get +@ stub ADL2_Overdrive5_FanSpeed_Set +@ stub ADL2_Overdrive5_ODParameters_Get +@ stub ADL2_Overdrive5_ODPerformanceLevels_Get +@ stub ADL2_Overdrive5_ODPerformanceLevels_Set +@ stub ADL2_Overdrive5_PowerControlAbsValue_Caps +@ stub ADL2_Overdrive5_PowerControlAbsValue_Get +@ stub ADL2_Overdrive5_PowerControlAbsValue_Set +@ stub ADL2_Overdrive5_PowerControlInfo_Get +@ stub ADL2_Overdrive5_PowerControl_Caps +@ stub ADL2_Overdrive5_PowerControl_Get +@ stub ADL2_Overdrive5_PowerControl_Set +@ stub ADL2_Overdrive5_Temperature_Get +@ stub ADL2_Overdrive5_ThermalDevices_Enum +@ stub ADL2_Overdrive6_AdvancedFan_Caps +@ stub ADL2_Overdrive6_CapabilitiesEx_Get +@ stub ADL2_Overdrive6_Capabilities_Get +@ stub ADL2_Overdrive6_ControlI2C +@ stub ADL2_Overdrive6_CurrentPower_Get +@ stub ADL2_Overdrive6_CurrentStatus_Get +@ stub ADL2_Overdrive6_FanPWMLimitData_Get +@ stub ADL2_Overdrive6_FanPWMLimitData_Set +@ stub ADL2_Overdrive6_FanPWMLimitRangeInfo_Get +@ stub ADL2_Overdrive6_FanSpeed_Get +@ stub ADL2_Overdrive6_FanSpeed_Reset +@ stub ADL2_Overdrive6_FanSpeed_Set +@ stub ADL2_Overdrive6_FuzzyController_Caps +@ stub ADL2_Overdrive6_MaxClockAdjust_Get +@ stub ADL2_Overdrive6_PowerControlInfo_Get +@ stub ADL2_Overdrive6_PowerControlInfo_Get_X2 +@ stub ADL2_Overdrive6_PowerControl_Caps +@ stub ADL2_Overdrive6_PowerControl_Get +@ stub ADL2_Overdrive6_PowerControl_Set +@ stub ADL2_Overdrive6_StateEx_Get +@ stub ADL2_Overdrive6_StateEx_Set +@ stub ADL2_Overdrive6_StateInfo_Get +@ stub ADL2_Overdrive6_State_Reset +@ stub ADL2_Overdrive6_State_Set +@ stub ADL2_Overdrive6_TargetTemperatureData_Get +@ stub ADL2_Overdrive6_TargetTemperatureData_Set +@ stub ADL2_Overdrive6_TargetTemperatureRangeInfo_Get +@ stub ADL2_Overdrive6_TemperatureEx_Get +@ stub ADL2_Overdrive6_Temperature_Get +@ stub ADL2_Overdrive6_ThermalController_Caps +@ stub ADL2_Overdrive6_ThermalLimitUnlock_Get +@ stub ADL2_Overdrive6_ThermalLimitUnlock_Set +@ stub ADL2_Overdrive6_VoltageControlInfo_Get +@ stub ADL2_Overdrive6_VoltageControl_Get +@ stub ADL2_Overdrive6_VoltageControl_Set +@ stub ADL2_Overdrive8_Current_SettingX2_Get +@ stub ADL2_Overdrive8_Current_SettingX3_Get +@ stub ADL2_Overdrive8_Current_Setting_Get +@ stub ADL2_Overdrive8_Init_SettingX2_Get +@ stub ADL2_Overdrive8_Init_Setting_Get +@ stub ADL2_Overdrive8_PMLogSenorRange_Caps +@ stub ADL2_Overdrive8_PMLogSenorType_Support_Get +@ stub ADL2_Overdrive8_PMLog_ShareMemory_Read +@ stub ADL2_Overdrive8_PMLog_ShareMemory_Start +@ stub ADL2_Overdrive8_PMLog_ShareMemory_Stop +@ stub ADL2_Overdrive8_PMLog_ShareMemory_Support +@ stub ADL2_Overdrive8_Setting_Set +@ stub ADL2_OverdriveN_AutoWattman_Caps +@ stub ADL2_OverdriveN_AutoWattman_Get +@ stub ADL2_OverdriveN_AutoWattman_Set +@ stub ADL2_OverdriveN_CapabilitiesX2_Get +@ stub ADL2_OverdriveN_Capabilities_Get +@ stub ADL2_OverdriveN_CountOfEvents_Get +@ stub ADL2_OverdriveN_FanControl_Get +@ stub ADL2_OverdriveN_FanControl_Set +@ stub ADL2_OverdriveN_MemoryClocksX2_Get +@ stub ADL2_OverdriveN_MemoryClocksX2_Set +@ stub ADL2_OverdriveN_MemoryClocks_Get +@ stub ADL2_OverdriveN_MemoryClocks_Set +@ stub ADL2_OverdriveN_MemoryTimingLevel_Get +@ stub ADL2_OverdriveN_MemoryTimingLevel_Set +@ stub ADL2_OverdriveN_PerformanceStatus_Get +@ stub ADL2_OverdriveN_PowerLimit_Get +@ stub ADL2_OverdriveN_PowerLimit_Set +@ stub ADL2_OverdriveN_SCLKAutoOverClock_Get +@ stub ADL2_OverdriveN_SCLKAutoOverClock_Set +@ stub ADL2_OverdriveN_SettingsExt_Get +@ stub ADL2_OverdriveN_SettingsExt_Set +@ stub ADL2_OverdriveN_SystemClocksX2_Get +@ stub ADL2_OverdriveN_SystemClocksX2_Set +@ stub ADL2_OverdriveN_SystemClocks_Get +@ stub ADL2_OverdriveN_SystemClocks_Set +@ stub ADL2_OverdriveN_Temperature_Get +@ stub ADL2_OverdriveN_Test_Set +@ stub ADL2_OverdriveN_ThrottleNotification_Get +@ stub ADL2_OverdriveN_ZeroRPMFan_Get +@ stub ADL2_OverdriveN_ZeroRPMFan_Set +@ stub ADL2_Overdrive_Caps +@ stub ADL2_PPLogSettings_Get +@ stub ADL2_PPLogSettings_Set +@ stub ADL2_PPW_Caps +@ stub ADL2_PPW_Status_Get +@ stub ADL2_PPW_Status_Set +@ stub ADL2_PageMigration_Settings_Get +@ stub ADL2_PageMigration_Settings_Set +@ stub ADL2_PerGPU_GDEvent_Register +@ stub ADL2_PerGPU_GDEvent_UnRegister +@ stub ADL2_PerfTuning_Status_Get +@ stub ADL2_PerfTuning_Status_Set +@ stub ADL2_PerformanceTuning_Caps +@ stub ADL2_PowerStates_Get +@ stub ADL2_PowerXpress_AncillaryDevices_Get +@ stub ADL2_PowerXpress_Config_Caps +@ stub ADL2_PowerXpress_Configuration_Get +@ stub ADL2_PowerXpress_ExtendedBatteryMode_Caps +@ stub ADL2_PowerXpress_ExtendedBatteryMode_Get +@ stub ADL2_PowerXpress_ExtendedBatteryMode_Set +@ stub ADL2_PowerXpress_LongIdleDetect_Get +@ stub ADL2_PowerXpress_LongIdleDetect_Set +@ stub ADL2_PowerXpress_PowerControlMode_Get +@ stub ADL2_PowerXpress_PowerControlMode_Set +@ stub ADL2_PowerXpress_Scheme_Get +@ stub ADL2_PowerXpress_Scheme_Set +@ stub ADL2_RIS_Settings_Get +@ stub ADL2_RIS_Settings_Set +@ stub ADL2_RegisterEvent +@ stub ADL2_RegisterEventX2 +@ stub ADL2_Remap +@ stub ADL2_RemoteDisplay_Destroy +@ stub ADL2_RemoteDisplay_Display_Acquire +@ stub ADL2_RemoteDisplay_Display_Release +@ stub ADL2_RemoteDisplay_Display_Release_All +@ stub ADL2_RemoteDisplay_Hdcp20_Create +@ stub ADL2_RemoteDisplay_Hdcp20_Destroy +@ stub ADL2_RemoteDisplay_Hdcp20_Notify +@ stub ADL2_RemoteDisplay_Hdcp20_Process +@ stub ADL2_RemoteDisplay_IEPort_Set +@ stub ADL2_RemoteDisplay_Initialize +@ stub ADL2_RemoteDisplay_Nofitiation_Register +@ stub ADL2_RemoteDisplay_Notification_UnRegister +@ stub ADL2_RemoteDisplay_Support_Caps +@ stub ADL2_RemoteDisplay_VirtualWirelessAdapter_InUse_Get +@ stub ADL2_RemoteDisplay_VirtualWirelessAdapter_Info_Get +@ stub ADL2_RemoteDisplay_VirtualWirelessAdapter_RadioState_Get +@ stub ADL2_RemoteDisplay_VirtualWirelessAdapter_WPSSetting_Change +@ stub ADL2_RemoteDisplay_VirtualWirelessAdapter_WPSSetting_Get +@ stub ADL2_RemoteDisplay_WFDDeviceInfo_Get +@ stub ADL2_RemoteDisplay_WFDDeviceName_Change +@ stub ADL2_RemoteDisplay_WFDDevice_StatusInfo_Get +@ stub ADL2_RemoteDisplay_WFDDiscover_Start +@ stub ADL2_RemoteDisplay_WFDDiscover_Stop +@ stub ADL2_RemoteDisplay_WFDLink_Connect +@ stub ADL2_RemoteDisplay_WFDLink_Creation_Accept +@ stub ADL2_RemoteDisplay_WFDLink_Disconnect +@ stub ADL2_RemoteDisplay_WFDLink_WPS_Process +@ stub ADL2_RemoteDisplay_WFDWDSPSettings_Set +@ stub ADL2_RemoteDisplay_WirelessDisplayEnableDisable_Commit +@ stub ADL2_RemotePlay_ControlFlags_Set +@ stub ADL2_ScreenPoint_AudioMappingInfo_Get +@ stub ADL2_Send +@ stub ADL2_SendX2 +@ stub ADL2_Stereo3D_2DPackedFormat_Set +@ stub ADL2_Stereo3D_3DCursorOffset_Get +@ stub ADL2_Stereo3D_3DCursorOffset_Set +@ stub ADL2_Stereo3D_CurrentFormat_Get +@ stub ADL2_Stereo3D_Info_Get +@ stub ADL2_Stereo3D_Modes_Get +@ stub ADL2_SwitchableGraphics_Applications_Get +@ stub ADL2_TV_Standard_Get +@ stub ADL2_TV_Standard_Set +@ stub ADL2_TurboSyncSupport_Get +@ stub ADL2_UnRegisterEvent +@ stub ADL2_UnRegisterEventX2 +@ stub ADL2_User_Settings_Notify +@ stub ADL2_WS_Overdrive_Caps +@ stub ADL2_Win_IsHybridAI +@ stub ADL2_Workstation_8BitGrayscale_Get +@ stub ADL2_Workstation_8BitGrayscale_Set +@ stub ADL2_Workstation_AdapterNumOfGLSyncConnectors_Get +@ stub ADL2_Workstation_Caps +@ stub ADL2_Workstation_DeepBitDepthX2_Get +@ stub ADL2_Workstation_DeepBitDepthX2_Set +@ stub ADL2_Workstation_DeepBitDepth_Get +@ stub ADL2_Workstation_DeepBitDepth_Set +@ stub ADL2_Workstation_DisplayGLSyncMode_Get +@ stub ADL2_Workstation_DisplayGLSyncMode_Set +@ stub ADL2_Workstation_DisplayGenlockCapable_Get +@ stub ADL2_Workstation_ECCData_Get +@ stub ADL2_Workstation_ECCX2_Get +@ stub ADL2_Workstation_ECC_Caps +@ stub ADL2_Workstation_ECC_Get +@ stub ADL2_Workstation_ECC_Set +@ stub ADL2_Workstation_GLSyncCounters_Get +@ stub ADL2_Workstation_GLSyncGenlockConfiguration_Get +@ stub ADL2_Workstation_GLSyncGenlockConfiguration_Set +@ stub ADL2_Workstation_GLSyncModuleDetect_Get +@ stub ADL2_Workstation_GLSyncModuleInfo_Get +@ stub ADL2_Workstation_GLSyncPortState_Get +@ stub ADL2_Workstation_GLSyncPortState_Set +@ stub ADL2_Workstation_GLSyncSupportedTopology_Get +@ stub ADL2_Workstation_GlobalEDIDPersistence_Get +@ stub ADL2_Workstation_GlobalEDIDPersistence_Set +@ stub ADL2_Workstation_LoadBalancing_Caps +@ stub ADL2_Workstation_LoadBalancing_Get +@ stub ADL2_Workstation_LoadBalancing_Set +@ stub ADL2_Workstation_RAS_ErrorCounts_Get +@ stub ADL2_Workstation_RAS_ErrorCounts_Reset +@ stub ADL2_Workstation_SDISegmentList_Get +@ stub ADL2_Workstation_SDI_Caps +@ stub ADL2_Workstation_SDI_Get +@ stub ADL2_Workstation_SDI_Set +@ stub ADL2_Workstation_Stereo_Get +@ stub ADL2_Workstation_Stereo_Set +@ stub ADL2_Workstation_UnsupportedDisplayModes_Enable +@ stub ADL_ADC_CurrentProfileFromDrv_Get +@ stub ADL_ADC_Display_AdapterDeviceProfileEx_Get +@ stub ADL_ADC_DrvDataToProfile_Copy +@ stub ADL_ADC_FindClosestMode_Get +@ stub ADL_ADC_IsDevModeEqual_Get +@ stub ADL_ADC_Profile_Apply +@ stub ADL_APO_AudioDelayAdjustmentInfo_Get +@ stub ADL_APO_AudioDelay_Restore +@ stub ADL_APO_AudioDelay_Set +@ stub ADL_AdapterLimitation_Caps +@ stub ADL_AdapterX2_Caps +@ stdcall ADL_Adapter_ASICFamilyType_Get(long ptr ptr) +@ stub ADL_Adapter_ASICInfo_Get +@ stub ADL_Adapter_Accessibility_Get +@ stub ADL_Adapter_Active_Get +@ stub ADL_Adapter_Active_Set +@ stub ADL_Adapter_Active_SetPrefer +@ stub ADL_Adapter_AdapterInfoX2_Get +@ stdcall ADL_Adapter_AdapterInfo_Get(ptr long) +@ stub ADL_Adapter_AdapterList_Disable +@ stub ADL_Adapter_Aspects_Get +@ stub ADL_Adapter_AudioChannelSplitConfiguration_Get +@ stub ADL_Adapter_AudioChannelSplit_Disable +@ stub ADL_Adapter_AudioChannelSplit_Enable +@ stub ADL_Adapter_BigSw_Info_Get +@ stub ADL_Adapter_BlackAndWhiteLevelSupport_Get +@ stub ADL_Adapter_BlackAndWhiteLevel_Get +@ stub ADL_Adapter_BlackAndWhiteLevel_Set +@ stub ADL_Adapter_BoardLayout_Get +@ stub ADL_Adapter_Caps +@ stub ADL_Adapter_ChipSetInfo_Get +@ stub ADL_Adapter_ConfigMemory_Cap +@ stub ADL_Adapter_ConfigMemory_Get +@ stub ADL_Adapter_ConfigureState_Get +@ stub ADL_Adapter_ConnectionData_Get +@ stub ADL_Adapter_ConnectionData_Remove +@ stub ADL_Adapter_ConnectionData_Set +@ stub ADL_Adapter_ConnectionState_Get +@ stub ADL_Adapter_CrossDisplayPlatformInfo_Get +@ stub ADL_Adapter_CrossdisplayAdapterRole_Caps +@ stub ADL_Adapter_CrossdisplayInfoX2_Set +@ stub ADL_Adapter_CrossdisplayInfo_Get +@ stub ADL_Adapter_CrossdisplayInfo_Set +@ stub ADL_Adapter_CrossfireX2_Get +@ stdcall ADL_Adapter_Crossfire_Caps(long ptr ptr ptr) +@ stdcall ADL_Adapter_Crossfire_Get(long ptr ptr) +@ stub ADL_Adapter_Crossfire_Set +@ stub ADL_Adapter_DefaultAudioChannelTable_Load +@ stub ADL_Adapter_DisplayAudioEndpoint_Enable +@ stub ADL_Adapter_DisplayAudioEndpoint_Mute +@ stub ADL_Adapter_DisplayAudioInfo_Get +@ stub ADL_Adapter_DisplayGTCCaps_Get +@ stub ADL_Adapter_Display_Caps +@ stub ADL_Adapter_DriverSettings_Get +@ stub ADL_Adapter_DriverSettings_Set +@ stub ADL_Adapter_EDIDManagement_Caps +@ stub ADL_Adapter_EmulationMode_Set +@ stub ADL_Adapter_ExtInfo_Get +@ stub ADL_Adapter_Gamma_Get +@ stub ADL_Adapter_Gamma_Set +@ stub ADL_Adapter_ID_Get +@ stub ADL_Adapter_LocalDisplayConfig_Get +@ stub ADL_Adapter_LocalDisplayConfig_Set +@ stub ADL_Adapter_LocalDisplayState_Get +@ stub ADL_Adapter_MaxCursorSize_Get +@ stub ADL_Adapter_MemoryInfo2_Get +@ stdcall ADL_Adapter_MemoryInfo_Get(long ptr) +@ stub ADL_Adapter_MirabilisSupport_Get +@ stub ADL_Adapter_ModeSwitch +@ stub ADL_Adapter_ModeTimingOverride_Caps +@ stub ADL_Adapter_Modes_ReEnumerate +@ stub ADL_Adapter_NumberOfActivatableSources_Get +@ stdcall ADL_Adapter_NumberOfAdapters_Get(ptr) +@ stdcall ADL_Adapter_ObservedClockInfo_Get(long ptr ptr) +@ stub ADL_Adapter_ObservedGameClockInfo_Get +@ stub ADL_Adapter_Primary_Get +@ stub ADL_Adapter_Primary_Set +@ stub ADL_Adapter_RegValueInt_Get +@ stub ADL_Adapter_RegValueInt_Set +@ stub ADL_Adapter_RegValueString_Get +@ stub ADL_Adapter_RegValueString_Set +@ stub ADL_Adapter_SWInfo_Get +@ stub ADL_Adapter_Speed_Caps +@ stub ADL_Adapter_Speed_Get +@ stub ADL_Adapter_Speed_Set +@ stub ADL_Adapter_SupportedConnections_Get +@ stub ADL_Adapter_Tear_Free_Cap +@ stub ADL_Adapter_VariBrightEnable_Set +@ stub ADL_Adapter_VariBrightLevel_Get +@ stub ADL_Adapter_VariBrightLevel_Set +@ stub ADL_Adapter_VariBright_Caps +@ stub ADL_Adapter_VideoBiosInfo_Get +@ stub ADL_Adapter_VideoTheaterModeInfo_Get +@ stub ADL_Adapter_VideoTheaterModeInfo_Set +@ stub ADL_ApplicationProfiles_Applications_Get +@ stub ADL_ApplicationProfiles_ConvertToCompact +@ stub ADL_ApplicationProfiles_DriverAreaPrivacy_Get +@ stub ADL_ApplicationProfiles_GetCustomization +@ stub ADL_ApplicationProfiles_HitListsX2_Get +@ stub ADL_ApplicationProfiles_HitLists_Get +@ stub ADL_ApplicationProfiles_ProfileApplicationX2_Assign +@ stub ADL_ApplicationProfiles_ProfileApplication_Assign +@ stub ADL_ApplicationProfiles_ProfileOfAnApplicationX2_Search +@ stub ADL_ApplicationProfiles_ProfileOfAnApplication_InMemorySearch +@ stub ADL_ApplicationProfiles_ProfileOfAnApplication_Search +@ stub ADL_ApplicationProfiles_Profile_Create +@ stub ADL_ApplicationProfiles_Profile_Exist +@ stub ADL_ApplicationProfiles_Profile_Remove +@ stub ADL_ApplicationProfiles_PropertyType_Get +@ stub ADL_ApplicationProfiles_Release_Get +@ stub ADL_ApplicationProfiles_RemoveApplication +@ stub ADL_ApplicationProfiles_StatusInfo_Get +@ stub ADL_ApplicationProfiles_System_Reload +@ stub ADL_ApplicationProfiles_User_Load +@ stub ADL_ApplicationProfiles_User_Unload +@ stub ADL_Audio_CurrentSampleRate_Get +@ stub ADL_CDS_UnsafeMode_Set +@ stub ADL_CV_DongleSettings_Get +@ stub ADL_CV_DongleSettings_Reset +@ stub ADL_CV_DongleSettings_Set +@ stub ADL_DFP_AllowOnlyCETimings_Get +@ stub ADL_DFP_AllowOnlyCETimings_Set +@ stub ADL_DFP_BaseAudioSupport_Get +@ stub ADL_DFP_GPUScalingEnable_Get +@ stub ADL_DFP_GPUScalingEnable_Set +@ stub ADL_DFP_HDMISupport_Get +@ stub ADL_DFP_MVPUAnalogSupport_Get +@ stub ADL_DFP_PixelFormat_Caps +@ stub ADL_DFP_PixelFormat_Get +@ stub ADL_DFP_PixelFormat_Set +@ stub ADL_DisplayScaling_Set +@ stub ADL_Display_AdapterID_Get +@ stub ADL_Display_AdjustCaps_Get +@ stub ADL_Display_AdjustmentCoherent_Get +@ stub ADL_Display_AdjustmentCoherent_Set +@ stub ADL_Display_AudioMappingInfo_Get +@ stub ADL_Display_AvivoColor_Get +@ stub ADL_Display_AvivoCurrentColor_Set +@ stub ADL_Display_AvivoDefaultColor_Set +@ stub ADL_Display_BackLight_Get +@ stub ADL_Display_BackLight_Set +@ stub ADL_Display_BezelOffsetSteppingSize_Get +@ stub ADL_Display_BezelOffset_Set +@ stub ADL_Display_BezelSupported_Validate +@ stub ADL_Display_Capabilities_Get +@ stub ADL_Display_ColorCaps_Get +@ stub ADL_Display_ColorDepth_Get +@ stub ADL_Display_ColorDepth_Set +@ stub ADL_Display_ColorTemperatureSource_Get +@ stub ADL_Display_ColorTemperatureSource_Set +@ stub ADL_Display_Color_Get +@ stub ADL_Display_Color_Set +@ stub ADL_Display_ConnectedDisplays_Get +@ stub ADL_Display_ContainerID_Get +@ stub ADL_Display_ControllerOverlayAdjustmentCaps_Get +@ stub ADL_Display_ControllerOverlayAdjustmentData_Get +@ stub ADL_Display_ControllerOverlayAdjustmentData_Set +@ stub ADL_Display_CurrentPixelClock_Get +@ stub ADL_Display_CustomizedModeListNum_Get +@ stub ADL_Display_CustomizedModeList_Get +@ stub ADL_Display_CustomizedMode_Add +@ stub ADL_Display_CustomizedMode_Delete +@ stub ADL_Display_CustomizedMode_Validate +@ stub ADL_Display_DCE_Get +@ stub ADL_Display_DCE_Set +@ stub ADL_Display_DDCBlockAccess_Get +@ stub ADL_Display_DDCInfo2_Get +@ stub ADL_Display_DDCInfo_Get +@ stub ADL_Display_Deflicker_Get +@ stub ADL_Display_Deflicker_Set +@ stub ADL_Display_DeviceConfig_Get +@ stub ADL_Display_DisplayContent_Cap +@ stub ADL_Display_DisplayContent_Get +@ stub ADL_Display_DisplayContent_Set +@ stdcall ADL_Display_DisplayInfo_Get(long long ptr long) +@ stdcall ADL_Display_DisplayMapConfig_Get(long ptr ptr ptr ptr long) +@ stub ADL_Display_DisplayMapConfig_PossibleAddAndRemove +@ stub ADL_Display_DisplayMapConfig_Set +@ stub ADL_Display_DisplayMapConfig_Validate +@ stub ADL_Display_DitherState_Get +@ stub ADL_Display_DitherState_Set +@ stub ADL_Display_Downscaling_Caps +@ stub ADL_Display_DpMstInfo_Get +@ stub ADL_Display_EdidData_Get +@ stub ADL_Display_EdidData_Set +@ stub ADL_Display_EnumDisplays_Get +@ stub ADL_Display_FilterSVideo_Get +@ stub ADL_Display_FilterSVideo_Set +@ stub ADL_Display_ForcibleDisplay_Get +@ stub ADL_Display_ForcibleDisplay_Set +@ stub ADL_Display_FormatsOverride_Get +@ stub ADL_Display_FormatsOverride_Set +@ stub ADL_Display_FreeSyncState_Get +@ stub ADL_Display_FreeSyncState_Set +@ stub ADL_Display_FreeSync_Cap +@ stub ADL_Display_GamutMapping_Get +@ stub ADL_Display_GamutMapping_Reset +@ stub ADL_Display_GamutMapping_Set +@ stub ADL_Display_Gamut_Caps +@ stub ADL_Display_Gamut_Get +@ stub ADL_Display_Gamut_Set +@ stub ADL_Display_ImageExpansion_Get +@ stub ADL_Display_ImageExpansion_Set +@ stub ADL_Display_InfoPacket_Get +@ stub ADL_Display_InfoPacket_Set +@ stub ADL_Display_LCDRefreshRateCapability_Get +@ stub ADL_Display_LCDRefreshRateOptions_Get +@ stub ADL_Display_LCDRefreshRateOptions_Set +@ stub ADL_Display_LCDRefreshRate_Get +@ stub ADL_Display_LCDRefreshRate_Set +@ stub ADL_Display_Limits_Get +@ stub ADL_Display_MVPUCaps_Get +@ stub ADL_Display_MVPUStatus_Get +@ stub ADL_Display_ModeTimingOverrideInfo_Get +@ stub ADL_Display_ModeTimingOverrideListX2_Get +@ stub ADL_Display_ModeTimingOverrideList_Get +@ stub ADL_Display_ModeTimingOverrideX2_Get +@ stub ADL_Display_ModeTimingOverride_Delete +@ stub ADL_Display_ModeTimingOverride_Get +@ stub ADL_Display_ModeTimingOverride_Set +@ stub ADL_Display_Modes_Get +@ stub ADL_Display_Modes_Set +@ stub ADL_Display_MonitorPowerState_Set +@ stub ADL_Display_NativeAUXChannel_Access +@ stub ADL_Display_NeedWorkaroundFor5Clone_Get +@ stub ADL_Display_NumberOfDisplays_Get +@ stub ADL_Display_ODClockConfig_Set +@ stub ADL_Display_ODClockInfo_Get +@ stub ADL_Display_Overlap_Set +@ stub ADL_Display_Overscan_Get +@ stub ADL_Display_Overscan_Set +@ stub ADL_Display_PixelClockAllowableRange_Set +@ stub ADL_Display_PixelClockCaps_Get +@ stub ADL_Display_PixelFormat_Get +@ stub ADL_Display_PixelFormat_Set +@ stub ADL_Display_Position_Get +@ stub ADL_Display_Position_Set +@ stub ADL_Display_PossibleMapping_Get +@ stub ADL_Display_PossibleMode_Get +@ stub ADL_Display_PowerXpressActiveGPU_Get +@ stub ADL_Display_PowerXpressActiveGPU_Set +@ stub ADL_Display_PowerXpressActvieGPUR2_Get +@ stub ADL_Display_PowerXpressVersion_Get +@ stub ADL_Display_PowerXpress_AutoSwitchConfig_Get +@ stub ADL_Display_PowerXpress_AutoSwitchConfig_Set +@ stub ADL_Display_PreservedAspectRatio_Get +@ stub ADL_Display_PreservedAspectRatio_Set +@ stub ADL_Display_Property_Get +@ stub ADL_Display_Property_Set +@ stub ADL_Display_RcDisplayAdjustment +@ stub ADL_Display_ReGammaCoefficients_Get +@ stub ADL_Display_ReGammaCoefficients_Set +@ stub ADL_Display_ReducedBlanking_Get +@ stub ADL_Display_ReducedBlanking_Set +@ stub ADL_Display_RegammaR1_Get +@ stub ADL_Display_RegammaR1_Set +@ stub ADL_Display_Regamma_Get +@ stub ADL_Display_Regamma_Set +@ stub ADL_Display_SLSGrid_Caps +@ stub ADL_Display_SLSMapConfigX2_Get +@ stub ADL_Display_SLSMapConfig_Create +@ stub ADL_Display_SLSMapConfig_Delete +@ stub ADL_Display_SLSMapConfig_Get +@ stub ADL_Display_SLSMapConfig_Rearrange +@ stub ADL_Display_SLSMapConfig_SetState +@ stub ADL_Display_SLSMapIndexList_Get +@ stub ADL_Display_SLSMapIndex_Get +@ stub ADL_Display_SLSMiddleMode_Get +@ stub ADL_Display_SLSMiddleMode_Set +@ stub ADL_Display_SLSRecords_Get +@ stub ADL_Display_Sharpness_Caps +@ stub ADL_Display_Sharpness_Get +@ stub ADL_Display_Sharpness_Info_Get +@ stub ADL_Display_Sharpness_Set +@ stub ADL_Display_Size_Get +@ stub ADL_Display_Size_Set +@ stub ADL_Display_SourceContentAttribute_Get +@ stub ADL_Display_SourceContentAttribute_Set +@ stub ADL_Display_SplitDisplay_Caps +@ stub ADL_Display_SplitDisplay_Get +@ stub ADL_Display_SplitDisplay_RestoreDesktopConfiguration +@ stub ADL_Display_SplitDisplay_Set +@ stub ADL_Display_SupportedColorDepth_Get +@ stub ADL_Display_SupportedPixelFormat_Get +@ stub ADL_Display_SwitchingCapability_Get +@ stub ADL_Display_TVCaps_Get +@ stub ADL_Display_TargetTiming_Get +@ stub ADL_Display_UnderScan_Auto_Get +@ stub ADL_Display_UnderScan_Auto_Set +@ stub ADL_Display_Underscan_Get +@ stub ADL_Display_Underscan_Set +@ stub ADL_Display_Vector_Get +@ stub ADL_Display_ViewPort_Cap +@ stub ADL_Display_ViewPort_Get +@ stub ADL_Display_ViewPort_Set +@ stub ADL_Display_WriteAndReadI2C +@ stub ADL_Display_WriteAndReadI2CLargePayload +@ stub ADL_Display_WriteAndReadI2CRev_Get +@ stub ADL_Flush_Driver_Data +@ stdcall ADL_Graphics_Platform_Get(ptr) +@ stdcall ADL_Graphics_Versions_Get(ptr) +@ stub ADL_MMD_FeatureList_Get +@ stub ADL_MMD_FeatureValuesX2_Get +@ stub ADL_MMD_FeatureValuesX2_Set +@ stub ADL_MMD_FeatureValues_Get +@ stub ADL_MMD_FeatureValues_Set +@ stub ADL_MMD_FeaturesX2_Caps +@ stub ADL_MMD_Features_Caps +@ stub ADL_MMD_VideoAdjustInfo_Get +@ stub ADL_MMD_VideoAdjustInfo_Set +@ stub ADL_MMD_VideoColor_Caps +@ stub ADL_MMD_VideoColor_Get +@ stub ADL_MMD_VideoColor_Set +@ stub ADL_MMD_Video_Caps +@ stub ADL_Main_ControlX2_Create +@ stdcall ADL_Main_Control_Create(ptr long) +@ stdcall ADL_Main_Control_Destroy() +@ stub ADL_Main_Control_GetProcAddress +@ stub ADL_Main_Control_IsFunctionValid +@ stub ADL_Main_Control_Refresh +@ stub ADL_Main_LogDebug_Set +@ stub ADL_Main_LogError_Set +@ stub ADL_Overdrive5_CurrentActivity_Get +@ stub ADL_Overdrive5_FanSpeedInfo_Get +@ stub ADL_Overdrive5_FanSpeedToDefault_Set +@ stub ADL_Overdrive5_FanSpeed_Get +@ stub ADL_Overdrive5_FanSpeed_Set +@ stub ADL_Overdrive5_ODParameters_Get +@ stub ADL_Overdrive5_ODPerformanceLevels_Get +@ stub ADL_Overdrive5_ODPerformanceLevels_Set +@ stub ADL_Overdrive5_PowerControlAbsValue_Caps +@ stub ADL_Overdrive5_PowerControlAbsValue_Get +@ stub ADL_Overdrive5_PowerControlAbsValue_Set +@ stub ADL_Overdrive5_PowerControlInfo_Get +@ stub ADL_Overdrive5_PowerControl_Caps +@ stub ADL_Overdrive5_PowerControl_Get +@ stub ADL_Overdrive5_PowerControl_Set +@ stub ADL_Overdrive5_Temperature_Get +@ stub ADL_Overdrive5_ThermalDevices_Enum +@ stub ADL_Overdrive6_AdvancedFan_Caps +@ stub ADL_Overdrive6_CapabilitiesEx_Get +@ stub ADL_Overdrive6_Capabilities_Get +@ stub ADL_Overdrive6_CurrentStatus_Get +@ stub ADL_Overdrive6_FanPWMLimitData_Get +@ stub ADL_Overdrive6_FanPWMLimitData_Set +@ stub ADL_Overdrive6_FanPWMLimitRangeInfo_Get +@ stub ADL_Overdrive6_FanSpeed_Get +@ stub ADL_Overdrive6_FanSpeed_Reset +@ stub ADL_Overdrive6_FanSpeed_Set +@ stub ADL_Overdrive6_FuzzyController_Caps +@ stub ADL_Overdrive6_MaxClockAdjust_Get +@ stub ADL_Overdrive6_PowerControlInfo_Get +@ stub ADL_Overdrive6_PowerControl_Caps +@ stub ADL_Overdrive6_PowerControl_Get +@ stub ADL_Overdrive6_PowerControl_Set +@ stub ADL_Overdrive6_StateEx_Get +@ stub ADL_Overdrive6_StateEx_Set +@ stub ADL_Overdrive6_StateInfo_Get +@ stub ADL_Overdrive6_State_Reset +@ stub ADL_Overdrive6_State_Set +@ stub ADL_Overdrive6_TargetTemperatureData_Get +@ stub ADL_Overdrive6_TargetTemperatureData_Set +@ stub ADL_Overdrive6_TargetTemperatureRangeInfo_Get +@ stub ADL_Overdrive6_Temperature_Get +@ stub ADL_Overdrive6_ThermalController_Caps +@ stub ADL_Overdrive6_ThermalLimitUnlock_Get +@ stub ADL_Overdrive6_ThermalLimitUnlock_Set +@ stub ADL_Overdrive6_VoltageControlInfo_Get +@ stub ADL_Overdrive6_VoltageControl_Get +@ stub ADL_Overdrive6_VoltageControl_Set +@ stub ADL_Overdrive_Caps +@ stub ADL_PowerXpress_AncillaryDevices_Get +@ stub ADL_PowerXpress_Config_Caps +@ stub ADL_PowerXpress_ExtendedBatteryMode_Caps +@ stub ADL_PowerXpress_ExtendedBatteryMode_Get +@ stub ADL_PowerXpress_ExtendedBatteryMode_Set +@ stub ADL_PowerXpress_LongIdleDetect_Get +@ stub ADL_PowerXpress_LongIdleDetect_Set +@ stub ADL_PowerXpress_PowerControlMode_Get +@ stub ADL_PowerXpress_PowerControlMode_Set +@ stub ADL_PowerXpress_Scheme_Get +@ stub ADL_PowerXpress_Scheme_Set +@ stub ADL_Remap +@ stub ADL_RemoteDisplay_Destroy +@ stub ADL_RemoteDisplay_Display_Acquire +@ stub ADL_RemoteDisplay_Display_Release +@ stub ADL_RemoteDisplay_Display_Release_All +@ stub ADL_RemoteDisplay_Hdcp20_Create +@ stub ADL_RemoteDisplay_Hdcp20_Destroy +@ stub ADL_RemoteDisplay_Hdcp20_Notify +@ stub ADL_RemoteDisplay_Hdcp20_Process +@ stub ADL_RemoteDisplay_IEPort_Set +@ stub ADL_RemoteDisplay_Initialize +@ stub ADL_RemoteDisplay_Nofitiation_Register +@ stub ADL_RemoteDisplay_Notification_UnRegister +@ stub ADL_RemoteDisplay_Support_Caps +@ stub ADL_RemoteDisplay_VirtualWirelessAdapter_InUse_Get +@ stub ADL_RemoteDisplay_VirtualWirelessAdapter_Info_Get +@ stub ADL_RemoteDisplay_VirtualWirelessAdapter_RadioState_Get +@ stub ADL_RemoteDisplay_VirtualWirelessAdapter_WPSSetting_Change +@ stub ADL_RemoteDisplay_VirtualWirelessAdapter_WPSSetting_Get +@ stub ADL_RemoteDisplay_WFDDeviceInfo_Get +@ stub ADL_RemoteDisplay_WFDDeviceName_Change +@ stub ADL_RemoteDisplay_WFDDevice_StatusInfo_Get +@ stub ADL_RemoteDisplay_WFDDiscover_Start +@ stub ADL_RemoteDisplay_WFDDiscover_Stop +@ stub ADL_RemoteDisplay_WFDLink_Connect +@ stub ADL_RemoteDisplay_WFDLink_Creation_Accept +@ stub ADL_RemoteDisplay_WFDLink_Disconnect +@ stub ADL_RemoteDisplay_WFDLink_WPS_Process +@ stub ADL_RemoteDisplay_WFDWDSPSettings_Set +@ stub ADL_RemoteDisplay_WirelessDisplayEnableDisable_Commit +@ stub ADL_ScreenPoint_AudioMappingInfo_Get +@ stub ADL_Stereo3D_2DPackedFormat_Set +@ stub ADL_Stereo3D_3DCursorOffset_Get +@ stub ADL_Stereo3D_3DCursorOffset_Set +@ stub ADL_Stereo3D_CurrentFormat_Get +@ stub ADL_Stereo3D_Info_Get +@ stub ADL_Stereo3D_Modes_Get +@ stub ADL_TV_Standard_Get +@ stub ADL_TV_Standard_Set +@ stub ADL_Win_IsHybridAI +@ stub ADL_Workstation_8BitGrayscale_Get +@ stub ADL_Workstation_8BitGrayscale_Set +@ stub ADL_Workstation_AdapterNumOfGLSyncConnectors_Get +@ stub ADL_Workstation_Caps +@ stub ADL_Workstation_DeepBitDepthX2_Get +@ stub ADL_Workstation_DeepBitDepthX2_Set +@ stub ADL_Workstation_DeepBitDepth_Get +@ stub ADL_Workstation_DeepBitDepth_Set +@ stub ADL_Workstation_DisplayGLSyncMode_Get +@ stub ADL_Workstation_DisplayGLSyncMode_Set +@ stub ADL_Workstation_DisplayGenlockCapable_Get +@ stub ADL_Workstation_ECCData_Get +@ stub ADL_Workstation_ECCX2_Get +@ stub ADL_Workstation_ECC_Caps +@ stub ADL_Workstation_ECC_Get +@ stub ADL_Workstation_ECC_Set +@ stub ADL_Workstation_GLSyncCounters_Get +@ stub ADL_Workstation_GLSyncGenlockConfiguration_Get +@ stub ADL_Workstation_GLSyncGenlockConfiguration_Set +@ stub ADL_Workstation_GLSyncModuleDetect_Get +@ stub ADL_Workstation_GLSyncModuleInfo_Get +@ stub ADL_Workstation_GLSyncPortState_Get +@ stub ADL_Workstation_GLSyncPortState_Set +@ stub ADL_Workstation_GLSyncSupportedTopology_Get +@ stub ADL_Workstation_GlobalEDIDPersistence_Get +@ stub ADL_Workstation_GlobalEDIDPersistence_Set +@ stub ADL_Workstation_LoadBalancing_Caps +@ stub ADL_Workstation_LoadBalancing_Get +@ stub ADL_Workstation_LoadBalancing_Set +@ stub ADL_Workstation_RAS_Get_Error_Counts +@ stub ADL_Workstation_RAS_Get_Features +@ stub ADL_Workstation_RAS_Reset_Error_Counts +@ stub ADL_Workstation_RAS_Set_Features +@ stub ADL_Workstation_SDISegmentList_Get +@ stub ADL_Workstation_SDI_Caps +@ stub ADL_Workstation_SDI_Get +@ stub ADL_Workstation_SDI_Set +@ stub ADL_Workstation_Stereo_Get +@ stub ADL_Workstation_Stereo_Set +@ stub ADL_Workstation_UnsupportedDisplayModes_Enable +@ stub AmdPowerXpressRequestHighPerformance +@ stub Desktop_Detach +@ stub Send +@ stub SendX2 diff --git a/dlls/atiadlxx/atiadlxx_main.c b/dlls/atiadlxx/atiadlxx_main.c new file mode 100644 index 00000000000..21dfbe71096 --- /dev/null +++ wine/dlls/atiadlxx/atiadlxx_main.c @@ -0,0 +1,493 @@ +/* Headers: https://github.com/GPUOpen-LibrariesAndSDKs/display-library */ + +#include +#include +#include + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "objbase.h" +#include "initguid.h" +#include "wine/debug.h" + +#include "dxgi.h" + +#define MAX_GPUS 64 +#define VENDOR_AMD 0x1002 + +#define ADL_OK 0 +#define ADL_ERR -1 +#define ADL_ERR_INVALID_PARAM -3 +#define ADL_ERR_INVALID_ADL_IDX -5 +#define ADL_ERR_NOT_SUPPORTED -8 +#define ADL_ERR_NULL_POINTER -9 + +#define ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED 0x00000001 +#define ADL_DISPLAY_DISPLAYINFO_DISPLAYMAPPED 0x00000002 +#define ADL_DISPLAY_DISPLAYINFO_MASK 0x31fff + +#define ADL_ASIC_DISCRETE (1 << 0) +#define ADL_ASIC_MASK 0xAF + +enum ADLPlatForm +{ + GRAPHICS_PLATFORM_DESKTOP = 0, + GRAPHICS_PLATFORM_MOBILE = 1 +}; +#define GRAPHICS_PLATFORM_UNKNOWN -1 + + +static IDXGIFactory *dxgi_factory; + +WINE_DEFAULT_DEBUG_CHANNEL(atiadlxx); + +BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) +{ + TRACE("(%p, %u, %p)\n", instance, reason, reserved); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(instance); + break; + } + + return TRUE; +} + +typedef void *(CALLBACK *ADL_MAIN_MALLOC_CALLBACK)(int); +typedef void *ADL_CONTEXT_HANDLE; + +ADL_MAIN_MALLOC_CALLBACK adl_malloc; +#define ADL_MAX_PATH 256 + +typedef struct ADLVersionsInfo +{ + char strDriverVer[ADL_MAX_PATH]; + char strCatalystVersion[ADL_MAX_PATH]; + char strCatalystWebLink[ADL_MAX_PATH]; +} ADLVersionsInfo, *LPADLVersionsInfo; + +typedef struct ADLVersionsInfoX2 +{ + char strDriverVer[ADL_MAX_PATH]; + char strCatalystVersion[ADL_MAX_PATH]; + char strCrimsonVersion[ADL_MAX_PATH]; + char strCatalystWebLink[ADL_MAX_PATH]; +} ADLVersionsInfoX2, *LPADLVersionsInfoX2; + +typedef struct ADLAdapterInfo { + int iSize; + int iAdapterIndex; + char strUDID[ADL_MAX_PATH]; + int iBusNumber; + int iDeviceNumber; + int iFunctionNumber; + int iVendorID; + char strAdapterName[ADL_MAX_PATH]; + char strDisplayName[ADL_MAX_PATH]; + int iPresent; + int iExist; + char strDriverPath[ADL_MAX_PATH]; + char strDriverPathExt[ADL_MAX_PATH]; + char strPNPString[ADL_MAX_PATH]; + int iOSDisplayIndex; +} ADLAdapterInfo, *LPADLAdapterInfo; + +typedef struct ADLDisplayID +{ + int iDisplayLogicalIndex; + int iDisplayPhysicalIndex; + int iDisplayLogicalAdapterIndex; + int iDisplayPhysicalAdapterIndex; +} ADLDisplayID, *LPADLDisplayID; + +typedef struct ADLDisplayInfo +{ + ADLDisplayID displayID; + int iDisplayControllerIndex; + char strDisplayName[ADL_MAX_PATH]; + char strDisplayManufacturerName[ADL_MAX_PATH]; + int iDisplayType; + int iDisplayOutputType; + int iDisplayConnector; + int iDisplayInfoMask; + int iDisplayInfoValue; +} ADLDisplayInfo, *LPADLDisplayInfo; + +typedef struct ADLCrossfireComb +{ + int iNumLinkAdapter; + int iAdaptLink[3]; +} ADLCrossfireComb; + +typedef struct ADLCrossfireInfo +{ + int iErrorCode; + int iState; + int iSupported; +} ADLCrossfireInfo; + +typedef struct ADLMemoryInfo +{ + long long iMemorySize; + char strMemoryType[ADL_MAX_PATH]; + long long iMemoryBandwidth; +} ADLMemoryInfo, *LPADLMemoryInfo; + +typedef struct ADLDisplayTarget +{ + ADLDisplayID displayID; + int iDisplayMapIndex; + int iDisplayTargetMask; + int iDisplayTargetValue; +} ADLDisplayTarget, *LPADLDisplayTarget; + +typedef struct ADLMode +{ + int iAdapterIndex; + ADLDisplayID displayID; + int iXPos; + int iYPos; + int iXRes; + int iYRes; + int iColourDepth; + float fRefreshRate; + int iOrientation; + int iModeFlag; + int iModeMask; + int iModeValue; +} ADLMode, *LPADLMode; + +typedef struct ADLDisplayMap +{ + int iDisplayMapIndex; + ADLMode displayMode; + int iNumDisplayTarget; + int iFirstDisplayTargetArrayIndex; + int iDisplayMapMask; + int iDisplayMapValue; +} ADLDisplayMap, *LPADLDisplayMap; + +static const ADLVersionsInfo version = { + "22.20.19.16-221003a-384125E-AMD-Software-Adrenalin-Edition", + "", + "http://support.amd.com/drivers/xml/driver_09_us.xml", +}; + +static const ADLVersionsInfoX2 version2 = { + "22.20.19.16-221003a-384125E-AMD-Software-Adrenalin-Edition", + "", + "22.10.1", + "http://support.amd.com/drivers/xml/driver_09_us.xml", +}; + +int WINAPI ADL2_Main_Control_Create(ADL_MAIN_MALLOC_CALLBACK cb, int arg, ADL_CONTEXT_HANDLE *ptr) +{ + FIXME("cb %p, arg %d, ptr %p stub!\n", cb, arg, ptr); + return ADL_OK; +} + +int WINAPI ADL_Main_Control_Create(ADL_MAIN_MALLOC_CALLBACK cb, int arg) +{ + FIXME("cb %p, arg %d stub!\n", cb, arg); + adl_malloc = cb; + + + if (SUCCEEDED(CreateDXGIFactory(&IID_IDXGIFactory, (void**) &dxgi_factory))) + return ADL_OK; + else + return ADL_ERR; +} + +int WINAPI ADL_Main_Control_Destroy(void) +{ + FIXME("stub!\n"); + + if (dxgi_factory != NULL) + IUnknown_Release(dxgi_factory); + + return ADL_OK; +} + +int WINAPI ADL2_Adapter_NumberOfAdapters_Get(ADL_CONTEXT_HANDLE *ptr, int *count) +{ + FIXME("ptr %p, count %p stub!\n", ptr, count); + + *count = 0; + + return ADL_OK; +} + +int WINAPI ADL2_Graphics_VersionsX2_Get(ADL_CONTEXT_HANDLE *ptr, ADLVersionsInfoX2 *ver) +{ + FIXME("ptr %p, ver %p stub!\n", ptr, ver); + memcpy(ver, &version2, sizeof(version2)); + return ADL_OK; +} + +int WINAPI ADL_Graphics_Versions_Get(ADLVersionsInfo *ver) +{ + FIXME("ver %p stub!\n", ver); + memcpy(ver, &version, sizeof(version)); + return ADL_OK; +} + +int WINAPI ADL_Adapter_NumberOfAdapters_Get(int *count) +{ + IDXGIAdapter *adapter; + + FIXME("count %p stub!\n", count); + + *count = 0; + while (SUCCEEDED(IDXGIFactory_EnumAdapters(dxgi_factory, *count, &adapter))) + { + (*count)++; + IUnknown_Release(adapter); + } + + TRACE("*count = %d\n", *count); + return ADL_OK; +} + +static int get_adapter_desc(int adapter_index, DXGI_ADAPTER_DESC *desc) +{ + IDXGIAdapter *adapter; + HRESULT hr; + + if (FAILED(IDXGIFactory_EnumAdapters(dxgi_factory, adapter_index, &adapter))) + return ADL_ERR; + + hr = IDXGIAdapter_GetDesc(adapter, desc); + + IUnknown_Release(adapter); + + return SUCCEEDED(hr) ? ADL_OK : ADL_ERR; +} + +/* yep, seriously */ +static int convert_vendor_id(int id) +{ + char str[16]; + snprintf(str, ARRAY_SIZE(str), "%x", id); + return atoi(str); +} + +int WINAPI ADL_Adapter_AdapterInfo_Get(ADLAdapterInfo *adapters, int input_size) +{ + int count, i; + DXGI_ADAPTER_DESC adapter_desc; + + FIXME("adapters %p, input_size %d, stub!\n", adapters, input_size); + + ADL_Adapter_NumberOfAdapters_Get(&count); + + if (!adapters) return ADL_ERR_INVALID_PARAM; + if (input_size != count * sizeof(ADLAdapterInfo)) return ADL_ERR_INVALID_PARAM; + + memset(adapters, 0, input_size); + + for (i = 0; i < count; i++) + { + adapters[i].iSize = sizeof(ADLAdapterInfo); + adapters[i].iAdapterIndex = i; + + if (get_adapter_desc(i, &adapter_desc) != ADL_OK) + return ADL_ERR; + + adapters[i].iVendorID = convert_vendor_id(adapter_desc.VendorId); + } + + return ADL_OK; +} + +int WINAPI ADL_Display_DisplayInfo_Get(int adapter_index, int *num_displays, ADLDisplayInfo **info, int force_detect) +{ + IDXGIAdapter *adapter; + IDXGIOutput *output; + int i; + + FIXME("adapter %d, num_displays %p, info %p stub!\n", adapter_index, num_displays, info); + + if (info == NULL || num_displays == NULL) return ADL_ERR_NULL_POINTER; + + if (FAILED(IDXGIFactory_EnumAdapters(dxgi_factory, adapter_index, &adapter))) + return ADL_ERR_INVALID_PARAM; + + *num_displays = 0; + + while (SUCCEEDED(IDXGIAdapter_EnumOutputs(adapter, *num_displays, &output))) + { + (*num_displays)++; + IUnknown_Release(output); + } + + IUnknown_Release(adapter); + + if (*num_displays == 0) + return ADL_OK; + + *info = adl_malloc(*num_displays * sizeof(**info)); + memset(*info, 0, *num_displays * sizeof(**info)); + + for (i = 0; i < *num_displays; i++) + { + (*info)[i].displayID.iDisplayLogicalIndex = i; + (*info)[i].iDisplayInfoValue = ADL_DISPLAY_DISPLAYINFO_DISPLAYCONNECTED | ADL_DISPLAY_DISPLAYINFO_DISPLAYMAPPED; + (*info)[i].iDisplayInfoMask = (*info)[i].iDisplayInfoValue; + } + + return ADL_OK; +} + +int WINAPI ADL_Adapter_Crossfire_Caps(int adapter_index, int *preffered, int *num_comb, ADLCrossfireComb** comb) +{ + FIXME("adapter %d, preffered %p, num_comb %p, comb %p stub!\n", adapter_index, preffered, num_comb, comb); + return ADL_ERR; +} + +int WINAPI ADL_Adapter_Crossfire_Get(int adapter_index, ADLCrossfireComb *comb, ADLCrossfireInfo *info) +{ + FIXME("adapter %d, comb %p, info %p, stub!\n", adapter_index, comb, info); + return ADL_ERR; +} + +int WINAPI ADL_Adapter_ASICFamilyType_Get(int adapter_index, int *asic_type, int *valids) +{ + DXGI_ADAPTER_DESC adapter_desc; + + FIXME("adapter %d, asic_type %p, valids %p, stub!\n", adapter_index, asic_type, valids); + + if (asic_type == NULL || valids == NULL) + return ADL_ERR_NULL_POINTER; + + if (get_adapter_desc(adapter_index, &adapter_desc) != ADL_OK) + return ADL_ERR_INVALID_ADL_IDX; + + if (adapter_desc.VendorId != VENDOR_AMD) + return ADL_ERR_NOT_SUPPORTED; + + *asic_type = ADL_ASIC_DISCRETE; + *valids = ADL_ASIC_MASK; + + return ADL_OK; +} + +static int get_max_clock(const char *clock, int default_value) +{ + char path[MAX_PATH], line[256]; + FILE *file; + int drm_card, value = 0; + + for (drm_card = 0; drm_card < MAX_GPUS; drm_card++) + { + sprintf(path, "/sys/class/drm/card%d/device/pp_dpm_%s", drm_card, clock); + file = fopen(path, "r"); + + if (file == NULL) + continue; + + while (fgets(line, sizeof(line), file) != NULL) + { + char *number; + + number = strchr(line, ' '); + if (number == NULL) + { + WARN("pp_dpm_%s file has unexpected format\n", clock); + break; + } + + number++; + value = max(strtol(number, NULL, 0), value); + } + } + + if (value != 0) + return value; + + return default_value; +} + +/* documented in the "Linux Specific APIs" section, present and used on Windows */ +/* the name and documentation suggests that this returns current freqs, but it's actually max */ +int WINAPI ADL_Adapter_ObservedClockInfo_Get(int adapter_index, int *core_clock, int *memory_clock) +{ + DXGI_ADAPTER_DESC adapter_desc; + + FIXME("adapter %d, core_clock %p, memory_clock %p, stub!\n", adapter_index, core_clock, memory_clock); + + if (core_clock == NULL || memory_clock == NULL) return ADL_ERR; + if (get_adapter_desc(adapter_index, &adapter_desc) != ADL_OK) return ADL_ERR; + if (adapter_desc.VendorId != VENDOR_AMD) return ADL_ERR_INVALID_ADL_IDX; + + /* default values based on RX580 */ + *core_clock = get_max_clock("sclk", 1350); + *memory_clock = get_max_clock("mclk", 2000); + + TRACE("*core_clock: %i, *memory_clock %i\n", *core_clock, *memory_clock); + + return ADL_OK; +} + +/* documented in the "Linux Specific APIs" section, present and used on Windows */ +int WINAPI ADL_Adapter_MemoryInfo_Get(int adapter_index, ADLMemoryInfo *mem_info) +{ + DXGI_ADAPTER_DESC adapter_desc; + + FIXME("adapter %d, mem_info %p stub!\n", adapter_index, mem_info); + + if (mem_info == NULL) return ADL_ERR_NULL_POINTER; + if (get_adapter_desc(adapter_index, &adapter_desc) != ADL_OK) return ADL_ERR_INVALID_ADL_IDX; + if (adapter_desc.VendorId != VENDOR_AMD) return ADL_ERR; + + mem_info->iMemorySize = adapter_desc.DedicatedVideoMemory; + mem_info->iMemoryBandwidth = 256000; /* not exposed on Linux, probably needs a lookup table */ + + TRACE("iMemoryBandwidth %s, iMemorySize %s\n", + wine_dbgstr_longlong(mem_info->iMemoryBandwidth), + wine_dbgstr_longlong(mem_info->iMemorySize)); + return ADL_OK; +} + +int WINAPI ADL_Graphics_Platform_Get(int *platform) +{ + DXGI_ADAPTER_DESC adapter_desc; + int count, i; + + FIXME("platform %p, stub!\n", platform); + + *platform = GRAPHICS_PLATFORM_UNKNOWN; + + ADL_Adapter_NumberOfAdapters_Get(&count); + + for (i = 0; i < count; i ++) + { + if (get_adapter_desc(i, &adapter_desc) != ADL_OK) + continue; + + if (adapter_desc.VendorId == VENDOR_AMD) + *platform = GRAPHICS_PLATFORM_DESKTOP; + } + + /* NOTE: The real value can be obtained by doing: + * 1. ioctl(DRM_AMDGPU_INFO) with AMDGPU_INFO_DEV_INFO - dev_info.ids_flags & AMDGPU_IDS_FLAGS_FUSION + * 2. VkPhysicalDeviceType() if we ever want to use Vulkan directly + */ + + return ADL_OK; +} + + +int WINAPI ADL_Display_DisplayMapConfig_Get(int adapter_index, int *display_map_count, ADLDisplayMap **display_maps, + int *display_target_count, ADLDisplayTarget **display_targets, int options) +{ + FIXME("adapter_index %d, display_map_count %p, display_maps %p, " + "display_target_count %p, display_targets %p, options %d stub.\n", + adapter_index, display_map_count, display_maps, display_target_count, + display_targets, options); + + return ADL_ERR; +} -- 2.39.2 (Apple Git-144) diff --git a/dlls/windows.gaming.input/Makefile.in b/dlls/windows.gaming.input/Makefile.in index 1e0ce5c360c..3ec3dd0d864 100644 --- wine/dlls/windows.gaming.input/Makefile.in +++ wine/dlls/windows.gaming.input/Makefile.in @@ -2,13 +2,19 @@ MODULE = windows.gaming.input.dll IMPORTS = combase uuid user32 dinput8 setupapi hid C_SRCS = \ + async.c \ + condition_effect.c \ + constant_effect.c \ controller.c \ event_handlers.c \ + force_feedback.c \ gamepad.c \ main.c \ manager.c \ + periodic_effect.c \ provider.c \ racing_wheel.c \ + ramp_effect.c \ vector.c IDL_SRCS = \ diff --git a/dlls/windows.gaming.input/async.c b/dlls/windows.gaming.input/async.c new file mode 100644 index 00000000000..862886195ba --- /dev/null +++ wine/dlls/windows.gaming.input/async.c @@ -0,0 +1,647 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 Bernhard Kölbl for CodeWeavers + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" +#include "provider.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +#define Closed 4 +#define HANDLER_NOT_SET ((void *)~(ULONG_PTR)0) + +struct async_info +{ + IWineAsyncInfoImpl IWineAsyncInfoImpl_iface; + IAsyncInfo IAsyncInfo_iface; + IInspectable *IInspectable_outer; + LONG ref; + + async_operation_callback callback; + TP_WORK *async_run_work; + IUnknown *invoker; + IUnknown *param; + + CRITICAL_SECTION cs; + IWineAsyncOperationCompletedHandler *handler; + PROPVARIANT result; + AsyncStatus status; + HRESULT hr; +}; + +static inline struct async_info *impl_from_IWineAsyncInfoImpl( IWineAsyncInfoImpl *iface ) +{ + return CONTAINING_RECORD( iface, struct async_info, IWineAsyncInfoImpl_iface ); +} + +static HRESULT WINAPI async_impl_QueryInterface( IWineAsyncInfoImpl *iface, REFIID iid, void **out ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IWineAsyncInfoImpl )) + { + IInspectable_AddRef( (*out = &impl->IWineAsyncInfoImpl_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IAsyncInfo )) + { + IInspectable_AddRef( (*out = &impl->IAsyncInfo_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_impl_AddRef( IWineAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_impl_Release( IWineAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + if (impl->handler && impl->handler != HANDLER_NOT_SET) IWineAsyncOperationCompletedHandler_Release( impl->handler ); + IAsyncInfo_Close( &impl->IAsyncInfo_iface ); + if (impl->param) IUnknown_Release( impl->param ); + if (impl->invoker) IUnknown_Release( impl->invoker ); + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &impl->cs ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_impl_put_Completed( IWineAsyncInfoImpl *iface, IWineAsyncOperationCompletedHandler *handler ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, handler %p.\n", iface, handler ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + else if (impl->handler != HANDLER_NOT_SET) hr = E_ILLEGAL_DELEGATE_ASSIGNMENT; + else if ((impl->handler = handler)) + { + IWineAsyncOperationCompletedHandler_AddRef( impl->handler ); + + if (impl->status > Started) + { + IInspectable *operation = impl->IInspectable_outer; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IWineAsyncOperationCompletedHandler_Invoke( handler, operation, status ); + IWineAsyncOperationCompletedHandler_Release( handler ); + + return S_OK; + } + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_get_Completed( IWineAsyncInfoImpl *iface, IWineAsyncOperationCompletedHandler **handler ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, handler %p.\n", iface, handler ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + if (impl->handler == NULL || impl->handler == HANDLER_NOT_SET) *handler = NULL; + else IWineAsyncOperationCompletedHandler_AddRef( (*handler = impl->handler) ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_get_Result( IWineAsyncInfoImpl *iface, PROPVARIANT *result ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + HRESULT hr = E_ILLEGAL_METHOD_CALL; + + TRACE( "iface %p, result %p.\n", iface, result ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Completed || impl->status == Error) + { + PropVariantCopy( result, &impl->result ); + hr = impl->hr; + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_impl_Start( IWineAsyncInfoImpl *iface ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + + TRACE( "iface %p.\n", iface ); + + /* keep the async alive in the callback */ + IInspectable_AddRef( impl->IInspectable_outer ); + SubmitThreadpoolWork( impl->async_run_work ); + + return S_OK; +} + +static const struct IWineAsyncInfoImplVtbl async_impl_vtbl = +{ + /* IUnknown methods */ + async_impl_QueryInterface, + async_impl_AddRef, + async_impl_Release, + /* IWineAsyncInfoImpl */ + async_impl_put_Completed, + async_impl_get_Completed, + async_impl_get_Result, + async_impl_Start, +}; + +DEFINE_IINSPECTABLE_OUTER( async_info, IAsyncInfo, struct async_info, IInspectable_outer ) + +static HRESULT WINAPI async_info_get_Id( IAsyncInfo *iface, UINT32 *id ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, id %p.\n", iface, id ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *id = 1; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_get_Status( IAsyncInfo *iface, AsyncStatus *status ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, status %p.\n", iface, status ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + *status = impl->status; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p, error_code %p.\n", iface, error_code ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) *error_code = hr = E_ILLEGAL_METHOD_CALL; + else *error_code = impl->hr; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_Cancel( IAsyncInfo *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL; + else if (impl->status == Started) impl->status = Canceled; + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI async_info_Close( IAsyncInfo *iface ) +{ + struct async_info *impl = impl_from_IAsyncInfo( iface ); + HRESULT hr = S_OK; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->status == Started) + hr = E_ILLEGAL_STATE_CHANGE; + else if (impl->status != Closed) + { + CloseThreadpoolWork( impl->async_run_work ); + impl->async_run_work = NULL; + impl->status = Closed; + } + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static const struct IAsyncInfoVtbl async_info_vtbl = +{ + /* IUnknown methods */ + async_info_QueryInterface, + async_info_AddRef, + async_info_Release, + /* IInspectable methods */ + async_info_GetIids, + async_info_GetRuntimeClassName, + async_info_GetTrustLevel, + /* IAsyncInfo */ + async_info_get_Id, + async_info_get_Status, + async_info_get_ErrorCode, + async_info_Cancel, + async_info_Close, +}; + +static void CALLBACK async_info_callback( TP_CALLBACK_INSTANCE *instance, void *iface, TP_WORK *work ) +{ + struct async_info *impl = impl_from_IWineAsyncInfoImpl( iface ); + IInspectable *operation = impl->IInspectable_outer; + PROPVARIANT result; + HRESULT hr; + + hr = impl->callback( impl->invoker, impl->param, &result ); + + EnterCriticalSection( &impl->cs ); + if (impl->status != Closed) impl->status = FAILED(hr) ? Error : Completed; + PropVariantCopy( &impl->result, &result ); + impl->hr = hr; + + if (impl->handler != NULL && impl->handler != HANDLER_NOT_SET) + { + IWineAsyncOperationCompletedHandler *handler = impl->handler; + AsyncStatus status = impl->status; + impl->handler = NULL; /* Prevent concurrent invoke. */ + LeaveCriticalSection( &impl->cs ); + + IWineAsyncOperationCompletedHandler_Invoke( handler, operation, status ); + IWineAsyncOperationCompletedHandler_Release( handler ); + } + else LeaveCriticalSection( &impl->cs ); + + /* release refcount acquired in Start */ + IInspectable_Release( operation ); + + PropVariantClear( &result ); +} + +static HRESULT async_info_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IInspectable *outer, IWineAsyncInfoImpl **out ) +{ + struct async_info *impl; + + if (!(impl = calloc( 1, sizeof(struct async_info) ))) return E_OUTOFMEMORY; + impl->IWineAsyncInfoImpl_iface.lpVtbl = &async_impl_vtbl; + impl->IAsyncInfo_iface.lpVtbl = &async_info_vtbl; + impl->IInspectable_outer = outer; + impl->ref = 1; + + impl->callback = callback; + impl->handler = HANDLER_NOT_SET; + impl->status = Started; + if (!(impl->async_run_work = CreateThreadpoolWork( async_info_callback, &impl->IWineAsyncInfoImpl_iface, NULL ))) + return HRESULT_FROM_WIN32( GetLastError() ); + + if ((impl->invoker = invoker)) IUnknown_AddRef( impl->invoker ); + if ((impl->param = param)) IUnknown_AddRef( impl->param ); + + InitializeCriticalSection( &impl->cs ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": async_info.cs" ); + + *out = &impl->IWineAsyncInfoImpl_iface; + return S_OK; +} + +struct async_bool +{ + IAsyncOperation_boolean IAsyncOperation_boolean_iface; + IWineAsyncInfoImpl *IWineAsyncInfoImpl_inner; + LONG ref; +}; + +static inline struct async_bool *impl_from_IAsyncOperation_boolean( IAsyncOperation_boolean *iface ) +{ + return CONTAINING_RECORD( iface, struct async_bool, IAsyncOperation_boolean_iface ); +} + +static HRESULT WINAPI async_bool_QueryInterface( IAsyncOperation_boolean *iface, REFIID iid, void **out ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperation_boolean )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_boolean_iface) ); + return S_OK; + } + + return IWineAsyncInfoImpl_QueryInterface( impl->IWineAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_bool_AddRef( IAsyncOperation_boolean *iface ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_bool_Release( IAsyncOperation_boolean *iface ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_bool_GetIids( IAsyncOperation_boolean *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_bool_GetRuntimeClassName( IAsyncOperation_boolean *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1"), + class_name ); +} + +static HRESULT WINAPI async_bool_GetTrustLevel( IAsyncOperation_boolean *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_bool_put_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean *bool_handler ) +{ + IWineAsyncOperationCompletedHandler *handler = (IWineAsyncOperationCompletedHandler *)bool_handler; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_put_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler *)handler ); +} + +static HRESULT WINAPI async_bool_get_Completed( IAsyncOperation_boolean *iface, IAsyncOperationCompletedHandler_boolean **bool_handler ) +{ + IWineAsyncOperationCompletedHandler **handler = (IWineAsyncOperationCompletedHandler **)bool_handler; + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_get_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler **)handler ); +} + +static HRESULT WINAPI async_bool_GetResults( IAsyncOperation_boolean *iface, BOOLEAN *results ) +{ + struct async_bool *impl = impl_from_IAsyncOperation_boolean( iface ); + PROPVARIANT result = {.vt = VT_BOOL}; + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + hr = IWineAsyncInfoImpl_get_Result( impl->IWineAsyncInfoImpl_inner, &result ); + + *results = result.boolVal; + PropVariantClear( &result ); + return hr; +} + +static const struct IAsyncOperation_booleanVtbl async_bool_vtbl = +{ + /* IUnknown methods */ + async_bool_QueryInterface, + async_bool_AddRef, + async_bool_Release, + /* IInspectable methods */ + async_bool_GetIids, + async_bool_GetRuntimeClassName, + async_bool_GetTrustLevel, + /* IAsyncOperation */ + async_bool_put_Completed, + async_bool_get_Completed, + async_bool_GetResults, +}; + +HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_boolean **out ) +{ + struct async_bool *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_boolean_iface.lpVtbl = &async_bool_vtbl; + impl->ref = 1; + + if (FAILED(hr = async_info_create( invoker, param, callback, (IInspectable *)&impl->IAsyncOperation_boolean_iface, &impl->IWineAsyncInfoImpl_inner )) || + FAILED(hr = IWineAsyncInfoImpl_Start( impl->IWineAsyncInfoImpl_inner ))) + { + if (impl->IWineAsyncInfoImpl_inner) IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + return hr; + } + + *out = &impl->IAsyncOperation_boolean_iface; + TRACE( "created IAsyncOperation_boolean %p\n", *out ); + return S_OK; +} + +struct async_result +{ + IAsyncOperation_ForceFeedbackLoadEffectResult IAsyncOperation_ForceFeedbackLoadEffectResult_iface; + IWineAsyncInfoImpl *IWineAsyncInfoImpl_inner; + LONG ref; +}; + +static inline struct async_result *impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( IAsyncOperation_ForceFeedbackLoadEffectResult *iface ) +{ + return CONTAINING_RECORD( iface, struct async_result, IAsyncOperation_ForceFeedbackLoadEffectResult_iface ); +} + +static HRESULT WINAPI async_result_QueryInterface( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, REFIID iid, void **out ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IAsyncOperation_ForceFeedbackLoadEffectResult )) + { + IInspectable_AddRef( (*out = &impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface) ); + return S_OK; + } + + return IWineAsyncInfoImpl_QueryInterface( impl->IWineAsyncInfoImpl_inner, iid, out ); +} + +static ULONG WINAPI async_result_AddRef( IAsyncOperation_ForceFeedbackLoadEffectResult *iface ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI async_result_Release( IAsyncOperation_ForceFeedbackLoadEffectResult *iface ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p, ref %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI async_result_GetIids( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_result_GetRuntimeClassName( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, HSTRING *class_name ) +{ + return WindowsCreateString( L"Windows.Foundation.IAsyncOperation`1", + ARRAY_SIZE(L"Windows.Foundation.IAsyncOperation`1"), + class_name ); +} + +static HRESULT WINAPI async_result_GetTrustLevel( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI async_result_put_Completed( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult *handler ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_put_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler *)handler ); +} + +static HRESULT WINAPI async_result_get_Completed( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, IAsyncOperationCompletedHandler_ForceFeedbackLoadEffectResult **handler ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + TRACE( "iface %p, handler %p.\n", iface, handler ); + return IWineAsyncInfoImpl_get_Completed( impl->IWineAsyncInfoImpl_inner, (IWineAsyncOperationCompletedHandler **)handler ); +} + +static HRESULT WINAPI async_result_GetResults( IAsyncOperation_ForceFeedbackLoadEffectResult *iface, ForceFeedbackLoadEffectResult *results ) +{ + struct async_result *impl = impl_from_IAsyncOperation_ForceFeedbackLoadEffectResult( iface ); + PROPVARIANT result = {.vt = VT_UI4}; + HRESULT hr; + + TRACE( "iface %p, results %p.\n", iface, results ); + + hr = IWineAsyncInfoImpl_get_Result( impl->IWineAsyncInfoImpl_inner, &result ); + + *results = result.ulVal; + PropVariantClear( &result ); + return hr; +} + +static const struct IAsyncOperation_ForceFeedbackLoadEffectResultVtbl async_result_vtbl = +{ + /* IUnknown methods */ + async_result_QueryInterface, + async_result_AddRef, + async_result_Release, + /* IInspectable methods */ + async_result_GetIids, + async_result_GetRuntimeClassName, + async_result_GetTrustLevel, + /* IAsyncOperation */ + async_result_put_Completed, + async_result_get_Completed, + async_result_GetResults, +}; + +HRESULT async_operation_effect_result_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_ForceFeedbackLoadEffectResult **out ) +{ + struct async_result *impl; + HRESULT hr; + + *out = NULL; + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface.lpVtbl = &async_result_vtbl; + impl->ref = 1; + + if (FAILED(hr = async_info_create( invoker, param, callback, (IInspectable *)&impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface, &impl->IWineAsyncInfoImpl_inner )) || + FAILED(hr = IWineAsyncInfoImpl_Start( impl->IWineAsyncInfoImpl_inner ))) + { + if (impl->IWineAsyncInfoImpl_inner) IWineAsyncInfoImpl_Release( impl->IWineAsyncInfoImpl_inner ); + free( impl ); + return hr; + } + + *out = &impl->IAsyncOperation_ForceFeedbackLoadEffectResult_iface; + TRACE( "created IAsyncOperation_ForceFeedbackLoadEffectResult %p\n", *out ); + return S_OK; +} diff --git a/dlls/windows.gaming.input/classes.idl b/dlls/windows.gaming.input/classes.idl index ca3bd5d8dd7..3172def88f5 100644 --- wine/dlls/windows.gaming.input/classes.idl +++ wine/dlls/windows.gaming.input/classes.idl @@ -29,6 +29,7 @@ import "asyncinfo.idl"; import "eventtoken.idl"; import "windowscontracts.idl"; import "windows.foundation.idl"; +import "windows.foundation.numerics.idl"; import "windows.devices.haptics.idl"; import "windows.gaming.input.forcefeedback.idl"; import "windows.system.idl"; @@ -36,4 +37,5 @@ import "windows.devices.power.idl"; #define DO_NO_IMPORTS #include "windows.gaming.input.idl" +#include "windows.gaming.ui.idl" #include "windows.gaming.input.custom.idl" diff --git a/dlls/windows.gaming.input/condition_effect.c b/dlls/windows.gaming.input/condition_effect.c new file mode 100644 index 00000000000..c3a5a1fcd8b --- /dev/null +++ wine/dlls/windows.gaming.input/condition_effect.c @@ -0,0 +1,288 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct condition_effect +{ + IConditionForceEffect IConditionForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; + + ConditionForceEffectKind kind; +}; + +static inline struct condition_effect *impl_from_IConditionForceEffect( IConditionForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct condition_effect, IConditionForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IConditionForceEffect *iface, REFIID iid, void **out ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IConditionForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IConditionForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IConditionForceEffect *iface ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IConditionForceEffect *iface ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IConditionForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IConditionForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IConditionForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_get_Kind( IConditionForceEffect *iface, ConditionForceEffectKind *kind ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + TRACE( "iface %p, kind %p.\n", iface, kind ); + *kind = impl->kind; + return S_OK; +} + +static HRESULT WINAPI effect_SetParameters( IConditionForceEffect *iface, Vector3 direction, FLOAT positive_coeff, FLOAT negative_coeff, + FLOAT max_positive_magnitude, FLOAT max_negative_magnitude, FLOAT deadzone, FLOAT bias ) +{ + struct condition_effect *impl = impl_from_IConditionForceEffect( iface ); + WineForceFeedbackEffectParameters params = + { + .condition = + { + .type = WineForceFeedbackEffectType_Condition + impl->kind, + .direction = direction, + .positive_coeff = positive_coeff, + .negative_coeff = negative_coeff, + .max_positive_magnitude = max_positive_magnitude, + .max_negative_magnitude = max_negative_magnitude, + .deadzone = deadzone, + .bias = bias, + }, + }; + + TRACE( "iface %p, direction %s, positive_coeff %f, negative_coeff %f, max_positive_magnitude %f, max_negative_magnitude %f, deadzone %f, bias %f.\n", + iface, debugstr_vector3( &direction ), positive_coeff, negative_coeff, max_positive_magnitude, max_negative_magnitude, deadzone, bias ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, NULL ); +} + +static const struct IConditionForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IConditionForceEffect methods */ + effect_get_Kind, + effect_SetParameters, +}; + +struct condition_factory +{ + IActivationFactory IActivationFactory_iface; + IConditionForceEffectFactory IConditionForceEffectFactory_iface; + LONG ref; +}; + +static inline struct condition_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct condition_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct condition_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IConditionForceEffectFactory )) + { + IInspectable_AddRef( (*out = &impl->IConditionForceEffectFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct condition_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct condition_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +DEFINE_IINSPECTABLE( factory, IConditionForceEffectFactory, struct condition_factory, IActivationFactory_iface ) + +static HRESULT WINAPI factory_CreateInstance( IConditionForceEffectFactory *iface, enum ConditionForceEffectKind kind, IForceFeedbackEffect **out ) +{ + enum WineForceFeedbackEffectType type = WineForceFeedbackEffectType_Condition + kind; + struct condition_effect *impl; + HRESULT hr; + + TRACE( "iface %p, kind %u, out %p.\n", iface, kind, out ); + + if (!(impl = calloc( 1, sizeof(struct condition_effect) ))) return E_OUTOFMEMORY; + impl->IConditionForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + impl->kind = kind; + + if (FAILED(hr = force_feedback_effect_create( type, (IInspectable *)&impl->IConditionForceEffect_iface, &impl->IWineForceFeedbackEffectImpl_inner )) || + FAILED(hr = IConditionForceEffect_QueryInterface( &impl->IConditionForceEffect_iface, &IID_IForceFeedbackEffect, (void **)out ))) + { + if (impl->IWineForceFeedbackEffectImpl_inner) IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + return hr; + } + + IConditionForceEffect_Release( &impl->IConditionForceEffect_iface ); + TRACE( "created ConditionForceEffect %p\n", *out ); + return S_OK; +} + +static const struct IConditionForceEffectFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IConditionForceEffectFactory methods */ + factory_CreateInstance, +}; + +static struct condition_factory condition_statics = +{ + {&activation_vtbl}, + {&factory_vtbl}, + 1, +}; + +IInspectable *condition_effect_factory = (IInspectable *)&condition_statics.IActivationFactory_iface; diff --git a/dlls/windows.gaming.input/constant_effect.c b/dlls/windows.gaming.input/constant_effect.c new file mode 100644 index 00000000000..15763b30d67 --- /dev/null +++ wine/dlls/windows.gaming.input/constant_effect.c @@ -0,0 +1,275 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct constant_effect +{ + IConstantForceEffect IConstantForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; +}; + +static inline struct constant_effect *impl_from_IConstantForceEffect( IConstantForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct constant_effect, IConstantForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IConstantForceEffect *iface, REFIID iid, void **out ) +{ + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IConstantForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IConstantForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IConstantForceEffect *iface ) +{ + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IConstantForceEffect *iface ) +{ + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IConstantForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IConstantForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IConstantForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParameters( IConstantForceEffect *iface, Vector3 direction, TimeSpan duration ) +{ + WineForceFeedbackEffectParameters params = + { + .constant = + { + .type = WineForceFeedbackEffectType_Constant, + .direction = direction, + .duration = duration, + .repeat_count = 1, + .gain = 1., + }, + }; + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + + TRACE( "iface %p, direction %s, duration %I64u.\n", iface, debugstr_vector3( &direction ), duration.Duration ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, NULL ); +} + +static HRESULT WINAPI effect_SetParametersWithEnvelope( IConstantForceEffect *iface, Vector3 direction, FLOAT attack_gain, + FLOAT sustain_gain, FLOAT release_gain, TimeSpan start_delay, + TimeSpan attack_duration, TimeSpan sustain_duration, + TimeSpan release_duration, UINT32 repeat_count ) +{ + WineForceFeedbackEffectParameters params = + { + .constant = + { + .type = WineForceFeedbackEffectType_Constant, + .direction = direction, + .duration = {attack_duration.Duration + sustain_duration.Duration + release_duration.Duration}, + .start_delay = start_delay, + .repeat_count = repeat_count, + .gain = sustain_gain, + }, + }; + WineForceFeedbackEffectEnvelope envelope = + { + .attack_gain = attack_gain, + .release_gain = release_gain, + .attack_duration = attack_duration, + .release_duration = release_duration, + }; + struct constant_effect *impl = impl_from_IConstantForceEffect( iface ); + + TRACE( "iface %p, direction %s, attack_gain %f, sustain_gain %f, release_gain %f, start_delay %I64u, attack_duration %I64u, " + "sustain_duration %I64u, release_duration %I64u, repeat_count %u.\n", iface, debugstr_vector3( &direction ), + attack_gain, sustain_gain, release_gain, start_delay.Duration, attack_duration.Duration, sustain_duration.Duration, + release_duration.Duration, repeat_count ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, &envelope ); +} + +static const struct IConstantForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IConstantForceEffect methods */ + effect_SetParameters, + effect_SetParametersWithEnvelope, +}; + +struct constant_factory +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct constant_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct constant_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct constant_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct constant_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct constant_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + struct constant_effect *impl; + HRESULT hr; + + TRACE( "iface %p, instance %p.\n", iface, instance ); + + if (!(impl = calloc( 1, sizeof(struct constant_effect) ))) return E_OUTOFMEMORY; + impl->IConstantForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + + if (FAILED(hr = force_feedback_effect_create( WineForceFeedbackEffectType_Constant, (IInspectable *)&impl->IConstantForceEffect_iface, + &impl->IWineForceFeedbackEffectImpl_inner ))) + { + free( impl ); + return hr; + } + + *instance = (IInspectable *)&impl->IConstantForceEffect_iface; + TRACE( "created ConstantForceEffect %p\n", *instance ); + return S_OK; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +static struct constant_factory constant_statics = +{ + {&activation_vtbl}, + 1, +}; + +IInspectable *constant_effect_factory = (IInspectable *)&constant_statics.IActivationFactory_iface; diff --git a/dlls/windows.gaming.input/controller.c b/dlls/windows.gaming.input/controller.c index 03a3ae398cf..bd3d441c445 100644 --- wine/dlls/windows.gaming.input/controller.c +++ wine/dlls/windows.gaming.input/controller.c @@ -99,7 +99,7 @@ static HRESULT WINAPI controller_QueryInterface( IGameControllerImpl *iface, REF return S_OK; } - WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -229,8 +229,32 @@ static HRESULT WINAPI raw_controller_get_ButtonCount( IRawGameController *iface, static HRESULT WINAPI raw_controller_get_ForceFeedbackMotors( IRawGameController *iface, IVectorView_ForceFeedbackMotor **value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + static const struct vector_iids iids = + { + .vector = &IID_IVector_ForceFeedbackMotor, + .view = &IID_IVectorView_ForceFeedbackMotor, + .iterable = &IID_IIterable_ForceFeedbackMotor, + .iterator = &IID_IIterator_ForceFeedbackMotor, + }; + struct controller *impl = impl_from_IRawGameController( iface ); + IVector_ForceFeedbackMotor *vector; + IForceFeedbackMotor *motor; + HRESULT hr; + + TRACE( "iface %p, value %p\n", iface, value ); + + if (FAILED(hr = vector_create( &iids, (void **)&vector ))) return hr; + + if (SUCCEEDED(IWineGameControllerProvider_get_ForceFeedbackMotor( impl->wine_provider, &motor )) && motor) + { + hr = IVector_ForceFeedbackMotor_Append( vector, motor ); + IForceFeedbackMotor_Release( motor ); + } + + if (SUCCEEDED(hr)) hr = IVector_ForceFeedbackMotor_GetView( vector, value ); + IVector_ForceFeedbackMotor_Release( vector ); + + return hr; } static HRESULT WINAPI raw_controller_get_HardwareProductId( IRawGameController *iface, UINT16 *value ) diff --git a/dlls/windows.gaming.input/force_feedback.c b/dlls/windows.gaming.input/force_feedback.c new file mode 100644 index 00000000000..f7a233b46d4 --- /dev/null +++ wine/dlls/windows.gaming.input/force_feedback.c @@ -0,0 +1,801 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" + +#include "math.h" + +#include "ddk/hidsdi.h" +#include "dinput.h" +#include "hidusage.h" +#include "provider.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct effect +{ + IWineForceFeedbackEffectImpl IWineForceFeedbackEffectImpl_iface; + IForceFeedbackEffect IForceFeedbackEffect_iface; + IInspectable *IInspectable_outer; + LONG ref; + + CRITICAL_SECTION cs; + IDirectInputEffect *effect; + + GUID type; + DWORD axes[3]; + LONG directions[3]; + ULONG repeat_count; + DICONSTANTFORCE constant_force; + DIRAMPFORCE ramp_force; + DICONDITION condition; + DIPERIODIC periodic; + DIENVELOPE envelope; + DIEFFECT params; +}; + +static inline struct effect *impl_from_IWineForceFeedbackEffectImpl( IWineForceFeedbackEffectImpl *iface ) +{ + return CONTAINING_RECORD( iface, struct effect, IWineForceFeedbackEffectImpl_iface ); +} + +static HRESULT WINAPI effect_impl_QueryInterface( IWineForceFeedbackEffectImpl *iface, REFIID iid, void **out ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IWineForceFeedbackEffectImpl )) + { + IWineForceFeedbackEffectImpl_AddRef( (*out = &impl->IWineForceFeedbackEffectImpl_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IForceFeedbackEffect )) + { + IInspectable_AddRef( (*out = &impl->IForceFeedbackEffect_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI effect_impl_AddRef( IWineForceFeedbackEffectImpl *iface ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_impl_Release( IWineForceFeedbackEffectImpl *iface ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + if (impl->effect) IDirectInputEffect_Release( impl->effect ); + impl->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection( &impl->cs ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_impl_put_Parameters( IWineForceFeedbackEffectImpl *iface, WineForceFeedbackEffectParameters params, + WineForceFeedbackEffectEnvelope *envelope ) +{ + struct effect *impl = impl_from_IWineForceFeedbackEffectImpl( iface ); + DWORD count = 0; + HRESULT hr; + + TRACE( "iface %p, params %p, envelope %p.\n", iface, ¶ms, envelope ); + + EnterCriticalSection( &impl->cs ); + switch (params.type) + { + case WineForceFeedbackEffectType_Constant: + impl->repeat_count = params.constant.repeat_count; + impl->constant_force.lMagnitude = round( params.constant.gain * params.constant.direction.X * 10000 ); + impl->params.dwDuration = params.constant.duration.Duration / 10; + impl->params.dwStartDelay = params.constant.start_delay.Duration / 10; + if (impl->axes[count] == DIJOFS_X) impl->directions[count++] = round( -params.constant.direction.X * 10000 ); + if (impl->axes[count] == DIJOFS_Y) impl->directions[count++] = round( -params.constant.direction.Y * 10000 ); + if (impl->axes[count] == DIJOFS_Z) impl->directions[count++] = round( -params.constant.direction.Z * 10000 ); + break; + + case WineForceFeedbackEffectType_Ramp: + impl->repeat_count = params.ramp.repeat_count; + impl->ramp_force.lStart = round( params.ramp.gain * params.ramp.start_vector.X * 10000 ); + impl->ramp_force.lEnd = round( params.ramp.gain * params.ramp.end_vector.X * 10000 ); + impl->params.dwDuration = params.ramp.duration.Duration / 10; + impl->params.dwStartDelay = params.ramp.start_delay.Duration / 10; + if (impl->axes[count] == DIJOFS_X) impl->directions[count++] = round( -params.ramp.start_vector.X * 10000 ); + if (impl->axes[count] == DIJOFS_Y) impl->directions[count++] = round( -params.ramp.start_vector.Y * 10000 ); + if (impl->axes[count] == DIJOFS_Z) impl->directions[count++] = round( -params.ramp.start_vector.Z * 10000 ); + break; + + case WineForceFeedbackEffectType_Periodic_SineWave: + case WineForceFeedbackEffectType_Periodic_TriangleWave: + case WineForceFeedbackEffectType_Periodic_SquareWave: + case WineForceFeedbackEffectType_Periodic_SawtoothWaveDown: + case WineForceFeedbackEffectType_Periodic_SawtoothWaveUp: + impl->repeat_count = params.periodic.repeat_count; + impl->periodic.dwMagnitude = round( params.periodic.gain * 10000 ); + impl->periodic.dwPeriod = 1000000 / params.periodic.frequency; + impl->periodic.dwPhase = round( params.periodic.phase * 36000 ); + impl->periodic.lOffset = round( params.periodic.bias * 10000 ); + impl->params.dwDuration = params.periodic.duration.Duration / 10; + impl->params.dwStartDelay = params.periodic.start_delay.Duration / 10; + if (impl->axes[count] == DIJOFS_X) impl->directions[count++] = round( -params.periodic.direction.X * 10000 ); + if (impl->axes[count] == DIJOFS_Y) impl->directions[count++] = round( -params.periodic.direction.Y * 10000 ); + if (impl->axes[count] == DIJOFS_Z) impl->directions[count++] = round( -params.periodic.direction.Z * 10000 ); + break; + + case WineForceFeedbackEffectType_Condition_Spring: + case WineForceFeedbackEffectType_Condition_Damper: + case WineForceFeedbackEffectType_Condition_Inertia: + case WineForceFeedbackEffectType_Condition_Friction: + impl->repeat_count = 1; + impl->condition.lPositiveCoefficient = round( atan( params.condition.positive_coeff ) / M_PI_2 * 10000 ); + impl->condition.lNegativeCoefficient = round( atan( params.condition.negative_coeff ) / M_PI_2 * 10000 ); + impl->condition.dwPositiveSaturation = round( params.condition.max_positive_magnitude * 10000 ); + impl->condition.dwNegativeSaturation = round( params.condition.max_negative_magnitude * 10000 ); + impl->condition.lDeadBand = round( params.condition.deadzone * 10000 ); + impl->condition.lOffset = round( params.condition.bias * 10000 ); + impl->params.dwDuration = -1; + impl->params.dwStartDelay = 0; + if (impl->axes[count] == DIJOFS_X) impl->directions[count++] = round( params.condition.direction.X * 10000 ); + if (impl->axes[count] == DIJOFS_Y) impl->directions[count++] = round( params.condition.direction.Y * 10000 ); + if (impl->axes[count] == DIJOFS_Z) impl->directions[count++] = round( params.condition.direction.Z * 10000 ); + break; + } + + if (!envelope) impl->params.lpEnvelope = NULL; + else + { + impl->envelope.dwAttackTime = envelope->attack_duration.Duration / 10; + impl->envelope.dwAttackLevel = round( envelope->attack_gain * 10000 ); + impl->envelope.dwFadeTime = impl->params.dwDuration - envelope->release_duration.Duration / 10; + impl->envelope.dwFadeLevel = round( envelope->release_gain * 10000 ); + impl->params.lpEnvelope = &impl->envelope; + } + + if (!impl->effect) hr = S_OK; + else hr = IDirectInputEffect_SetParameters( impl->effect, &impl->params, DIEP_ALLPARAMS & ~DIEP_AXES ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static const struct IWineForceFeedbackEffectImplVtbl effect_impl_vtbl = +{ + effect_impl_QueryInterface, + effect_impl_AddRef, + effect_impl_Release, + /* IWineForceFeedbackEffectImpl methods */ + effect_impl_put_Parameters, +}; + +DEFINE_IINSPECTABLE_OUTER( effect, IForceFeedbackEffect, struct effect, IInspectable_outer ) + +static HRESULT WINAPI effect_get_Gain( IForceFeedbackEffect *iface, DOUBLE *value ) +{ + struct effect *impl = impl_from_IForceFeedbackEffect( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + EnterCriticalSection( &impl->cs ); + *value = impl->params.dwGain / 10000.; + LeaveCriticalSection( &impl->cs ); + + return S_OK; +} + +static HRESULT WINAPI effect_put_Gain( IForceFeedbackEffect *iface, DOUBLE value ) +{ + struct effect *impl = impl_from_IForceFeedbackEffect( iface ); + HRESULT hr; + + TRACE( "iface %p, value %f.\n", iface, value ); + + EnterCriticalSection( &impl->cs ); + impl->params.dwGain = round( value * 10000 ); + if (!impl->effect) hr = S_FALSE; + else hr = IDirectInputEffect_SetParameters( impl->effect, &impl->params, DIEP_GAIN ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI effect_get_State( IForceFeedbackEffect *iface, ForceFeedbackEffectState *value ) +{ + struct effect *impl = impl_from_IForceFeedbackEffect( iface ); + DWORD status; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + EnterCriticalSection( &impl->cs ); + if (!impl->effect) + *value = ForceFeedbackEffectState_Stopped; + else if (FAILED(hr = IDirectInputEffect_GetEffectStatus( impl->effect, &status ))) + *value = ForceFeedbackEffectState_Faulted; + else + { + if (status == DIEGES_PLAYING) *value = ForceFeedbackEffectState_Running; + else *value = ForceFeedbackEffectState_Stopped; + } + LeaveCriticalSection( &impl->cs ); + + return S_OK; +} + +static HRESULT WINAPI effect_Start( IForceFeedbackEffect *iface ) +{ + struct effect *impl = impl_from_IForceFeedbackEffect( iface ); + HRESULT hr = E_UNEXPECTED; + DWORD flags = 0; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->effect) hr = IDirectInputEffect_Start( impl->effect, impl->repeat_count, flags ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static HRESULT WINAPI effect_Stop( IForceFeedbackEffect *iface ) +{ + struct effect *impl = impl_from_IForceFeedbackEffect( iface ); + HRESULT hr = E_UNEXPECTED; + + TRACE( "iface %p.\n", iface ); + + EnterCriticalSection( &impl->cs ); + if (impl->effect) hr = IDirectInputEffect_Stop( impl->effect ); + LeaveCriticalSection( &impl->cs ); + + return hr; +} + +static const struct IForceFeedbackEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IForceFeedbackEffect methods */ + effect_get_Gain, + effect_put_Gain, + effect_get_State, + effect_Start, + effect_Stop, +}; + +HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IInspectable *outer, IWineForceFeedbackEffectImpl **out ) +{ + struct effect *impl; + + TRACE( "outer %p, out %p\n", outer, out ); + + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IWineForceFeedbackEffectImpl_iface.lpVtbl = &effect_impl_vtbl; + impl->IForceFeedbackEffect_iface.lpVtbl = &effect_vtbl; + impl->IInspectable_outer = outer; + impl->ref = 1; + + switch (type) + { + case WineForceFeedbackEffectType_Constant: + impl->type = GUID_ConstantForce; + impl->params.lpvTypeSpecificParams = &impl->constant_force; + impl->params.cbTypeSpecificParams = sizeof(impl->constant_force); + break; + + case WineForceFeedbackEffectType_Ramp: + impl->type = GUID_RampForce; + impl->params.lpvTypeSpecificParams = &impl->ramp_force; + impl->params.cbTypeSpecificParams = sizeof(impl->ramp_force); + break; + + case WineForceFeedbackEffectType_Periodic_SineWave: + impl->type = GUID_Sine; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_TriangleWave: + impl->type = GUID_Triangle; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_SquareWave: + impl->type = GUID_Square; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_SawtoothWaveDown: + impl->type = GUID_SawtoothDown; + goto WineForceFeedbackEffectType_Periodic; + case WineForceFeedbackEffectType_Periodic_SawtoothWaveUp: + impl->type = GUID_SawtoothUp; + goto WineForceFeedbackEffectType_Periodic; + WineForceFeedbackEffectType_Periodic: + impl->params.lpvTypeSpecificParams = &impl->periodic; + impl->params.cbTypeSpecificParams = sizeof(impl->periodic); + break; + + case WineForceFeedbackEffectType_Condition_Spring: + impl->type = GUID_Spring; + goto WineForceFeedbackEffectType_Condition; + case WineForceFeedbackEffectType_Condition_Damper: + impl->type = GUID_Damper; + goto WineForceFeedbackEffectType_Condition; + case WineForceFeedbackEffectType_Condition_Inertia: + impl->type = GUID_Inertia; + goto WineForceFeedbackEffectType_Condition; + case WineForceFeedbackEffectType_Condition_Friction: + impl->type = GUID_Friction; + goto WineForceFeedbackEffectType_Condition; + WineForceFeedbackEffectType_Condition: + impl->params.lpvTypeSpecificParams = &impl->condition; + impl->params.cbTypeSpecificParams = sizeof(impl->condition); + break; + } + + impl->envelope.dwSize = sizeof(DIENVELOPE); + impl->params.dwSize = sizeof(DIEFFECT); + impl->params.rgdwAxes = impl->axes; + impl->params.rglDirection = impl->directions; + impl->params.dwTriggerButton = -1; + impl->params.dwGain = 10000; + impl->params.dwFlags = DIEFF_CARTESIAN|DIEFF_OBJECTOFFSETS; + impl->params.cAxes = -1; + impl->axes[0] = DIJOFS_X; + impl->axes[1] = DIJOFS_Y; + impl->axes[2] = DIJOFS_Z; + + InitializeCriticalSection( &impl->cs ); + impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": effect.cs" ); + + *out = &impl->IWineForceFeedbackEffectImpl_iface; + TRACE( "created ForceFeedbackEffect %p\n", *out ); + return S_OK; +} + +struct motor +{ + IForceFeedbackMotor IForceFeedbackMotor_iface; + LONG ref; + + IDirectInputDevice8W *device; +}; + +static inline struct motor *impl_from_IForceFeedbackMotor( IForceFeedbackMotor *iface ) +{ + return CONTAINING_RECORD( iface, struct motor, IForceFeedbackMotor_iface ); +} + +static HRESULT WINAPI motor_QueryInterface( IForceFeedbackMotor *iface, REFIID iid, void **out ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IForceFeedbackMotor )) + { + IInspectable_AddRef( (*out = &impl->IForceFeedbackMotor_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI motor_AddRef( IForceFeedbackMotor *iface ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI motor_Release( IForceFeedbackMotor *iface ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + IDirectInputDevice8_Release( impl->device ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI motor_GetIids( IForceFeedbackMotor *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI motor_GetRuntimeClassName( IForceFeedbackMotor *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_ForceFeedbackMotor), + class_name ); +} + +static HRESULT WINAPI motor_GetTrustLevel( IForceFeedbackMotor *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI motor_get_AreEffectsPaused( IForceFeedbackMotor *iface, BOOLEAN *value ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + DWORD state; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IDirectInputDevice8_GetForceFeedbackState( impl->device, &state ))) *value = FALSE; + else *value = (state & DIGFFS_PAUSED); + + return hr; +} + +static HRESULT WINAPI motor_get_MasterGain( IForceFeedbackMotor *iface, double *value ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + DIPROPDWORD gain = + { + .diph = + { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IDirectInputDevice8_GetProperty( impl->device, DIPROP_FFGAIN, &gain.diph ))) *value = 1.; + else *value = gain.dwData / 10000.; + + return hr; +} + +static HRESULT WINAPI motor_put_MasterGain( IForceFeedbackMotor *iface, double value ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + DIPROPDWORD gain = + { + .diph = + { + .dwSize = sizeof(DIPROPDWORD), + .dwHeaderSize = sizeof(DIPROPHEADER), + .dwHow = DIPH_DEVICE, + }, + }; + + TRACE( "iface %p, value %f.\n", iface, value ); + + gain.dwData = 10000 * value; + return IDirectInputDevice8_SetProperty( impl->device, DIPROP_FFGAIN, &gain.diph ); +} + +static HRESULT WINAPI motor_get_IsEnabled( IForceFeedbackMotor *iface, BOOLEAN *value ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + DWORD state; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (FAILED(hr = IDirectInputDevice8_GetForceFeedbackState( impl->device, &state ))) *value = FALSE; + else *value = !(state & DIGFFS_ACTUATORSOFF); + + return hr; +} + +static BOOL CALLBACK check_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *args ) +{ + ForceFeedbackEffectAxes *value = args; + + if (obj->dwType & DIDFT_FFACTUATOR) + { + if (IsEqualIID( &obj->guidType, &GUID_XAxis )) *value |= ForceFeedbackEffectAxes_X; + else if (IsEqualIID( &obj->guidType, &GUID_YAxis )) *value |= ForceFeedbackEffectAxes_Y; + else if (IsEqualIID( &obj->guidType, &GUID_ZAxis )) *value |= ForceFeedbackEffectAxes_Z; + } + + return DIENUM_CONTINUE; +} + +static HRESULT WINAPI motor_get_SupportedAxes( IForceFeedbackMotor *iface, enum ForceFeedbackEffectAxes *value ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + *value = ForceFeedbackEffectAxes_None; + if (FAILED(hr = IDirectInputDevice8_EnumObjects( impl->device, check_ffb_axes, value, DIDFT_AXIS ))) + *value = ForceFeedbackEffectAxes_None; + + return hr; +} + +static HRESULT WINAPI motor_load_effect_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + struct effect *effect = impl_from_IForceFeedbackEffect( (IForceFeedbackEffect *)param ); + IForceFeedbackMotor *motor = (IForceFeedbackMotor *)invoker; + struct motor *impl = impl_from_IForceFeedbackMotor( motor ); + ForceFeedbackEffectAxes supported_axes = 0; + IDirectInputEffect *dinput_effect; + HRESULT hr; + + EnterCriticalSection( &effect->cs ); + + if (FAILED(hr = IForceFeedbackMotor_get_SupportedAxes( motor, &supported_axes ))) + { + WARN( "get_SupportedAxes for motor %p returned %#lx\n", motor, hr ); + effect->params.cAxes = 0; + } + else if (effect->params.cAxes == -1) + { + DWORD count = 0; + + /* initialize axis mapping and re-map directions that were set with the initial mapping */ + if (supported_axes & ForceFeedbackEffectAxes_X) + { + effect->directions[count] = effect->directions[0]; + effect->axes[count++] = DIJOFS_X; + } + if (supported_axes & ForceFeedbackEffectAxes_Y) + { + effect->directions[count] = effect->directions[1]; + effect->axes[count++] = DIJOFS_Y; + } + if (supported_axes & ForceFeedbackEffectAxes_Z) + { + effect->directions[count] = effect->directions[2]; + effect->axes[count++] = DIJOFS_Z; + } + + effect->params.cAxes = count; + } + + if (SUCCEEDED(hr = IDirectInputDevice8_CreateEffect( impl->device, &effect->type, &effect->params, + &dinput_effect, NULL ))) + { + if (effect->effect) IDirectInputEffect_Release( effect->effect ); + effect->effect = dinput_effect; + IDirectInputEffect_AddRef( effect->effect ); + } + + LeaveCriticalSection( &effect->cs ); + + result->vt = VT_UI4; + if (SUCCEEDED(hr)) result->ulVal = ForceFeedbackLoadEffectResult_Succeeded; + else if (hr == DIERR_DEVICEFULL) result->ulVal = ForceFeedbackLoadEffectResult_EffectStorageFull; + else result->ulVal = ForceFeedbackLoadEffectResult_EffectNotSupported; + + return hr; +} + +static HRESULT WINAPI motor_LoadEffectAsync( IForceFeedbackMotor *iface, IForceFeedbackEffect *effect, + IAsyncOperation_ForceFeedbackLoadEffectResult **async_op ) +{ + TRACE( "iface %p, effect %p, async_op %p.\n", iface, effect, async_op ); + return async_operation_effect_result_create( (IUnknown *)iface, (IUnknown *)effect, motor_load_effect_async, async_op ); +} + +static HRESULT WINAPI motor_PauseAllEffects( IForceFeedbackMotor *iface ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + + TRACE( "iface %p.\n", iface ); + + return IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_PAUSE ); +} + +static HRESULT WINAPI motor_ResumeAllEffects( IForceFeedbackMotor *iface ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + + TRACE( "iface %p.\n", iface ); + + return IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_CONTINUE ); +} + +static HRESULT WINAPI motor_StopAllEffects( IForceFeedbackMotor *iface ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( iface ); + + TRACE( "iface %p.\n", iface ); + + return IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_STOPALL ); +} + +static HRESULT WINAPI motor_try_disable_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)invoker ); + HRESULT hr; + + hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_SETACTUATORSOFF ); + result->vt = VT_BOOL; + result->boolVal = SUCCEEDED(hr); + + return hr; +} + +static HRESULT WINAPI motor_TryDisableAsync( IForceFeedbackMotor *iface, IAsyncOperation_boolean **async_op ) +{ + TRACE( "iface %p, async_op %p.\n", iface, async_op ); + return async_operation_boolean_create( (IUnknown *)iface, NULL, motor_try_disable_async, async_op ); +} + +static HRESULT WINAPI motor_try_enable_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)invoker ); + HRESULT hr; + + hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_SETACTUATORSON ); + result->vt = VT_BOOL; + result->boolVal = SUCCEEDED(hr); + + return hr; +} + +static HRESULT WINAPI motor_TryEnableAsync( IForceFeedbackMotor *iface, IAsyncOperation_boolean **async_op ) +{ + TRACE( "iface %p, async_op %p.\n", iface, async_op ); + return async_operation_boolean_create( (IUnknown *)iface, NULL, motor_try_enable_async, async_op ); +} + +static HRESULT WINAPI motor_try_reset_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ) +{ + struct motor *impl = impl_from_IForceFeedbackMotor( (IForceFeedbackMotor *)invoker ); + HRESULT hr; + + hr = IDirectInputDevice8_SendForceFeedbackCommand( impl->device, DISFFC_RESET ); + result->vt = VT_BOOL; + result->boolVal = SUCCEEDED(hr); + + return hr; +} + +static HRESULT WINAPI motor_TryResetAsync( IForceFeedbackMotor *iface, IAsyncOperation_boolean **async_op ) +{ + TRACE( "iface %p, async_op %p.\n", iface, async_op ); + return async_operation_boolean_create( (IUnknown *)iface, NULL, motor_try_reset_async, async_op ); +} + +static HRESULT WINAPI motor_unload_effect_async( IUnknown *iface, IUnknown *param, PROPVARIANT *result ) +{ + struct effect *effect = impl_from_IForceFeedbackEffect( (IForceFeedbackEffect *)param ); + IDirectInputEffect *dinput_effect; + HRESULT hr; + + EnterCriticalSection( &effect->cs ); + dinput_effect = effect->effect; + effect->effect = NULL; + LeaveCriticalSection( &effect->cs ); + + if (!dinput_effect) hr = S_OK; + else + { + hr = IDirectInputEffect_Unload( dinput_effect ); + IDirectInputEffect_Release( dinput_effect ); + } + + result->vt = VT_BOOL; + result->boolVal = SUCCEEDED(hr); + return hr; +} + +static HRESULT WINAPI motor_TryUnloadEffectAsync( IForceFeedbackMotor *iface, IForceFeedbackEffect *effect, + IAsyncOperation_boolean **async_op ) +{ + struct effect *impl = impl_from_IForceFeedbackEffect( (IForceFeedbackEffect *)effect ); + HRESULT hr = S_OK; + + TRACE( "iface %p, effect %p, async_op %p.\n", iface, effect, async_op ); + + EnterCriticalSection( &impl->cs ); + if (!impl->effect) hr = E_FAIL; + LeaveCriticalSection( &impl->cs ); + if (FAILED(hr)) return hr; + + return async_operation_boolean_create( (IUnknown *)iface, (IUnknown *)effect, motor_unload_effect_async, async_op ); +} + +static const struct IForceFeedbackMotorVtbl motor_vtbl = +{ + motor_QueryInterface, + motor_AddRef, + motor_Release, + /* IInspectable methods */ + motor_GetIids, + motor_GetRuntimeClassName, + motor_GetTrustLevel, + /* IForceFeedbackMotor methods */ + motor_get_AreEffectsPaused, + motor_get_MasterGain, + motor_put_MasterGain, + motor_get_IsEnabled, + motor_get_SupportedAxes, + motor_LoadEffectAsync, + motor_PauseAllEffects, + motor_ResumeAllEffects, + motor_StopAllEffects, + motor_TryDisableAsync, + motor_TryEnableAsync, + motor_TryResetAsync, + motor_TryUnloadEffectAsync, +}; + +HRESULT force_feedback_motor_create( IDirectInputDevice8W *device, IForceFeedbackMotor **out ) +{ + struct motor *impl; + HRESULT hr; + + TRACE( "device %p, out %p\n", device, out ); + + if (FAILED(hr = IDirectInputDevice8_Unacquire( device ))) goto failed; + if (FAILED(hr = IDirectInputDevice8_SetCooperativeLevel( device, GetDesktopWindow(), DISCL_BACKGROUND | DISCL_EXCLUSIVE ))) goto failed; + if (FAILED(hr = IDirectInputDevice8_Acquire( device ))) goto failed; + + if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY; + impl->IForceFeedbackMotor_iface.lpVtbl = &motor_vtbl; + impl->ref = 1; + + IDirectInputDevice_AddRef( device ); + impl->device = device; + + *out = &impl->IForceFeedbackMotor_iface; + TRACE( "created ForceFeedbackMotor %p\n", *out ); + return S_OK; + +failed: + IDirectInputDevice8_SetCooperativeLevel( device, 0, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE ); + IDirectInputDevice8_Acquire( device ); + WARN( "Failed to acquire device exclusively, hr %#lx\n", hr ); + return hr; +} diff --git a/dlls/windows.gaming.input/gamepad.c b/dlls/windows.gaming.input/gamepad.c index 0c38fb5cd1a..8dab9a62d09 100644 --- wine/dlls/windows.gaming.input/gamepad.c +++ wine/dlls/windows.gaming.input/gamepad.c @@ -61,6 +61,7 @@ struct gamepad IGameControllerImpl IGameControllerImpl_iface; IGameControllerInputSink IGameControllerInputSink_iface; IGamepad IGamepad_iface; + IGamepad2 IGamepad2_iface; IGameController *IGameController_outer; LONG ref; @@ -99,7 +100,13 @@ static HRESULT WINAPI controller_QueryInterface( IGameControllerImpl *iface, REF return S_OK; } - WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + if (IsEqualGUID( iid, &IID_IGamepad2 )) + { + IInspectable_AddRef( (*out = &impl->IGamepad2_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -330,6 +337,28 @@ static const struct IGamepadVtbl gamepad_vtbl = gamepad_GetCurrentReading, }; +DEFINE_IINSPECTABLE_OUTER( gamepad2, IGamepad2, struct gamepad, IGameController_outer ) + +static HRESULT WINAPI gamepad2_GetButtonLabel(IGamepad2 *iface, GamepadButtons button, GameControllerButtonLabel *value) +{ + FIXME( "iface %p, button %#x, value %p stub!\n", iface, button, value ); + *value = GameControllerButtonLabel_None; + return S_OK; +} + +static const struct IGamepad2Vtbl gamepad2_vtbl = +{ + gamepad2_QueryInterface, + gamepad2_AddRef, + gamepad2_Release, + /* IInspectable methods */ + gamepad2_GetIids, + gamepad2_GetRuntimeClassName, + gamepad2_GetTrustLevel, + /* IGamepad2 methods */ + gamepad2_GetButtonLabel, +}; + struct gamepad_statics { IActivationFactory IActivationFactory_iface; @@ -542,6 +571,7 @@ static HRESULT WINAPI controller_factory_CreateGameController( ICustomGameContro impl->IGameControllerImpl_iface.lpVtbl = &controller_vtbl; impl->IGameControllerInputSink_iface.lpVtbl = &input_sink_vtbl; impl->IGamepad_iface.lpVtbl = &gamepad_vtbl; + impl->IGamepad2_iface.lpVtbl = &gamepad2_vtbl; impl->ref = 1; TRACE( "created Gamepad %p\n", impl ); diff --git a/dlls/windows.gaming.input/main.c b/dlls/windows.gaming.input/main.c index 21808d9c2ad..a20630cd20b 100644 --- wine/dlls/windows.gaming.input/main.c +++ wine/dlls/windows.gaming.input/main.c @@ -185,6 +185,15 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory ** if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_Custom_GameControllerFactoryManager )) IGameControllerFactoryManagerStatics2_QueryInterface( manager_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConstantForceEffect )) + IInspectable_QueryInterface( constant_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect )) + IInspectable_QueryInterface( ramp_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect )) + IInspectable_QueryInterface( periodic_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_Input_ForceFeedback_ConditionForceEffect )) + IInspectable_QueryInterface( condition_effect_factory, &IID_IActivationFactory, (void **)factory ); + if (*factory) return S_OK; return CLASS_E_CLASSNOTAVAILABLE; } diff --git a/dlls/windows.gaming.input/periodic_effect.c b/dlls/windows.gaming.input/periodic_effect.c new file mode 100644 index 00000000000..8633a8fb9b9 --- /dev/null +++ wine/dlls/windows.gaming.input/periodic_effect.c @@ -0,0 +1,326 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct periodic_effect +{ + IPeriodicForceEffect IPeriodicForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; + + PeriodicForceEffectKind kind; +}; + +static inline struct periodic_effect *impl_from_IPeriodicForceEffect( IPeriodicForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct periodic_effect, IPeriodicForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IPeriodicForceEffect *iface, REFIID iid, void **out ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IPeriodicForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IPeriodicForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IPeriodicForceEffect *iface ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IPeriodicForceEffect *iface ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IPeriodicForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IPeriodicForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_PeriodicForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IPeriodicForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_get_Kind( IPeriodicForceEffect *iface, PeriodicForceEffectKind *kind ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + TRACE( "iface %p, kind %p.\n", iface, kind ); + *kind = impl->kind; + return S_OK; +} + +static HRESULT WINAPI effect_SetParameters( IPeriodicForceEffect *iface, Vector3 direction, FLOAT frequency, FLOAT phase, + FLOAT bias, TimeSpan duration ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + WineForceFeedbackEffectParameters params = + { + .periodic = + { + .type = WineForceFeedbackEffectType_Periodic_SquareWave + impl->kind, + .direction = direction, + .frequency = frequency, + .phase = phase, + .bias = bias, + .duration = duration, + .repeat_count = 1, + .gain = 1., + }, + }; + + TRACE( "iface %p, direction %s, frequency %f, phase %f, bias %f, duration %I64u.\n", iface, + debugstr_vector3( &direction ), frequency, phase, bias, duration.Duration ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, NULL ); +} + +static HRESULT WINAPI effect_SetParametersWithEnvelope( IPeriodicForceEffect *iface, Vector3 direction, FLOAT frequency, FLOAT phase, FLOAT bias, + FLOAT attack_gain, FLOAT sustain_gain, FLOAT release_gain, TimeSpan start_delay, + TimeSpan attack_duration, TimeSpan sustain_duration, + TimeSpan release_duration, UINT32 repeat_count ) +{ + struct periodic_effect *impl = impl_from_IPeriodicForceEffect( iface ); + WineForceFeedbackEffectParameters params = + { + .periodic = + { + .type = WineForceFeedbackEffectType_Periodic_SquareWave + impl->kind, + .direction = direction, + .frequency = frequency, + .phase = phase, + .bias = bias, + .duration = {attack_duration.Duration + sustain_duration.Duration + release_duration.Duration}, + .start_delay = start_delay, + .repeat_count = repeat_count, + .gain = sustain_gain, + }, + }; + WineForceFeedbackEffectEnvelope envelope = + { + .attack_gain = attack_gain, + .release_gain = release_gain, + .attack_duration = attack_duration, + .release_duration = release_duration, + }; + + TRACE( "iface %p, direction %s, frequency %f, phase %f, bias %f, attack_gain %f, sustain_gain %f, release_gain %f, start_delay %I64u, " + "attack_duration %I64u, sustain_duration %I64u, release_duration %I64u, repeat_count %u.\n", iface, debugstr_vector3( &direction ), + frequency, phase, bias, attack_gain, sustain_gain, release_gain, start_delay.Duration, attack_duration.Duration, sustain_duration.Duration, + release_duration.Duration, repeat_count ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, &envelope ); +} + +static const struct IPeriodicForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IPeriodicForceEffect methods */ + effect_get_Kind, + effect_SetParameters, + effect_SetParametersWithEnvelope, +}; + +struct periodic_factory +{ + IActivationFactory IActivationFactory_iface; + IPeriodicForceEffectFactory IPeriodicForceEffectFactory_iface; + LONG ref; +}; + +static inline struct periodic_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct periodic_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct periodic_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IPeriodicForceEffectFactory )) + { + IInspectable_AddRef( (*out = &impl->IPeriodicForceEffectFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct periodic_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct periodic_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +DEFINE_IINSPECTABLE( factory, IPeriodicForceEffectFactory, struct periodic_factory, IActivationFactory_iface ) + +static HRESULT WINAPI factory_CreateInstance( IPeriodicForceEffectFactory *iface, enum PeriodicForceEffectKind kind, IForceFeedbackEffect **out ) +{ + enum WineForceFeedbackEffectType type = WineForceFeedbackEffectType_Periodic + kind; + struct periodic_effect *impl; + HRESULT hr; + + TRACE( "iface %p, kind %u, out %p.\n", iface, kind, out ); + + if (!(impl = calloc( 1, sizeof(struct periodic_effect) ))) return E_OUTOFMEMORY; + impl->IPeriodicForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + impl->kind = kind; + + if (FAILED(hr = force_feedback_effect_create( type, (IInspectable *)&impl->IPeriodicForceEffect_iface, &impl->IWineForceFeedbackEffectImpl_inner )) || + FAILED(hr = IPeriodicForceEffect_QueryInterface( &impl->IPeriodicForceEffect_iface, &IID_IForceFeedbackEffect, (void **)out ))) + { + if (impl->IWineForceFeedbackEffectImpl_inner) IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + return hr; + } + + IPeriodicForceEffect_Release( &impl->IPeriodicForceEffect_iface ); + TRACE( "created PeriodicForceEffect %p\n", *out ); + return S_OK; +} + +static const struct IPeriodicForceEffectFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IPeriodicForceEffectFactory methods */ + factory_CreateInstance, +}; + +static struct periodic_factory periodic_statics = +{ + {&activation_vtbl}, + {&factory_vtbl}, + 1, +}; + +IInspectable *periodic_effect_factory = (IInspectable *)&periodic_statics.IActivationFactory_iface; diff --git a/dlls/windows.gaming.input/private.h b/dlls/windows.gaming.input/private.h index 58b2040d3de..f53d5b5bc37 100644 --- wine/dlls/windows.gaming.input/private.h +++ wine/dlls/windows.gaming.input/private.h @@ -25,11 +25,13 @@ #include "winbase.h" #include "winstring.h" #include "objbase.h" +#include "dinput.h" #include "activation.h" #define WIDL_using_Windows_Foundation #define WIDL_using_Windows_Foundation_Collections +#define WIDL_using_Windows_Foundation_Numerics #include "windows.foundation.h" #define WIDL_using_Windows_Devices_Power #define WIDL_using_Windows_Gaming_Input @@ -37,13 +39,20 @@ #define WIDL_using_Windows_Gaming_Input_ForceFeedback #include "windows.gaming.input.custom.h" +#include "wine/debug.h" #include "wine/list.h" +#include "provider.h" + extern HINSTANCE windows_gaming_input; extern ICustomGameControllerFactory *controller_factory; extern ICustomGameControllerFactory *gamepad_factory; extern ICustomGameControllerFactory *racing_wheel_factory; extern IGameControllerFactoryManagerStatics2 *manager_factory; +extern IInspectable *constant_effect_factory; +extern IInspectable *ramp_effect_factory; +extern IInspectable *periodic_effect_factory; +extern IInspectable *condition_effect_factory; struct vector_iids { @@ -64,6 +73,15 @@ extern HRESULT event_handlers_append( struct list *list, IEventHandler_IInspecta extern HRESULT event_handlers_remove( struct list *list, EventRegistrationToken *token ); extern void event_handlers_notify( struct list *list, IInspectable *element ); +extern HRESULT force_feedback_motor_create( IDirectInputDevice8W *device, IForceFeedbackMotor **out ); +extern HRESULT force_feedback_effect_create( enum WineForceFeedbackEffectType type, IInspectable *outer, IWineForceFeedbackEffectImpl **out ); + +typedef HRESULT (WINAPI *async_operation_callback)( IUnknown *invoker, IUnknown *param, PROPVARIANT *result ); +extern HRESULT async_operation_boolean_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_boolean **out ); +extern HRESULT async_operation_effect_result_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback, + IAsyncOperation_ForceFeedbackLoadEffectResult **out ); + #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ static inline impl_type *impl_from( iface_type *iface ) \ { \ @@ -103,3 +121,9 @@ extern void event_handlers_notify( struct list *list, IInspectable *element ); DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface ) #define DEFINE_IINSPECTABLE_OUTER( pfx, iface_type, impl_type, outer_iface ) \ DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, impl->outer_iface ) + +static inline const char *debugstr_vector3( const Vector3 *vector ) +{ + if (!vector) return "(null)"; + return wine_dbg_sprintf( "[%f, %f, %f]", vector->X, vector->Y, vector->Z ); +} diff --git a/dlls/windows.gaming.input/provider.c b/dlls/windows.gaming.input/provider.c index 69098e8abb6..d0472727224 100644 --- wine/dlls/windows.gaming.input/provider.c +++ wine/dlls/windows.gaming.input/provider.c @@ -141,21 +141,37 @@ static HRESULT WINAPI wine_provider_GetTrustLevel( IWineGameControllerProvider * return E_NOTIMPL; } +static BOOL CALLBACK count_ffb_axes( const DIDEVICEOBJECTINSTANCEW *obj, void *args ) +{ + DWORD *count = args; + if (obj->dwType & DIDFT_FFACTUATOR) (*count)++; + return DIENUM_CONTINUE; +} + static HRESULT WINAPI wine_provider_get_Type( IWineGameControllerProvider *iface, WineGameControllerType *value ) { struct provider *impl = impl_from_IWineGameControllerProvider( iface ); DIDEVICEINSTANCEW instance = {.dwSize = sizeof(DIDEVICEINSTANCEW)}; + const WCHAR *tmp; HRESULT hr; TRACE( "iface %p, value %p.\n", iface, value ); if (FAILED(hr = IDirectInputDevice8_GetDeviceInfo( impl->dinput_device, &instance ))) return hr; - switch (GET_DIDEVICE_TYPE( instance.dwDevType )) + if ((tmp = wcschr( impl->device_path + 8, '#' )) && !wcsnicmp( tmp - 6, L"&XI_", 4 )) + *value = WineGameControllerType_Gamepad; + else switch (GET_DIDEVICE_TYPE( instance.dwDevType )) { case DI8DEVTYPE_DRIVING: *value = WineGameControllerType_RacingWheel; break; - case DI8DEVTYPE_GAMEPAD: *value = WineGameControllerType_Gamepad; break; - default: *value = WineGameControllerType_Joystick; break; + default: + { + DWORD count = 0; + hr = IDirectInputDevice8_EnumObjects( impl->dinput_device, count_ffb_axes, &count, DIDFT_AXIS ); + if (SUCCEEDED(hr) && count == 1) *value = WineGameControllerType_RacingWheel; + else *value = WineGameControllerType_Joystick; + break; + } } return S_OK; @@ -212,7 +228,7 @@ static HRESULT WINAPI wine_provider_get_State( IWineGameControllerProvider *ifac if (FAILED(hr = IDirectInputDevice8_GetDeviceState( impl->dinput_device, sizeof(state), &state ))) { WARN( "Failed to read device state, hr %#lx\n", hr ); - return hr; + return S_OK; } i = ARRAY_SIZE(state.rgbButtons); @@ -315,6 +331,21 @@ static HRESULT WINAPI wine_provider_put_Vibration( IWineGameControllerProvider * return S_OK; } +static HRESULT WINAPI wine_provider_get_ForceFeedbackMotor( IWineGameControllerProvider *iface, IForceFeedbackMotor **value ) +{ + struct provider *impl = impl_from_IWineGameControllerProvider( iface ); + DIDEVCAPS caps = {.dwSize = sizeof(DIDEVCAPS)}; + HRESULT hr; + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (SUCCEEDED(hr = IDirectInputDevice8_GetCapabilities( impl->dinput_device, &caps )) && (caps.dwFlags & DIDC_FORCEFEEDBACK)) + return force_feedback_motor_create( impl->dinput_device, value ); + + *value = NULL; + return S_OK; +} + static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = { wine_provider_QueryInterface, @@ -332,6 +363,7 @@ static const struct IWineGameControllerProviderVtbl wine_provider_vtbl = wine_provider_get_State, wine_provider_get_Vibration, wine_provider_put_Vibration, + wine_provider_get_ForceFeedbackMotor, }; DEFINE_IINSPECTABLE( game_provider, IGameControllerProvider, struct provider, IWineGameControllerProvider_iface ) @@ -556,7 +588,7 @@ void provider_create( const WCHAR *device_path ) EnterCriticalSection( &provider_cs ); LIST_FOR_EACH_ENTRY( entry, &provider_list, struct provider, entry ) - if ((found = !wcscmp( entry->device_path, device_path ))) break; + if ((found = !wcsicmp( entry->device_path, device_path ))) break; if (!found) list_add_tail( &provider_list, &impl->entry ); LeaveCriticalSection( &provider_cs ); @@ -576,11 +608,12 @@ void provider_remove( const WCHAR *device_path ) EnterCriticalSection( &provider_cs ); LIST_FOR_EACH_ENTRY( entry, &provider_list, struct provider, entry ) - if ((found = !wcscmp( entry->device_path, device_path ))) break; + if ((found = !wcsicmp( entry->device_path, device_path ))) break; if (found) list_remove( &entry->entry ); LeaveCriticalSection( &provider_cs ); - if (found) + if (!found) WARN( "provider not found for device %s\n", debugstr_w( device_path ) ); + else { provider = &entry->IGameControllerProvider_iface; manager_on_provider_removed( provider ); diff --git a/dlls/windows.gaming.input/provider.idl b/dlls/windows.gaming.input/provider.idl index 865a149eaa5..e7b6e96b8aa 100644 --- wine/dlls/windows.gaming.input/provider.idl +++ wine/dlls/windows.gaming.input/provider.idl @@ -22,6 +22,7 @@ #pragma winrt ns_prefix #endif +import "propidl.idl"; import "inspectable.idl"; import "asyncinfo.idl"; import "eventtoken.idl"; @@ -29,14 +30,25 @@ import "windowscontracts.idl"; import "windows.foundation.idl"; import "windows.gaming.input.idl"; import "windows.gaming.input.custom.idl"; +import "windows.gaming.input.forcefeedback.idl"; namespace Windows.Gaming.Input.Custom { typedef enum WineGameControllerType WineGameControllerType; + typedef enum WineForceFeedbackEffectType WineForceFeedbackEffectType; typedef struct WineGameControllerState WineGameControllerState; typedef struct WineGameControllerVibration WineGameControllerVibration; + typedef struct WineConditionEffectParameters WineConditionEffectParameters; + typedef struct WineConstantEffectParameters WineConstantEffectParameters; + typedef struct WineRampEffectParameters WineRampEffectParameters; + typedef struct WinePeriodicEffectParameters WinePeriodicEffectParameters; + typedef struct WineForceFeedbackEffectEnvelope WineForceFeedbackEffectEnvelope; + typedef union WineForceFeedbackEffectParameters WineForceFeedbackEffectParameters; interface IWineGameControllerProvider; runtimeclass WineGameControllerProvider; + /* type-pruning version of AsyncOperationCompletedHandler */ + delegate HRESULT WineAsyncOperationCompletedHandler([in] IInspectable *async, [in] AsyncStatus status); + enum WineGameControllerType { Joystick = 0, @@ -44,6 +56,27 @@ namespace Windows.Gaming.Input.Custom { RacingWheel = 2, }; + enum WineForceFeedbackEffectType + { + Constant = 1, + Ramp = 2, + + Periodic = 10, + /* same order as PeriodicForceEffectKind */ + Periodic_SquareWave = 10, + Periodic_SineWave = 11, + Periodic_TriangleWave = 12, + Periodic_SawtoothWaveUp = 13, + Periodic_SawtoothWaveDown = 14, + + Condition = 20, + /* same order as ConditionForceEffectKind */ + Condition_Spring = 20, + Condition_Damper = 21, + Condition_Inertia = 22, + Condition_Friction = 23, + }; + struct WineGameControllerState { UINT64 timestamp; @@ -60,6 +93,69 @@ namespace Windows.Gaming.Input.Custom { UINT16 right; }; + struct WineConditionEffectParameters + { + WineForceFeedbackEffectType type; + Windows.Foundation.Numerics.Vector3 direction; + FLOAT positive_coeff; + FLOAT negative_coeff; + FLOAT max_positive_magnitude; + FLOAT max_negative_magnitude; + FLOAT deadzone; + FLOAT bias; + }; + + struct WineConstantEffectParameters + { + WineForceFeedbackEffectType type; + Windows.Foundation.Numerics.Vector3 direction; + Windows.Foundation.TimeSpan duration; + Windows.Foundation.TimeSpan start_delay; + UINT32 repeat_count; + FLOAT gain; + }; + + struct WineRampEffectParameters + { + WineForceFeedbackEffectType type; + Windows.Foundation.Numerics.Vector3 start_vector; + Windows.Foundation.Numerics.Vector3 end_vector; + Windows.Foundation.TimeSpan duration; + Windows.Foundation.TimeSpan start_delay; + UINT32 repeat_count; + FLOAT gain; + }; + + struct WinePeriodicEffectParameters + { + WineForceFeedbackEffectType type; + Windows.Foundation.Numerics.Vector3 direction; + Windows.Foundation.TimeSpan duration; + Windows.Foundation.TimeSpan start_delay; + UINT32 repeat_count; + FLOAT frequency; + FLOAT phase; + FLOAT bias; + FLOAT gain; + }; + + struct WineForceFeedbackEffectEnvelope + { + FLOAT attack_gain; + FLOAT release_gain; + Windows.Foundation.TimeSpan attack_duration; + Windows.Foundation.TimeSpan release_duration; + }; + + union WineForceFeedbackEffectParameters + { + WineForceFeedbackEffectType type; + WineConditionEffectParameters condition; + WineConstantEffectParameters constant; + WineRampEffectParameters ramp; + WinePeriodicEffectParameters periodic; + }; + [ uuid(06e58977-7684-4dc5-bad1-cda52a4aa06d) ] @@ -85,6 +181,29 @@ namespace Windows.Gaming.Input.Custom { [propget] HRESULT State([out, retval] WineGameControllerState *state); [propget] HRESULT Vibration([out, retval] WineGameControllerVibration *vibration); [propput] HRESULT Vibration([in] WineGameControllerVibration vibration); + + [propget] HRESULT ForceFeedbackMotor([out, retval] Windows.Gaming.Input.ForceFeedback.ForceFeedbackMotor **motor); + } + + [ + uuid(27833469-7760-417e-adbe-e011a66e16ee) + ] + interface IWineForceFeedbackEffectImpl : IUnknown + requires Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect + { + [propput] HRESULT Parameters([in] WineForceFeedbackEffectParameters parameters, + [in, optional] WineForceFeedbackEffectEnvelope *envelope); + } + + [ + uuid(83f377ee-c799-11ec-9d64-0242ac120002) + ] + interface IWineAsyncInfoImpl : IUnknown + { + [propput] HRESULT Completed([in] WineAsyncOperationCompletedHandler *handler); + [propget] HRESULT Completed([out, retval] WineAsyncOperationCompletedHandler **handler); + [propget] HRESULT Result([out, retval] PROPVARIANT *result); + HRESULT Start(); } [ diff --git a/dlls/windows.gaming.input/racing_wheel.c b/dlls/windows.gaming.input/racing_wheel.c index b4635d03153..d646ca26c03 100644 --- wine/dlls/windows.gaming.input/racing_wheel.c +++ wine/dlls/windows.gaming.input/racing_wheel.c @@ -99,7 +99,7 @@ static HRESULT WINAPI controller_QueryInterface( IGameControllerImpl *iface, REF return S_OK; } - WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -245,8 +245,11 @@ static HRESULT WINAPI racing_wheel_get_MaxWheelAngle( IRacingWheel *iface, DOUBL static HRESULT WINAPI racing_wheel_get_WheelMotor( IRacingWheel *iface, IForceFeedbackMotor **value ) { - FIXME( "iface %p, value %p stub!\n", iface, value ); - return E_NOTIMPL; + struct racing_wheel *impl = impl_from_IRacingWheel( iface ); + + TRACE( "iface %p, value %p\n", iface, value ); + + return IWineGameControllerProvider_get_ForceFeedbackMotor( impl->wine_provider, value ); } static HRESULT WINAPI racing_wheel_GetButtonLabel( IRacingWheel *iface, enum RacingWheelButtons button, diff --git a/dlls/windows.gaming.input/ramp_effect.c b/dlls/windows.gaming.input/ramp_effect.c new file mode 100644 index 00000000000..fadcf151c04 --- /dev/null +++ wine/dlls/windows.gaming.input/ramp_effect.c @@ -0,0 +1,278 @@ +/* WinRT Windows.Gaming.Input implementation + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "private.h" +#include "provider.h" + +WINE_DEFAULT_DEBUG_CHANNEL(input); + +struct ramp_effect +{ + IRampForceEffect IRampForceEffect_iface; + IWineForceFeedbackEffectImpl *IWineForceFeedbackEffectImpl_inner; + LONG ref; +}; + +static inline struct ramp_effect *impl_from_IRampForceEffect( IRampForceEffect *iface ) +{ + return CONTAINING_RECORD( iface, struct ramp_effect, IRampForceEffect_iface ); +} + +static HRESULT WINAPI effect_QueryInterface( IRampForceEffect *iface, REFIID iid, void **out ) +{ + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IRampForceEffect )) + { + IInspectable_AddRef( (*out = &impl->IRampForceEffect_iface) ); + return S_OK; + } + + return IWineForceFeedbackEffectImpl_QueryInterface( impl->IWineForceFeedbackEffectImpl_inner, iid, out ); +} + +static ULONG WINAPI effect_AddRef( IRampForceEffect *iface ) +{ + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI effect_Release( IRampForceEffect *iface ) +{ + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + + if (!ref) + { + /* guard against re-entry if inner releases an outer iface */ + InterlockedIncrement( &impl->ref ); + IWineForceFeedbackEffectImpl_Release( impl->IWineForceFeedbackEffectImpl_inner ); + free( impl ); + } + + return ref; +} + +static HRESULT WINAPI effect_GetIids( IRampForceEffect *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_GetRuntimeClassName( IRampForceEffect *iface, HSTRING *class_name ) +{ + return WindowsCreateString( RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect, + ARRAY_SIZE(RuntimeClass_Windows_Gaming_Input_ForceFeedback_RampForceEffect), + class_name ); +} + +static HRESULT WINAPI effect_GetTrustLevel( IRampForceEffect *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI effect_SetParameters( IRampForceEffect *iface, Vector3 start_vector, Vector3 end_vector, TimeSpan duration ) +{ + WineForceFeedbackEffectParameters params = + { + .ramp = + { + .type = WineForceFeedbackEffectType_Ramp, + .start_vector = start_vector, + .end_vector = end_vector, + .duration = duration, + .repeat_count = 1, + .gain = 1., + }, + }; + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + + TRACE( "iface %p, start_vector %s, end_vector %s, duration %I64u.\n", iface, + debugstr_vector3( &start_vector ), debugstr_vector3( &end_vector ), duration.Duration ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, NULL ); +} + +static HRESULT WINAPI effect_SetParametersWithEnvelope( IRampForceEffect *iface, Vector3 start_vector, Vector3 end_vector, FLOAT attack_gain, + FLOAT sustain_gain, FLOAT release_gain, TimeSpan start_delay, + TimeSpan attack_duration, TimeSpan sustain_duration, + TimeSpan release_duration, UINT32 repeat_count ) +{ + WineForceFeedbackEffectParameters params = + { + .ramp = + { + .type = WineForceFeedbackEffectType_Ramp, + .start_vector = start_vector, + .end_vector = end_vector, + .duration = {attack_duration.Duration + sustain_duration.Duration + release_duration.Duration}, + .start_delay = start_delay, + .repeat_count = repeat_count, + .gain = sustain_gain, + }, + }; + WineForceFeedbackEffectEnvelope envelope = + { + .attack_gain = attack_gain, + .release_gain = release_gain, + .attack_duration = attack_duration, + .release_duration = release_duration, + }; + struct ramp_effect *impl = impl_from_IRampForceEffect( iface ); + + TRACE( "iface %p, start_vector %s, end_vector %s, attack_gain %f, sustain_gain %f, release_gain %f, start_delay %I64u, attack_duration %I64u, " + "sustain_duration %I64u, release_duration %I64u, repeat_count %u.\n", iface, debugstr_vector3( &start_vector ), debugstr_vector3( &end_vector ), + attack_gain, sustain_gain, release_gain, start_delay.Duration, attack_duration.Duration, sustain_duration.Duration, + release_duration.Duration, repeat_count ); + + return IWineForceFeedbackEffectImpl_put_Parameters( impl->IWineForceFeedbackEffectImpl_inner, params, &envelope ); +} + +static const struct IRampForceEffectVtbl effect_vtbl = +{ + effect_QueryInterface, + effect_AddRef, + effect_Release, + /* IInspectable methods */ + effect_GetIids, + effect_GetRuntimeClassName, + effect_GetTrustLevel, + /* IRampForceEffect methods */ + effect_SetParameters, + effect_SetParametersWithEnvelope, +}; + +struct ramp_factory +{ + IActivationFactory IActivationFactory_iface; + LONG ref; +}; + +static inline struct ramp_factory *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct ramp_factory, IActivationFactory_iface ); +} + +static HRESULT WINAPI activation_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct ramp_factory *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI activation_AddRef( IActivationFactory *iface ) +{ + struct ramp_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI activation_Release( IActivationFactory *iface ) +{ + struct ramp_factory *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI activation_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI activation_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + struct ramp_effect *impl; + HRESULT hr; + + TRACE( "iface %p, instance %p.\n", iface, instance ); + + if (!(impl = calloc( 1, sizeof(struct ramp_effect) ))) return E_OUTOFMEMORY; + impl->IRampForceEffect_iface.lpVtbl = &effect_vtbl; + impl->ref = 1; + + if (FAILED(hr = force_feedback_effect_create( WineForceFeedbackEffectType_Ramp, (IInspectable *)&impl->IRampForceEffect_iface, + &impl->IWineForceFeedbackEffectImpl_inner ))) + { + free( impl ); + return hr; + } + + *instance = (IInspectable *)&impl->IRampForceEffect_iface; + TRACE( "created RampForceEffect %p\n", *instance ); + return S_OK; +} + +static const struct IActivationFactoryVtbl activation_vtbl = +{ + activation_QueryInterface, + activation_AddRef, + activation_Release, + /* IInspectable methods */ + activation_GetIids, + activation_GetRuntimeClassName, + activation_GetTrustLevel, + /* IActivationFactory methods */ + activation_ActivateInstance, +}; + +static struct ramp_factory ramp_statics = +{ + {&activation_vtbl}, + 1, +}; + +IInspectable *ramp_effect_factory = (IInspectable *)&ramp_statics.IActivationFactory_iface; diff --git a/dlls/windows.gaming.input/vector.c b/dlls/windows.gaming.input/vector.c index db1a9057682..8958b07c0f2 100644 --- wine/dlls/windows.gaming.input/vector.c +++ wine/dlls/windows.gaming.input/vector.c @@ -54,7 +54,7 @@ static HRESULT WINAPI iterator_QueryInterface( IIterator_IInspectable *iface, RE return S_OK; } - WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -189,7 +189,7 @@ static HRESULT WINAPI vector_view_QueryInterface( IVectorView_IInspectable *ifac return S_OK; } - WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } @@ -384,7 +384,7 @@ static HRESULT WINAPI vector_QueryInterface( IVector_IInspectable *iface, REFIID return S_OK; } - WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); *out = NULL; return E_NOINTERFACE; } diff --git a/include/Makefile.in b/include/Makefile.in index 19d0088e431..cade5bb49dd 100644 --- wine/include/Makefile.in +++ wine/include/Makefile.in @@ -788,8 +788,10 @@ SOURCES = \ windows.foundation.collections.idl \ windows.foundation.idl \ windows.foundation.metadata.idl \ + windows.foundation.numerics.idl \ windows.gaming.input.custom.idl \ windows.gaming.input.forcefeedback.idl \ + windows.gaming.ui.idl \ windows.gaming.input.idl \ windows.globalization.idl \ windows.h \ diff --git a/include/windows.foundation.numerics.idl b/include/windows.foundation.numerics.idl new file mode 100644 index 00000000000..eca99ca29bc --- /dev/null +++ wine/include/windows.foundation.numerics.idl @@ -0,0 +1,39 @@ +/* + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +import "inspectable.idl"; +import "asyncinfo.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; + +namespace Windows.Foundation.Numerics { + typedef struct Vector3 Vector3; + + [contract(Windows.Foundation.UniversalApiContract, 1.0)] + struct Vector3 + { + FLOAT X; + FLOAT Y; + FLOAT Z; + }; +} diff --git a/include/windows.gaming.input.forcefeedback.idl b/include/windows.gaming.input.forcefeedback.idl index 432b60a5592..82fb083b34b 100644 --- wine/include/windows.gaming.input.forcefeedback.idl +++ wine/include/windows.gaming.input.forcefeedback.idl @@ -20,23 +20,41 @@ #pragma winrt ns_prefix #endif +#ifndef DO_NO_IMPORTS import "inspectable.idl"; import "asyncinfo.idl"; import "eventtoken.idl"; import "windowscontracts.idl"; import "windows.foundation.idl"; +import "windows.foundation.numerics.idl"; +#endif namespace Windows.Gaming.Input.ForceFeedback { typedef enum ForceFeedbackEffectAxes ForceFeedbackEffectAxes; typedef enum ForceFeedbackEffectState ForceFeedbackEffectState; typedef enum ForceFeedbackLoadEffectResult ForceFeedbackLoadEffectResult; + typedef enum PeriodicForceEffectKind PeriodicForceEffectKind; + typedef enum ConditionForceEffectKind ConditionForceEffectKind; interface IForceFeedbackEffect; + interface IPeriodicForceEffect; + interface IPeriodicForceEffectFactory; + interface IConditionForceEffect; + interface IConditionForceEffectFactory; + interface IConstantForceEffect; + interface IRampForceEffect; runtimeclass ForceFeedbackMotor; + runtimeclass PeriodicForceEffect; + runtimeclass ConditionForceEffect; + runtimeclass ConstantForceEffect; + runtimeclass RampForceEffect; declare { interface Windows.Foundation.AsyncOperationCompletedHandler; interface Windows.Foundation.IAsyncOperation; + interface Windows.Foundation.Collections.IIterator; + interface Windows.Foundation.Collections.IIterable; interface Windows.Foundation.Collections.IVectorView; + interface Windows.Foundation.Collections.IVector; } [ @@ -68,6 +86,25 @@ namespace Windows.Gaming.Input.ForceFeedback { EffectNotSupported = 2 }; + [contract(Windows.Foundation.UniversalApiContract, 3.0)] + enum PeriodicForceEffectKind + { + SquareWave = 0, + SineWave = 1, + TriangleWave = 2, + SawtoothWaveUp = 3, + SawtoothWaveDown = 4, + }; + + [contract(Windows.Foundation.UniversalApiContract, 3.0)] + enum ConditionForceEffectKind + { + Spring = 0, + Damper = 1, + Inertia = 2, + Friction = 3, + }; + [ contract(Windows.Foundation.UniversalApiContract, 3.0), uuid(a17fba0c-2ae4-48c2-8063-eabd0777cb89) @@ -105,6 +142,91 @@ namespace Windows.Gaming.Input.ForceFeedback { [out, retval] Windows.Foundation.IAsyncOperation **async_op); } + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.ForceFeedback.PeriodicForceEffect), + uuid(5c5138d7-fc75-4d52-9a0a-efe4cab5fe64) + ] + interface IPeriodicForceEffect : IInspectable + requires Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect + { + [propget] HRESULT Kind([out, retval] Windows.Gaming.Input.ForceFeedback.PeriodicForceEffectKind *value); + HRESULT SetParameters([in] Windows.Foundation.Numerics.Vector3 vector, [in] FLOAT frequency, [in] FLOAT phase, + [in] FLOAT bias, [in] Windows.Foundation.TimeSpan duration); + HRESULT SetParametersWithEnvelope([in] Windows.Foundation.Numerics.Vector3 vector, [in] FLOAT frequency, [in] FLOAT phase, + [in] FLOAT bias, [in] FLOAT attack_gain, [in] FLOAT sustain_gain, [in] FLOAT release_gain, + [in] Windows.Foundation.TimeSpan start_delay, [in] Windows.Foundation.TimeSpan attack_duration, + [in] Windows.Foundation.TimeSpan sustain_duration, [in] Windows.Foundation.TimeSpan release_duration, + [in] UINT32 repeat_count); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.ForceFeedback.PeriodicForceEffect), + uuid(6f62eb1a-9851-477b-b318-35ecaa15070f) + ] + interface IPeriodicForceEffectFactory : IInspectable + { + HRESULT CreateInstance([in] Windows.Gaming.Input.ForceFeedback.PeriodicForceEffectKind kind, + [out, retval] Windows.Gaming.Input.ForceFeedback.PeriodicForceEffect **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.ForceFeedback.ConditionForceEffect), + uuid(32d1ea68-3695-4e69-85c0-cd1944189140) + ] + interface IConditionForceEffect : IInspectable + requires Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect + { + [propget] HRESULT Kind([out, retval] Windows.Gaming.Input.ForceFeedback.ConditionForceEffectKind *value); + HRESULT SetParameters([in] Windows.Foundation.Numerics.Vector3 direction, [in] FLOAT positive_coeff, + [in] FLOAT negative_coeff, [in] FLOAT max_positive_magnitude, [in] FLOAT max_negative_magnitude, + [in] FLOAT deadzone, [in] FLOAT bias); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.ForceFeedback.ConditionForceEffect), + uuid(91a99264-1810-4eb6-a773-bfd3b8cddbab) + ] + interface IConditionForceEffectFactory : IInspectable + { + HRESULT CreateInstance([in] Windows.Gaming.Input.ForceFeedback.ConditionForceEffectKind kind, + [out, retval] Windows.Gaming.Input.ForceFeedback.ConditionForceEffect **value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.ForceFeedback.ConstantForceEffect), + uuid(9bfa0140-f3c7-415c-b068-0f068734bce0) + ] + interface IConstantForceEffect : IInspectable + requires Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect + { + HRESULT SetParameters([in] Windows.Foundation.Numerics.Vector3 vector, [in] Windows.Foundation.TimeSpan duration); + HRESULT SetParametersWithEnvelope([in] Windows.Foundation.Numerics.Vector3 vector, [in] FLOAT attack_gain, + [in] FLOAT sustain_gain, [in] FLOAT release_gain, [in] Windows.Foundation.TimeSpan start_delay, + [in] Windows.Foundation.TimeSpan attack_duration, [in] Windows.Foundation.TimeSpan sustain_duration, + [in] Windows.Foundation.TimeSpan release_duration, [in] UINT32 repeat_count); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.ForceFeedback.RampForceEffect), + uuid(f1f81259-1ca6-4080-b56d-b43f3354d052) + ] + interface IRampForceEffect : IInspectable + requires Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect + { + HRESULT SetParameters([in] Windows.Foundation.Numerics.Vector3 start_vector, [in] Windows.Foundation.Numerics.Vector3 end_vector, + [in] Windows.Foundation.TimeSpan duration); + HRESULT SetParametersWithEnvelope([in] Windows.Foundation.Numerics.Vector3 start_vector, [in] Windows.Foundation.Numerics.Vector3 end_vector, + [in] FLOAT attack_gain, [in] FLOAT sustain_gain, [in] FLOAT release_gain, [in] Windows.Foundation.TimeSpan start_delay, + [in] Windows.Foundation.TimeSpan attack_duration, [in] Windows.Foundation.TimeSpan sustain_duration, + [in] Windows.Foundation.TimeSpan release_duration, [in] UINT32 repeat_count); + } + [ contract(Windows.Foundation.UniversalApiContract, 3.0), marshaling_behavior(agile), @@ -114,4 +236,52 @@ namespace Windows.Gaming.Input.ForceFeedback { { [default] interface Windows.Gaming.Input.ForceFeedback.IForceFeedbackMotor; } + + [ + activatable(Windows.Gaming.Input.ForceFeedback.IPeriodicForceEffectFactory, Windows.Foundation.UniversalApiContract, 3.0), + contract(Windows.Foundation.UniversalApiContract, 3.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass PeriodicForceEffect + { + [default] interface Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect; + interface Windows.Gaming.Input.ForceFeedback.IPeriodicForceEffect; + } + + [ + activatable(Windows.Gaming.Input.ForceFeedback.IConditionForceEffectFactory, Windows.Foundation.UniversalApiContract, 3.0), + contract(Windows.Foundation.UniversalApiContract, 3.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass ConditionForceEffect + { + [default] interface Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect; + interface Windows.Gaming.Input.ForceFeedback.IConditionForceEffect; + } + + [ + activatable(Windows.Foundation.UniversalApiContract, 3.0), + contract(Windows.Foundation.UniversalApiContract, 3.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass ConstantForceEffect + { + [default] interface Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect; + interface Windows.Gaming.Input.ForceFeedback.IConstantForceEffect; + } + + [ + activatable(Windows.Foundation.UniversalApiContract, 3.0), + contract(Windows.Foundation.UniversalApiContract, 3.0), + marshaling_behavior(agile), + threading(both) + ] + runtimeclass RampForceEffect + { + [default] interface Windows.Gaming.Input.ForceFeedback.IForceFeedbackEffect; + interface Windows.Gaming.Input.ForceFeedback.IRampForceEffect; + } } diff --git a/include/windows.gaming.input.idl b/include/windows.gaming.input.idl index fdae3aa70b1..5fc5265247d 100644 --- wine/include/windows.gaming.input.idl +++ wine/include/windows.gaming.input.idl @@ -446,6 +446,19 @@ namespace Windows.Gaming.Input { HRESULT GetCurrentReading([out, retval] Windows.Gaming.Input.GamepadReading *value); } + [ + contract(Windows.Foundation.UniversalApiContract, 3.0), + exclusiveto(Windows.Gaming.Input.Gamepad), + uuid(3c1689bd-5915-4245-b0c0-c89fae0308ff) + ] + interface IGamepad2 : IInspectable + requires Windows.Gaming.Input.IGamepad, + Windows.Gaming.Input.IGameController + { + HRESULT GetButtonLabel([in] Windows.Gaming.Input.GamepadButtons button, + [out, retval] Windows.Gaming.Input.GameControllerButtonLabel *value); + } + [ contract(Windows.Foundation.UniversalApiContract, 3.0), exclusiveto(Windows.Gaming.Input.RacingWheel), diff --git a/include/windows.gaming.ui.idl b/include/windows.gaming.ui.idl new file mode 100644 index 00000000000..730f5dd90f7 --- /dev/null +++ wine/include/windows.gaming.ui.idl @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +#ifndef DO_NO_IMPORTS +import "inspectable.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; +#endif + +namespace Windows.Gaming.UI { + runtimeclass GameBar; + + declare { + interface Windows.Foundation.EventHandler; + } + + [ + contract(Windows.Foundation.UniversalApiContract, 2.0), + exclusiveto(Windows.Gaming.UI.GameBar), + uuid(1db9a292-cc78-4173-be45-b61e67283ea7) + ] + interface IGameBarStatics : IInspectable + { + [eventadd] HRESULT VisibilityChanged([in] Windows.Foundation.EventHandler *handler, [out, retval] EventRegistrationToken *token); + [eventremove] HRESULT VisibilityChanged([in] EventRegistrationToken token); + [eventadd] HRESULT IsInputRedirectedChanged([in] Windows.Foundation.EventHandler *handler, [out, retval] EventRegistrationToken *token); + [eventremove] HRESULT IsInputRedirectedChanged([in] EventRegistrationToken token); + [propget] HRESULT Visible([out] [retval] boolean* value); + [propget] HRESULT IsInputRedirected([out] [retval] boolean* value); + } + + [ + contract(Windows.Foundation.UniversalApiContract, 2.0), + marshaling_behavior(agile), + static(Windows.Gaming.UI.IGameBarStatics, Windows.Foundation.UniversalApiContract, 2.0), + threading(both) + ] + runtimeclass GameBar + { + } +} -- 2.39.2 (Apple Git-144) diff --git a/dlls/advapi32/advapi.c b/dlls/advapi32/advapi.c index 6497ea22f4e..f7d6e973252 100644 --- wine/dlls/advapi32/advapi.c +++ wine/dlls/advapi32/advapi.c @@ -32,6 +32,7 @@ #include "winerror.h" #include "wincred.h" #include "wct.h" +#include "perflib.h" #include "wine/debug.h" @@ -334,3 +335,50 @@ BOOL WINAPI GetThreadWaitChain(HWCT handle, DWORD_PTR ctx, DWORD flags, DWORD th SetLastError(ERROR_NOT_SUPPORTED); return FALSE; } + +ULONG WINAPI PerfCloseQueryHandle( HANDLE query ) +{ + FIXME( "query %p stub.\n", query ); + + return ERROR_SUCCESS; +} + +ULONG WINAPI PerfOpenQueryHandle( const WCHAR *machine, HANDLE *query ) +{ + FIXME( "machine %s, query %p.\n", debugstr_w(machine), query ); + + if (!query) return ERROR_INVALID_PARAMETER; + *query = (HANDLE)0xdeadbeef; + + return ERROR_SUCCESS; +} + +ULONG WINAPI PerfAddCounters( HANDLE query, PERF_COUNTER_IDENTIFIER *id, DWORD size ) +{ + FIXME( "query %p, id %p, size %lu stub.\n", query, id, size ); + + if (!id || size < sizeof(*id) || id->Size < sizeof(*id)) return ERROR_INVALID_PARAMETER; + + id->Status = ERROR_WMI_GUID_NOT_FOUND; + return ERROR_SUCCESS; +} + +ULONG WINAPI PerfQueryCounterData( HANDLE query, PERF_DATA_HEADER *data, DWORD data_size, DWORD *size_needed ) +{ + FIXME( "query %p, data %p, data_size %lu, size_needed %p stub.\n", query, data, data_size, size_needed ); + + if (!size_needed) return ERROR_INVALID_PARAMETER; + + *size_needed = sizeof(PERF_DATA_HEADER); + + if (!data || data_size < sizeof(PERF_DATA_HEADER)) return ERROR_NOT_ENOUGH_MEMORY; + + data->dwTotalSize = sizeof(PERF_DATA_HEADER); + data->dwNumCounters = 0; + QueryPerformanceCounter( (LARGE_INTEGER *)&data->PerfTimeStamp ); + QueryPerformanceFrequency( (LARGE_INTEGER *)&data->PerfFreq ); + GetSystemTimeAsFileTime( (FILETIME *)&data->PerfTime100NSec ); + FileTimeToSystemTime( (FILETIME *)&data->PerfTime100NSec, &data->SystemTime ); + + return ERROR_SUCCESS; +} diff --git a/dlls/advapi32/advapi32.spec b/dlls/advapi32/advapi32.spec index 3b5f587d40e..1c3f59bb7ee 100644 --- wine/dlls/advapi32/advapi32.spec +++ wine/dlls/advapi32/advapi32.spec @@ -553,8 +553,8 @@ @ stdcall -ret64 -import OpenTraceW(ptr) # @ stub OperationEnd # @ stub OperationStart -# @ stub PerfAddCounters -# @ stub PerfCloseQueryHandle +@ stdcall PerfAddCounters(long ptr long) +@ stdcall PerfCloseQueryHandle(long) @ stdcall -import PerfCreateInstance(long ptr wstr long) # @ stub PerfDecrementULongCounterValue # @ stub PerfDecrementULongLongCounterValue @@ -564,8 +564,8 @@ # @ stub PerfEnumerateCounterSetInstances # @ stub PerfIncrementULongCounterValue # @ stub PerfIncrementULongLongCounterValue -# @ stub PerfOpenQueryHandle -# @ stub PerfQueryCounterData +@ stdcall PerfOpenQueryHandle(wstr ptr) +@ stdcall PerfQueryCounterData(long ptr long ptr) # @ stub PerfQueryCounterInfo # @ stub PerfQueryCounterSetRegistrationInfo # @ stub PerfQueryInstance diff --git a/dlls/advapi32/tests/perf.c b/dlls/advapi32/tests/perf.c index fc07a09d327..34b6e952842 100644 --- wine/dlls/advapi32/tests/perf.c +++ wine/dlls/advapi32/tests/perf.c @@ -25,9 +25,31 @@ #include "winerror.h" #include "perflib.h" #include "winperf.h" +#include "winternl.h" #include "wine/test.h" +#include "initguid.h" + +#define DEFINE_FUNCTION(name) static typeof(name) *p##name; +DEFINE_FUNCTION(PerfCloseQueryHandle); +DEFINE_FUNCTION(PerfOpenQueryHandle); +DEFINE_FUNCTION(PerfAddCounters); +DEFINE_FUNCTION(PerfQueryCounterData); +#undef DEFINE_FUNCTION + +static void init_functions(void) +{ + HANDLE hadvapi = GetModuleHandleA("advapi32.dll"); + +#define GET_FUNCTION(name) p##name = (void *)GetProcAddress(hadvapi, #name) + GET_FUNCTION(PerfCloseQueryHandle); + GET_FUNCTION(PerfOpenQueryHandle); + GET_FUNCTION(PerfAddCounters); + GET_FUNCTION(PerfQueryCounterData); +#undef GET_FUNCTION +} + static ULONG WINAPI test_provider_callback(ULONG code, void *buffer, ULONG size) { ok(0, "Provider callback called.\n"); @@ -188,7 +210,94 @@ void test_provider_init(void) ok(!ret, "Got unexpected ret %lu.\n", ret); } +DEFINE_GUID(TestCounterGUID, 0x12345678, 0x1234, 0x5678, 0x12, 0x34, 0x11, 0x11, 0x22, 0x22, 0x33, 0x33); + +static ULONG64 trunc_nttime_ms(ULONG64 t) +{ + return (t / 10000) * 10000; +} + +static void test_perf_counters(void) +{ + LARGE_INTEGER freq, qpc1, qpc2, nttime1, nttime2, systime; + char buffer[sizeof(PERF_COUNTER_IDENTIFIER) + 8]; + PERF_COUNTER_IDENTIFIER *counter_id; + PERF_DATA_HEADER dh; + HANDLE query; + DWORD size; + ULONG ret; + + if (!pPerfOpenQueryHandle) + { + win_skip("PerfOpenQueryHandle not found.\n"); + return; + } + + ret = pPerfOpenQueryHandle(NULL, NULL); + ok(ret == ERROR_INVALID_PARAMETER, "got ret %lu.\n", ret); + ret = pPerfOpenQueryHandle(NULL, &query); + ok(!ret, "got ret %lu.\n", ret); + + counter_id = (PERF_COUNTER_IDENTIFIER *)buffer; + memset(buffer, 0, sizeof(buffer)); + + counter_id->CounterSetGuid = TestCounterGUID; + counter_id->CounterId = PERF_WILDCARD_COUNTER; + counter_id->InstanceId = PERF_WILDCARD_COUNTER; + + ret = pPerfAddCounters(query, counter_id, sizeof(*counter_id)); + ok(ret == ERROR_INVALID_PARAMETER, "got ret %lu.\n", ret); + + counter_id->Size = sizeof(*counter_id); + ret = pPerfAddCounters(query, counter_id, 8); + ok(ret == ERROR_INVALID_PARAMETER, "got ret %lu.\n", ret); + ret = pPerfAddCounters(query, counter_id, sizeof(*counter_id)); + ok(!ret, "got ret %lu.\n", ret); + ok(counter_id->Status == ERROR_WMI_GUID_NOT_FOUND, "got Status %#lx.\n", counter_id->Status); + + ret = pPerfQueryCounterData(query, NULL, 0, NULL); + ok(ret == ERROR_INVALID_PARAMETER, "got ret %lu.\n", ret); + + size = 0xdeadbeef; + ret = pPerfQueryCounterData(query, NULL, 0, &size); + ok(ret == ERROR_NOT_ENOUGH_MEMORY, "got ret %lu.\n", ret); + ok(size == sizeof(dh), "got size %lu.\n", size); + + ret = pPerfQueryCounterData(query, &dh, sizeof(dh), NULL); + ok(ret == ERROR_INVALID_PARAMETER, "got ret %lu.\n", ret); + + QueryPerformanceFrequency(&freq); + QueryPerformanceCounter(&qpc1); + NtQuerySystemTime(&nttime1); + + size = 0xdeadbeef; + ret = pPerfQueryCounterData(query, &dh, sizeof(dh), &size); + QueryPerformanceCounter(&qpc2); + NtQuerySystemTime(&nttime2); + SystemTimeToFileTime(&dh.SystemTime, (FILETIME *)&systime); + ok(!ret, "got ret %lu.\n", ret); + ok(size == sizeof(dh), "got size %lu.\n", size); + ok(dh.dwTotalSize == sizeof(dh), "got dwTotalSize %lu.\n", dh.dwTotalSize); + ok(!dh.dwNumCounters, "got dwNumCounters %lu.\n", dh.dwNumCounters); + ok(dh.PerfFreq == freq.QuadPart, "got PerfFreq %I64u.\n", dh.PerfFreq); + ok(dh.PerfTimeStamp >= qpc1.QuadPart && dh.PerfTimeStamp <= qpc2.QuadPart, + "got PerfTimeStamp %I64u, qpc1 %I64u, qpc2 %I64u.\n", + dh.PerfTimeStamp, qpc1.QuadPart, qpc2.QuadPart); + ok(dh.PerfTime100NSec >= nttime1.QuadPart && dh.PerfTime100NSec <= nttime2.QuadPart, + "got PerfTime100NSec %I64u, nttime1 %I64u, nttime2 %I64u.\n", + dh.PerfTime100NSec, nttime1.QuadPart, nttime2.QuadPart); + ok(systime.QuadPart >= trunc_nttime_ms(nttime1.QuadPart) && systime.QuadPart <= trunc_nttime_ms(nttime2.QuadPart), + "got systime %I64u, nttime1 %I64u, nttime2 %I64u, %d.\n", + systime.QuadPart, nttime1.QuadPart, nttime2.QuadPart, dh.SystemTime.wMilliseconds); + + ret = pPerfCloseQueryHandle(query); + ok(!ret, "got ret %lu.\n", ret); +} + START_TEST(perf) { + init_functions(); + test_provider_init(); + test_perf_counters(); } diff --git a/include/perflib.h b/include/perflib.h index eb65f0802a4..40704aeb6f7 100644 --- wine/include/perflib.h +++ wine/include/perflib.h @@ -83,6 +83,28 @@ typedef struct _PROVIDER_CONTEXT { LPVOID pMemContext; } PERF_PROVIDER_CONTEXT, * PPERF_PROVIDER_CONTEXT; +typedef struct _PERF_COUNTER_IDENTIFIER { + GUID CounterSetGuid; + ULONG Status; + ULONG Size; + ULONG CounterId; + ULONG InstanceId; + ULONG Index; + ULONG Reserved; +} PERF_COUNTER_IDENTIFIER, *PPERF_COUNTER_IDENTIFIER; + +#define PERF_WILDCARD_COUNTER 0xFFFFFFFF +#define PERF_WILDCARD_INSTANCE L"*" + +typedef struct _PERF_DATA_HEADER { + ULONG dwTotalSize; + ULONG dwNumCounters; + LONGLONG PerfTimeStamp; + LONGLONG PerfTime100NSec; + LONGLONG PerfFreq; + SYSTEMTIME SystemTime; +} PERF_DATA_HEADER, *PPERF_DATA_HEADER; + PERF_COUNTERSET_INSTANCE WINAPI *PerfCreateInstance(HANDLE, const GUID *, const WCHAR *, ULONG); ULONG WINAPI PerfDeleteInstance(HANDLE, PERF_COUNTERSET_INSTANCE *); ULONG WINAPI PerfSetCounterRefValue(HANDLE, PERF_COUNTERSET_INSTANCE *, ULONG, void *); @@ -91,6 +113,11 @@ ULONG WINAPI PerfStartProvider(GUID *, PERFLIBREQUEST, HANDLE *); ULONG WINAPI PerfStartProviderEx(GUID *, PERF_PROVIDER_CONTEXT *, HANDLE *); ULONG WINAPI PerfStopProvider(HANDLE); +ULONG WINAPI PerfAddCounters(HANDLE, PERF_COUNTER_IDENTIFIER *, DWORD); +ULONG WINAPI PerfCloseQueryHandle(HANDLE); +ULONG WINAPI PerfOpenQueryHandle(const WCHAR *, HANDLE *); +ULONG WINAPI PerfQueryCounterData(HANDLE, PERF_DATA_HEADER *, DWORD, DWORD *); + #ifdef __cplusplus } /* extern "C" */ #endif -- 2.39.2 (Apple Git-144) diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec index 2ae9ccdc93f..51a14338d93 100644 --- wine/dlls/kernel32/kernel32.spec +++ wine/dlls/kernel32/kernel32.spec @@ -1470,6 +1470,7 @@ @ stdcall -import SetThreadGroupAffinity(long ptr ptr) @ stdcall -import SetThreadIdealProcessor(long long) @ stdcall -import SetThreadIdealProcessorEx(long ptr ptr) +@ stdcall -import SetThreadInformation(long long ptr long) @ stdcall -import SetThreadLocale(long) @ stdcall -import SetThreadPreferredUILanguages(long ptr ptr) @ stdcall -import SetThreadPriority(long long) diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec index 00012198eb6..3fb2192b1ff 100644 --- wine/dlls/kernelbase/kernelbase.spec +++ wine/dlls/kernelbase/kernelbase.spec @@ -1519,7 +1519,7 @@ @ stdcall SetThreadGroupAffinity(long ptr ptr) @ stdcall SetThreadIdealProcessor(long long) @ stdcall SetThreadIdealProcessorEx(long ptr ptr) -# @ stub SetThreadInformation +@ stdcall SetThreadInformation(long long ptr long) @ stdcall SetThreadLocale(long) @ stdcall SetThreadPreferredUILanguages(long ptr ptr) @ stdcall SetThreadPriority(long long) diff --git a/dlls/kernelbase/thread.c b/dlls/kernelbase/thread.c index 1c878474acb..3f61ae46776 100644 --- wine/dlls/kernelbase/thread.c +++ wine/dlls/kernelbase/thread.c @@ -606,6 +606,25 @@ LANGID WINAPI DECLSPEC_HOTPATCH SetThreadUILanguage( LANGID langid ) } +/********************************************************************** + * SetThreadInformation (kernelbase.@) + */ +BOOL WINAPI DECLSPEC_HOTPATCH SetThreadInformation( HANDLE thread, THREAD_INFORMATION_CLASS info_class, + VOID *info, DWORD size ) +{ + switch (info_class) + { + case ThreadMemoryPriority: + return set_ntstatus( NtSetInformationThread( thread, ThreadPagePriority, info, size )); + case ThreadPowerThrottling: + return set_ntstatus( NtSetInformationThread( thread, ThreadPowerThrottlingState, info, size )); + default: + FIXME("Unsupported class %u.\n", info_class); + return FALSE; + } +} + + /********************************************************************** * SuspendThread (kernelbase.@) */ diff --git a/include/processthreadsapi.h b/include/processthreadsapi.h index 8cdaff4796a..d266b7a727b 100644 --- wine/include/processthreadsapi.h +++ wine/include/processthreadsapi.h @@ -23,8 +23,23 @@ extern "C" { #endif +typedef enum _THREAD_INFORMATION_CLASS +{ + ThreadMemoryPriority, + ThreadAbsoluteCpuPriority, + ThreadDynamicCodePolicy, + ThreadPowerThrottling, + ThreadInformationClassMax +} THREAD_INFORMATION_CLASS; + +typedef struct _MEMORY_PRIORITY_INFORMATION +{ + ULONG MemoryPriority; +} MEMORY_PRIORITY_INFORMATION, *PMEMORY_PRIORITY_INFORMATION; + WINBASEAPI HRESULT WINAPI GetThreadDescription(HANDLE,PWSTR *); WINBASEAPI HRESULT WINAPI SetThreadDescription(HANDLE,PCWSTR); +WINBASEAPI BOOL WINAPI SetThreadInformation(HANDLE,THREAD_INFORMATION_CLASS,LPVOID,DWORD); #ifdef __cplusplus } -- 2.39.2 (Apple Git-144) diff --git a/configure b/configure index cdf99fc287d..bfa6e1885a1 100755 --- wine/configure +++ wine/configure @@ -1424,6 +1424,7 @@ enable_wimgapi enable_win32u enable_windows_devices_enumeration enable_windows_gaming_input +enable_windows_gaming_ui_gamebar enable_windows_globalization enable_windows_media_devices enable_windows_media_speech @@ -22531,6 +22532,8 @@ wine_fn_config_makefile dlls/windebug.dll16 enable_win16 wine_fn_config_makefile dlls/windows.devices.enumeration enable_windows_devices_enumeration wine_fn_config_makefile dlls/windows.gaming.input enable_windows_gaming_input wine_fn_config_makefile dlls/windows.gaming.input/tests enable_tests +wine_fn_config_makefile dlls/windows.gaming.ui.gamebar enable_windows_gaming_ui_gamebar +wine_fn_config_makefile dlls/windows.gaming.ui.gamebar/tests enable_tests wine_fn_config_makefile dlls/windows.globalization enable_windows_globalization wine_fn_config_makefile dlls/windows.globalization/tests enable_tests wine_fn_config_makefile dlls/windows.media.devices enable_windows_media_devices diff --git a/configure.ac b/configure.ac index c05d5b6f539..8fad7462dd9 100644 --- wine/configure.ac +++ wine/configure.ac @@ -3175,6 +3175,8 @@ WINE_CONFIG_MAKEFILE(dlls/windebug.dll16,enable_win16) WINE_CONFIG_MAKEFILE(dlls/windows.devices.enumeration) WINE_CONFIG_MAKEFILE(dlls/windows.gaming.input) WINE_CONFIG_MAKEFILE(dlls/windows.gaming.input/tests) +WINE_CONFIG_MAKEFILE(dlls/windows.gaming.ui.gamebar) +WINE_CONFIG_MAKEFILE(dlls/windows.gaming.ui.gamebar/tests) WINE_CONFIG_MAKEFILE(dlls/windows.globalization) WINE_CONFIG_MAKEFILE(dlls/windows.globalization/tests) WINE_CONFIG_MAKEFILE(dlls/windows.media.devices) diff --git a/dlls/windows.gaming.ui.gamebar/Makefile.in b/dlls/windows.gaming.ui.gamebar/Makefile.in new file mode 100644 index 00000000000..a0eefc4b951 --- /dev/null +++ wine/dlls/windows.gaming.ui.gamebar/Makefile.in @@ -0,0 +1,8 @@ +MODULE = windows.gaming.ui.gamebar.dll +IMPORTS = combase uuid + +C_SRCS = \ + main.c + +IDL_SRCS = \ + classes.idl diff --git a/dlls/windows.gaming.ui.gamebar/classes.idl b/dlls/windows.gaming.ui.gamebar/classes.idl new file mode 100644 index 00000000000..ef10fcb6283 --- /dev/null +++ wine/dlls/windows.gaming.ui.gamebar/classes.idl @@ -0,0 +1,33 @@ +/* + * Runtime Classes for windows.gaming.ui.gamebar.dll + * + * Copyright 2022 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#pragma makedep register + +#ifdef __WIDL__ +#pragma winrt ns_prefix +#endif + +import "inspectable.idl"; +import "eventtoken.idl"; +import "windowscontracts.idl"; +import "windows.foundation.idl"; + +#define DO_NO_IMPORTS +#include "windows.gaming.ui.idl" diff --git a/dlls/windows.gaming.ui.gamebar/main.c b/dlls/windows.gaming.ui.gamebar/main.c new file mode 100644 index 00000000000..ec6b442cfc0 --- /dev/null +++ wine/dlls/windows.gaming.ui.gamebar/main.c @@ -0,0 +1,282 @@ +/* WinRT Windows.Gaming.UI.GameBar implementation + * + * Copyright 2022 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +#include + +#define COBJMACROS +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winuser.h" +#include "winstring.h" + +#include "initguid.h" + +#define WIDL_using_Windows_Foundation +#define WIDL_using_Windows_Gaming_UI +#include "activation.h" +#include "windows.gaming.ui.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(gamebar); + +static EventRegistrationToken dummy_token = {.value = 0xdeadbeef}; + +struct gamebar_statics +{ + IActivationFactory IActivationFactory_iface; + IGameBarStatics IGameBarStatics_iface; + LONG ref; +}; + +static inline struct gamebar_statics *impl_from_IActivationFactory( IActivationFactory *iface ) +{ + return CONTAINING_RECORD( iface, struct gamebar_statics, IActivationFactory_iface ); +} + +static HRESULT WINAPI factory_QueryInterface( IActivationFactory *iface, REFIID iid, void **out ) +{ + struct gamebar_statics *impl = impl_from_IActivationFactory( iface ); + + TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out ); + + if (IsEqualGUID( iid, &IID_IUnknown ) || + IsEqualGUID( iid, &IID_IInspectable ) || + IsEqualGUID( iid, &IID_IAgileObject ) || + IsEqualGUID( iid, &IID_IActivationFactory )) + { + IInspectable_AddRef( (*out = &impl->IActivationFactory_iface) ); + return S_OK; + } + + if (IsEqualGUID( iid, &IID_IGameBarStatics )) + { + IInspectable_AddRef( (*out = &impl->IGameBarStatics_iface) ); + return S_OK; + } + + FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) ); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI factory_AddRef( IActivationFactory *iface ) +{ + struct gamebar_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedIncrement( &impl->ref ); + TRACE( "iface %p increasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static ULONG WINAPI factory_Release( IActivationFactory *iface ) +{ + struct gamebar_statics *impl = impl_from_IActivationFactory( iface ); + ULONG ref = InterlockedDecrement( &impl->ref ); + TRACE( "iface %p decreasing refcount to %lu.\n", iface, ref ); + return ref; +} + +static HRESULT WINAPI factory_GetIids( IActivationFactory *iface, ULONG *iid_count, IID **iids ) +{ + FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetRuntimeClassName( IActivationFactory *iface, HSTRING *class_name ) +{ + FIXME( "iface %p, class_name %p stub!\n", iface, class_name ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_GetTrustLevel( IActivationFactory *iface, TrustLevel *trust_level ) +{ + FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level ); + return E_NOTIMPL; +} + +static HRESULT WINAPI factory_ActivateInstance( IActivationFactory *iface, IInspectable **instance ) +{ + FIXME( "iface %p, instance %p stub!\n", iface, instance ); + return E_NOTIMPL; +} + +static const struct IActivationFactoryVtbl factory_vtbl = +{ + factory_QueryInterface, + factory_AddRef, + factory_Release, + /* IInspectable methods */ + factory_GetIids, + factory_GetRuntimeClassName, + factory_GetTrustLevel, + /* IActivationFactory methods */ + factory_ActivateInstance, +}; + +#define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr ) \ + static inline impl_type *impl_from( iface_type *iface ) \ + { \ + return CONTAINING_RECORD( iface, impl_type, iface_mem ); \ + } \ + static HRESULT WINAPI pfx##_QueryInterface( iface_type *iface, REFIID iid, void **out ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_QueryInterface( (IInspectable *)(expr), iid, out ); \ + } \ + static ULONG WINAPI pfx##_AddRef( iface_type *iface ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_AddRef( (IInspectable *)(expr) ); \ + } \ + static ULONG WINAPI pfx##_Release( iface_type *iface ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_Release( (IInspectable *)(expr) ); \ + } \ + static HRESULT WINAPI pfx##_GetIids( iface_type *iface, ULONG *iid_count, IID **iids ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetIids( (IInspectable *)(expr), iid_count, iids ); \ + } \ + static HRESULT WINAPI pfx##_GetRuntimeClassName( iface_type *iface, HSTRING *class_name ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetRuntimeClassName( (IInspectable *)(expr), class_name ); \ + } \ + static HRESULT WINAPI pfx##_GetTrustLevel( iface_type *iface, TrustLevel *trust_level ) \ + { \ + impl_type *impl = impl_from( iface ); \ + return IInspectable_GetTrustLevel( (IInspectable *)(expr), trust_level ); \ + } +#define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface ) \ + DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface ) + +DEFINE_IINSPECTABLE( statics, IGameBarStatics, struct gamebar_statics, IActivationFactory_iface ) + +static HRESULT WINAPI statics_add_VisibilityChanged( IGameBarStatics *iface, + IEventHandler_IInspectable *handler, + EventRegistrationToken *token ) +{ + FIXME( "iface %p, handler %p, token %p stub.\n", iface, handler, token ); + *token = dummy_token; + return S_OK; +} + +static HRESULT WINAPI statics_remove_VisibilityChanged( IGameBarStatics *iface, EventRegistrationToken token ) +{ + FIXME( "iface %p, token %#I64x stub.\n", iface, token.value ); + return S_OK; +} + +static HRESULT WINAPI statics_add_IsInputRedirectedChanged( IGameBarStatics *iface, + IEventHandler_IInspectable *handler, + EventRegistrationToken *token ) +{ + FIXME( "iface %p, handler %p, token %p stub.\n", iface, handler, token ); + *token = dummy_token; + return S_OK; +} + +static HRESULT WINAPI statics_remove_IsInputRedirectedChanged( IGameBarStatics *iface, EventRegistrationToken token ) +{ + FIXME( "iface %p, token %#I64x stub.\n", iface, token.value ); + return S_OK; +} + +static HRESULT WINAPI statics_get_Visible( IGameBarStatics *iface, BOOLEAN *value) +{ + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_INVALIDARG; + *value = FALSE; + return S_OK; +} + +static HRESULT WINAPI statics_get_IsInputRedirected( IGameBarStatics *iface, BOOLEAN *value) +{ + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_INVALIDARG; + *value = FALSE; + return S_OK; +} + +static const struct IGameBarStaticsVtbl statics_vtbl = +{ + statics_QueryInterface, + statics_AddRef, + statics_Release, + /* IInspectable methods */ + statics_GetIids, + statics_GetRuntimeClassName, + statics_GetTrustLevel, + /* IGameBarStatics methods */ + statics_add_VisibilityChanged, + statics_remove_VisibilityChanged, + statics_add_IsInputRedirectedChanged, + statics_remove_IsInputRedirectedChanged, + statics_get_Visible, + statics_get_IsInputRedirected, +}; + +static struct gamebar_statics gamebar_statics = +{ + {&factory_vtbl}, + {&statics_vtbl}, + 1, +}; + +static IActivationFactory *gamebar_factory = &gamebar_statics.IActivationFactory_iface; + +HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **out) +{ + FIXME("clsid %s, riid %s, out %p stub!\n", debugstr_guid(clsid), debugstr_guid(riid), out); + + return CLASS_E_CLASSNOTAVAILABLE; +} + +HRESULT WINAPI DllGetActivationFactory( HSTRING class_str, IActivationFactory **factory ) +{ + const WCHAR *buffer = WindowsGetStringRawBuffer( class_str, NULL ); + + TRACE( "class %s, factory %p.\n", debugstr_w(buffer), factory ); + + *factory = NULL; + + if (!wcscmp( buffer, RuntimeClass_Windows_Gaming_UI_GameBar )) + IActivationFactory_QueryInterface( gamebar_factory, &IID_IActivationFactory, (void **)factory ); + + if (*factory) return S_OK; + return CLASS_E_CLASSNOTAVAILABLE; +} + +BOOL WINAPI DllMain( HINSTANCE instance, DWORD reason, void *reserved ) +{ + TRACE( "instance %p, reason %lu, reserved %p.\n", instance, reason, reserved ); + + switch (reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( instance ); + break; + } + return TRUE; +} diff --git a/dlls/windows.gaming.ui.gamebar/tests/Makefile.in b/dlls/windows.gaming.ui.gamebar/tests/Makefile.in new file mode 100644 index 00000000000..67d70eee241 --- /dev/null +++ wine/dlls/windows.gaming.ui.gamebar/tests/Makefile.in @@ -0,0 +1,5 @@ +TESTDLL = windows.gaming.ui.gamebar.dll +IMPORTS = combase + +C_SRCS = \ + gamebar.c diff --git a/dlls/windows.gaming.ui.gamebar/tests/gamebar.c b/dlls/windows.gaming.ui.gamebar/tests/gamebar.c new file mode 100644 index 00000000000..14fcb05284f --- /dev/null +++ wine/dlls/windows.gaming.ui.gamebar/tests/gamebar.c @@ -0,0 +1,91 @@ +/* + * Copyright 2022 Paul Gofman for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ +#define COBJMACROS +#include "initguid.h" +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winstring.h" + +#include "roapi.h" + +#define WIDL_using_Windows_Foundation +#define WIDL_using_Windows_Gaming_UI +#include "windows.foundation.h" +#include "windows.gaming.ui.h" + +#include "wine/test.h" + +static void test_GameBarStatics(void) +{ + static const WCHAR *gamebar_statics_name = L"Windows.Gaming.UI.GameBar"; + + IActivationFactory *factory = NULL; + IInspectable *inspectable = NULL, *tmp_inspectable = NULL; + IAgileObject *agile_object = NULL, *tmp_agile_object = NULL; + IGameBarStatics *gamebar_statics = NULL; + HSTRING str; + HRESULT hr; + + hr = WindowsCreateString(gamebar_statics_name, wcslen(gamebar_statics_name), &str); + ok(hr == S_OK, "WindowsCreateString failed, hr %#lx\n", hr); + + hr = RoGetActivationFactory(str, &IID_IActivationFactory, (void **)&factory); + ok(hr == S_OK, "RoGetActivationFactory failed, hr %#lx\n", hr); + WindowsDeleteString(str); + + /* interface tests */ + hr = IActivationFactory_QueryInterface(factory, &IID_IInspectable, (void **)&inspectable); + ok(hr == S_OK, "IActivationFactory_QueryInterface IID_IInspectable failed, hr %#lx\n", hr); + + hr = IActivationFactory_QueryInterface(factory, &IID_IAgileObject, (void **)&agile_object); + ok(hr == S_OK, "IActivationFactory_QueryInterface IID_IAgileObject failed, hr %#lx\n", hr); + + hr = IActivationFactory_QueryInterface(factory, &IID_IGameBarStatics, (void **)&gamebar_statics); + ok(hr == S_OK, "IActivationFactory_QueryInterface IID_IMediaDeviceStatics failed, hr %#lx\n", hr); + + hr = IGameBarStatics_QueryInterface(gamebar_statics, &IID_IInspectable, (void **)&tmp_inspectable); + ok(hr == S_OK, "IMediaDeviceStatics_QueryInterface IID_IInspectable failed, hr %#lx\n", hr); + ok(tmp_inspectable == inspectable, "IMediaDeviceStatics_QueryInterface IID_IInspectable returned %p, expected %p\n", tmp_inspectable, inspectable); + IInspectable_Release(tmp_inspectable); + + hr = IGameBarStatics_QueryInterface(gamebar_statics, &IID_IAgileObject, (void **)&tmp_agile_object); + ok(hr == S_OK, "IMediaDeviceStatics_QueryInterface IID_IAgileObject failed, hr %#lx\n", hr); + ok(tmp_agile_object == agile_object, "IMediaDeviceStatics_QueryInterface IID_IAgileObject returned %p, expected %p\n", tmp_agile_object, agile_object); + IAgileObject_Release(tmp_agile_object); + + + IAgileObject_Release(agile_object); + IInspectable_Release(inspectable); + IActivationFactory_Release(factory); + IGameBarStatics_Release(gamebar_statics); +} + +START_TEST(gamebar) +{ + HRESULT hr; + + hr = RoInitialize(RO_INIT_MULTITHREADED); + ok(hr == S_OK, "RoInitialize failed, hr %#lx\n", hr); + + test_GameBarStatics(); + + RoUninitialize(); +} diff --git a/dlls/windows.gaming.ui.gamebar/windows.gaming.ui.gamebar.spec b/dlls/windows.gaming.ui.gamebar/windows.gaming.ui.gamebar.spec new file mode 100644 index 00000000000..20a8bfa98ea --- /dev/null +++ wine/dlls/windows.gaming.ui.gamebar/windows.gaming.ui.gamebar.spec @@ -0,0 +1,3 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetActivationFactory(ptr ptr) +@ stdcall -private DllGetClassObject(ptr ptr ptr) -- 2.39.2 (Apple Git-144) diff --git a/dlls/mfmediaengine/Makefile.in b/dlls/mfmediaengine/Makefile.in index a0e944c0633..bd273aafdab 100644 --- wine/dlls/mfmediaengine/Makefile.in +++ wine/dlls/mfmediaengine/Makefile.in @@ -1,4 +1,6 @@ +EXTRADEFS = -DWINE_NO_LONG_TYPES MODULE = mfmediaengine.dll +IMPORTLIB = mfmediaengine IMPORTS = oleaut32 ole32 mfplat mf mfuuid dxguid uuid EXTRADLLFLAGS = -Wb,--prefer-native diff --git a/dlls/mfmediaengine/main.c b/dlls/mfmediaengine/main.c index a191448b69f..98504c5e269 100644 --- wine/dlls/mfmediaengine/main.c +++ wine/dlls/mfmediaengine/main.c @@ -29,8 +29,6 @@ #include "mferror.h" #include "dxgi.h" #include "d3d11.h" -#include "mmdeviceapi.h" -#include "audiosessiontypes.h" #include "wine/debug.h" @@ -115,8 +113,7 @@ struct rect struct media_engine { - IMFMediaEngineEx IMFMediaEngineEx_iface; - IMFGetService IMFGetService_iface; + IMFMediaEngine IMFMediaEngine_iface; IMFAsyncCallback session_events; IMFAsyncCallback load_handler; IMFSampleGrabberSinkCallback grabber_callback; @@ -141,11 +138,6 @@ struct media_engine IMFSourceResolver *resolver; BSTR current_source; struct - { - IMFMediaSource *source; - IMFPresentationDescriptor *pd; - } presentation; - struct { LONGLONG pts; SIZE size; @@ -214,7 +206,7 @@ static HRESULT media_engine_lock_d3d_device(struct media_engine *engine, ID3D11D { if (FAILED(hr = IMFDXGIDeviceManager_OpenDeviceHandle(engine->device_manager, &engine->device_handle))) { - WARN("Failed to open device handle, hr %#lx.\n", hr); + WARN("Failed to open device handle, hr %#x.\n", hr); return hr; } } @@ -230,7 +222,7 @@ static HRESULT media_engine_lock_d3d_device(struct media_engine *engine, ID3D11D if (FAILED(hr = IMFDXGIDeviceManager_OpenDeviceHandle(engine->device_manager, &engine->device_handle))) { - WARN("Failed to open a device handle, hr %#lx.\n", hr); + WARN("Failed to open a device handle, hr %#x.\n", hr); return hr; } hr = IMFDXGIDeviceManager_LockDevice(engine->device_manager, engine->device_handle, &IID_ID3D11Device, @@ -340,7 +332,7 @@ static HRESULT media_engine_create_d3d11_video_frame_resources(struct media_engi if (FAILED(hr = ID3D11Device_CreateBuffer(device, &buffer_desc, &resource_data, &engine->video_frame.d3d11.vb))) { - WARN("Failed to create a vertex buffer, hr %#lx.\n", hr); + WARN("Failed to create a vertex buffer, hr %#x.\n", hr); goto failed; } @@ -349,7 +341,7 @@ static HRESULT media_engine_create_d3d11_video_frame_resources(struct media_engi if (FAILED(hr = ID3D11Device_CreateBuffer(device, &buffer_desc, NULL, &engine->video_frame.d3d11.ps_cb))) { - WARN("Failed to create a buffer, hr %#lx.\n", hr); + WARN("Failed to create a buffer, hr %#x.\n", hr); goto failed; } @@ -368,14 +360,14 @@ static HRESULT media_engine_create_d3d11_video_frame_resources(struct media_engi if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &texture_desc, NULL, &engine->video_frame.d3d11.source))) { - WARN("Failed to create source texture, hr %#lx.\n", hr); + WARN("Failed to create source texture, hr %#x.\n", hr); goto failed; } if (FAILED(hr = ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource *)engine->video_frame.d3d11.source, NULL, &engine->video_frame.d3d11.srv))) { - WARN("Failed to create SRV, hr %#lx.\n", hr); + WARN("Failed to create SRV, hr %#x.\n", hr); goto failed; } @@ -388,7 +380,7 @@ static HRESULT media_engine_create_d3d11_video_frame_resources(struct media_engi if (FAILED(hr = ID3D11Device_CreateSamplerState(device, &sampler_desc, &engine->video_frame.d3d11.sampler))) { - WARN("Failed to create a sampler state, hr %#lx.\n", hr); + WARN("Failed to create a sampler state, hr %#x.\n", hr); goto failed; } @@ -396,20 +388,20 @@ static HRESULT media_engine_create_d3d11_video_frame_resources(struct media_engi if (FAILED(hr = ID3D11Device_CreateInputLayout(device, layout_desc, ARRAY_SIZE(layout_desc), vs_code, sizeof(vs_code), &engine->video_frame.d3d11.input_layout))) { - WARN("Failed to create input layout, hr %#lx.\n", hr); + WARN("Failed to create input layout, hr %#x.\n", hr); goto failed; } /* Shaders */ if (FAILED(hr = ID3D11Device_CreateVertexShader(device, vs_code, sizeof(vs_code), NULL, &engine->video_frame.d3d11.vs))) { - WARN("Failed to create the vertex shader, hr %#lx.\n", hr); + WARN("Failed to create the vertex shader, hr %#x.\n", hr); goto failed; } if (FAILED(hr = ID3D11Device_CreatePixelShader(device, ps_code, sizeof(ps_code), NULL, &engine->video_frame.d3d11.ps))) { - WARN("Failed to create the pixel shader, hr %#lx.\n", hr); + WARN("Failed to create the pixel shader, hr %#x.\n", hr); goto failed; } @@ -474,7 +466,7 @@ static ULONG WINAPI media_error_AddRef(IMFMediaError *iface) struct media_error *me = impl_from_IMFMediaError(iface); ULONG refcount = InterlockedIncrement(&me->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -484,7 +476,7 @@ static ULONG WINAPI media_error_Release(IMFMediaError *iface) struct media_error *me = impl_from_IMFMediaError(iface); ULONG refcount = InterlockedDecrement(&me->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) free(me); @@ -524,7 +516,7 @@ static HRESULT WINAPI media_error_SetExtendedErrorCode(IMFMediaError *iface, HRE { struct media_error *me = impl_from_IMFMediaError(iface); - TRACE("%p, %#lx.\n", iface, code); + TRACE("%p, %#x.\n", iface, code); me->extended_code = code; @@ -581,7 +573,7 @@ static ULONG WINAPI time_range_AddRef(IMFMediaTimeRange *iface) struct time_range *range = impl_from_IMFMediaTimeRange(iface); ULONG refcount = InterlockedIncrement(&range->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -591,7 +583,7 @@ static ULONG WINAPI time_range_Release(IMFMediaTimeRange *iface) struct time_range *range = impl_from_IMFMediaTimeRange(iface); ULONG refcount = InterlockedDecrement(&range->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -615,7 +607,7 @@ static HRESULT WINAPI time_range_GetStart(IMFMediaTimeRange *iface, DWORD idx, d { struct time_range *range = impl_from_IMFMediaTimeRange(iface); - TRACE("%p, %lu, %p.\n", iface, idx, start); + TRACE("%p, %u, %p.\n", iface, idx, start); if (idx >= range->count) return E_INVALIDARG; @@ -629,7 +621,7 @@ static HRESULT WINAPI time_range_GetEnd(IMFMediaTimeRange *iface, DWORD idx, dou { struct time_range *range = impl_from_IMFMediaTimeRange(iface); - TRACE("%p, %lu, %p.\n", iface, idx, end); + TRACE("%p, %u, %p.\n", iface, idx, end); if (idx >= range->count) return E_INVALIDARG; @@ -658,35 +650,13 @@ static BOOL WINAPI time_range_ContainsTime(IMFMediaTimeRange *iface, double time static HRESULT WINAPI time_range_AddRange(IMFMediaTimeRange *iface, double start, double end) { struct time_range *range = impl_from_IMFMediaTimeRange(iface); - struct range *c; - size_t i; TRACE("%p, %.8e, %.8e.\n", iface, start, end); - for (i = 0; i < range->count; ++i) + if (range->count) { - c = &range->ranges[i]; - - /* New range is fully contained within existing one. */ - if (c->start <= start && c->end >= end) - return S_OK; - - /* New range fully contains existing one. */ - if (c->start >= start && c->end <= end) - { - c->start = start; - c->end = end; - return S_OK; - } - - /* Merge if ranges intersect. */ - if ((start >= c->start && start <= c->end) || - (end >= c->start && end <= c->end)) - { - c->start = min(c->start, start); - c->end = max(c->end, end); - return S_OK; - } + FIXME("Range merging is not implemented.\n"); + return E_NOTIMPL; } if (!mf_array_reserve((void **)&range->ranges, &range->capacity, range->count + 1, sizeof(*range->ranges))) @@ -747,14 +717,9 @@ static void media_engine_set_flag(struct media_engine *engine, unsigned int mask engine->flags &= ~mask; } -static inline struct media_engine *impl_from_IMFMediaEngineEx(IMFMediaEngineEx *iface) +static inline struct media_engine *impl_from_IMFMediaEngine(IMFMediaEngine *iface) { - return CONTAINING_RECORD(iface, struct media_engine, IMFMediaEngineEx_iface); -} - -static inline struct media_engine *impl_from_IMFGetService(IMFGetService *iface) -{ - return CONTAINING_RECORD(iface, struct media_engine, IMFGetService_iface); + return CONTAINING_RECORD(iface, struct media_engine, IMFMediaEngine_iface); } static struct media_engine *impl_from_session_events_IMFAsyncCallback(IMFAsyncCallback *iface) @@ -819,7 +784,7 @@ static void media_engine_get_frame_size(struct media_engine *engine, IMFTopology IMFMediaTypeHandler_Release(handler); if (FAILED(hr)) { - WARN("Failed to get current media type %#lx.\n", hr); + WARN("Failed to get current media type %#x.\n", hr); return; } @@ -855,13 +820,13 @@ static HRESULT WINAPI media_engine_callback_QueryInterface(IMFAsyncCallback *ifa static ULONG WINAPI media_engine_session_events_AddRef(IMFAsyncCallback *iface) { struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface); - return IMFMediaEngineEx_AddRef(&engine->IMFMediaEngineEx_iface); + return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface); } static ULONG WINAPI media_engine_session_events_Release(IMFAsyncCallback *iface) { struct media_engine *engine = impl_from_session_events_IMFAsyncCallback(iface); - return IMFMediaEngineEx_Release(&engine->IMFMediaEngineEx_iface); + return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface); } static HRESULT WINAPI media_engine_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) @@ -878,13 +843,13 @@ static HRESULT WINAPI media_engine_session_events_Invoke(IMFAsyncCallback *iface if (FAILED(hr = IMFMediaSession_EndGetEvent(engine->session, result, &event))) { - WARN("Failed to get session event, hr %#lx.\n", hr); + WARN("Failed to get session event, hr %#x.\n", hr); goto failed; } if (FAILED(hr = IMFMediaEvent_GetType(event, &event_type))) { - WARN("Failed to get event type, hr %#lx.\n", hr); + WARN("Failed to get event type, hr %#x.\n", hr); goto failed; } @@ -960,7 +925,7 @@ failed: IMFMediaEvent_Release(event); if (FAILED(hr = IMFMediaSession_BeginGetEvent(engine->session, iface, NULL))) - WARN("Failed to subscribe to session events, hr %#lx.\n", hr); + WARN("Failed to subscribe to session events, hr %#x.\n", hr); return S_OK; } @@ -977,13 +942,13 @@ static const IMFAsyncCallbackVtbl media_engine_session_events_vtbl = static ULONG WINAPI media_engine_load_handler_AddRef(IMFAsyncCallback *iface) { struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface); - return IMFMediaEngineEx_AddRef(&engine->IMFMediaEngineEx_iface); + return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface); } static ULONG WINAPI media_engine_load_handler_Release(IMFAsyncCallback *iface) { struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface); - return IMFMediaEngineEx_Release(&engine->IMFMediaEngineEx_iface); + return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface); } static HRESULT media_engine_create_source_node(IMFMediaSource *source, IMFPresentationDescriptor *pd, IMFStreamDescriptor *sd, @@ -1076,35 +1041,22 @@ static HRESULT media_engine_create_video_renderer(struct media_engine *engine, I return hr; } -static void media_engine_clear_presentation(struct media_engine *engine) -{ - if (engine->presentation.source) - { - IMFMediaSource_Shutdown(engine->presentation.source); - IMFMediaSource_Release(engine->presentation.source); - } - if (engine->presentation.pd) - IMFPresentationDescriptor_Release(engine->presentation.pd); - memset(&engine->presentation, 0, sizeof(engine->presentation)); -} - static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMediaSource *source) { IMFStreamDescriptor *sd_audio = NULL, *sd_video = NULL; + unsigned int stream_count = 0, i; IMFPresentationDescriptor *pd; - DWORD stream_count = 0, i; IMFTopology *topology; UINT64 duration; HRESULT hr; media_engine_release_video_frame_resources(engine); - media_engine_clear_presentation(engine); if (FAILED(hr = IMFMediaSource_CreatePresentationDescriptor(source, &pd))) return hr; if (FAILED(hr = IMFPresentationDescriptor_GetStreamDescriptorCount(pd, &stream_count))) - WARN("Failed to get stream count, hr %#lx.\n", hr); + WARN("Failed to get stream count, hr %#x.\n", hr); /* Enable first video stream and first audio stream. */ @@ -1142,8 +1094,6 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi IMFMediaTypeHandler_Release(type_handler); } - - IMFStreamDescriptor_Release(sd); } if (!sd_video && !sd_audio) @@ -1152,11 +1102,6 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi return E_UNEXPECTED; } - engine->presentation.source = source; - IMFMediaSource_AddRef(engine->presentation.source); - engine->presentation.pd = pd; - IMFPresentationDescriptor_AddRef(engine->presentation.pd); - media_engine_set_flag(engine, FLAGS_ENGINE_HAS_VIDEO, !!sd_video); media_engine_set_flag(engine, FLAGS_ENGINE_HAS_AUDIO, !!sd_audio); @@ -1174,16 +1119,13 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi IMFTopologyNode *sar_node = NULL, *audio_src = NULL; IMFTopologyNode *grabber_node = NULL, *video_src = NULL; - if (engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE) - IMFTopology_SetUINT32(topology, &MF_LOW_LATENCY, TRUE); - if (sd_audio) { if (FAILED(hr = media_engine_create_source_node(source, pd, sd_audio, &audio_src))) - WARN("Failed to create audio source node, hr %#lx.\n", hr); + WARN("Failed to create audio source node, hr %#x.\n", hr); if (FAILED(hr = media_engine_create_audio_renderer(engine, &sar_node))) - WARN("Failed to create audio renderer node, hr %#lx.\n", hr); + WARN("Failed to create audio renderer node, hr %#x.\n", hr); if (sar_node && audio_src) { @@ -1201,10 +1143,10 @@ static HRESULT media_engine_create_topology(struct media_engine *engine, IMFMedi if (SUCCEEDED(hr) && sd_video) { if (FAILED(hr = media_engine_create_source_node(source, pd, sd_video, &video_src))) - WARN("Failed to create video source node, hr %#lx.\n", hr); + WARN("Failed to create video source node, hr %#x.\n", hr); if (FAILED(hr = media_engine_create_video_renderer(engine, &grabber_node))) - WARN("Failed to create video grabber node, hr %#lx.\n", hr); + WARN("Failed to create video grabber node, hr %#x.\n", hr); if (grabber_node && video_src) { @@ -1252,10 +1194,10 @@ static void media_engine_start_playback(struct media_engine *engine) static HRESULT WINAPI media_engine_load_handler_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) { struct media_engine *engine = impl_from_load_handler_IMFAsyncCallback(iface); - IUnknown *object = NULL, *state; unsigned int start_playback; MF_OBJECT_TYPE obj_type; IMFMediaSource *source; + IUnknown *object = NULL; HRESULT hr; EnterCriticalSection(&engine->cs); @@ -1266,16 +1208,8 @@ static HRESULT WINAPI media_engine_load_handler_Invoke(IMFAsyncCallback *iface, start_playback = engine->flags & FLAGS_ENGINE_PLAY_PENDING; media_engine_set_flag(engine, FLAGS_ENGINE_SOURCE_PENDING | FLAGS_ENGINE_PLAY_PENDING, FALSE); - if (SUCCEEDED(IMFAsyncResult_GetState(result, &state))) - { - hr = IMFSourceResolver_EndCreateObjectFromByteStream(engine->resolver, result, &obj_type, &object); - IUnknown_Release(state); - } - else - hr = IMFSourceResolver_EndCreateObjectFromURL(engine->resolver, result, &obj_type, &object); - - if (FAILED(hr)) - WARN("Failed to create source object, hr %#lx.\n", hr); + if (FAILED(hr = IMFSourceResolver_EndCreateObjectFromURL(engine->resolver, result, &obj_type, &object))) + WARN("Failed to create source object, hr %#x.\n", hr); if (object) { @@ -1316,39 +1250,29 @@ static const IMFAsyncCallbackVtbl media_engine_load_handler_vtbl = media_engine_load_handler_Invoke, }; -static HRESULT WINAPI media_engine_QueryInterface(IMFMediaEngineEx *iface, REFIID riid, void **obj) +static HRESULT WINAPI media_engine_QueryInterface(IMFMediaEngine *iface, REFIID riid, void **obj) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); - if (IsEqualIID(riid, &IID_IMFMediaEngineEx) || - IsEqualIID(riid, &IID_IMFMediaEngine) || + if (IsEqualIID(riid, &IID_IMFMediaEngine) || IsEqualIID(riid, &IID_IUnknown)) { *obj = iface; - } - else if (IsEqualIID(riid, &IID_IMFGetService)) - { - *obj = &engine->IMFGetService_iface; - } - else - { - WARN("Unsupported interface %s.\n", debugstr_guid(riid)); - *obj = NULL; - return E_NOINTERFACE; + IMFMediaEngine_AddRef(iface); + return S_OK; } - IUnknown_AddRef((IUnknown *)*obj); - return S_OK; + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; } -static ULONG WINAPI media_engine_AddRef(IMFMediaEngineEx *iface) +static ULONG WINAPI media_engine_AddRef(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); ULONG refcount = InterlockedIncrement(&engine->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -1366,7 +1290,6 @@ static void free_media_engine(struct media_engine *engine) if (engine->resolver) IMFSourceResolver_Release(engine->resolver); media_engine_release_video_frame_resources(engine); - media_engine_clear_presentation(engine); if (engine->device_manager) { IMFDXGIDeviceManager_CloseDeviceHandle(engine->device_manager, engine->device_handle); @@ -1378,12 +1301,12 @@ static void free_media_engine(struct media_engine *engine) free(engine); } -static ULONG WINAPI media_engine_Release(IMFMediaEngineEx *iface) +static ULONG WINAPI media_engine_Release(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); ULONG refcount = InterlockedDecrement(&engine->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) free_media_engine(engine); @@ -1391,9 +1314,9 @@ static ULONG WINAPI media_engine_Release(IMFMediaEngineEx *iface) return refcount; } -static HRESULT WINAPI media_engine_GetError(IMFMediaEngineEx *iface, IMFMediaError **error) +static HRESULT WINAPI media_engine_GetError(IMFMediaEngine *iface, IMFMediaError **error) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %p.\n", iface, error); @@ -1416,9 +1339,9 @@ static HRESULT WINAPI media_engine_GetError(IMFMediaEngineEx *iface, IMFMediaErr return hr; } -static HRESULT WINAPI media_engine_SetErrorCode(IMFMediaEngineEx *iface, MF_MEDIA_ENGINE_ERR code) +static HRESULT WINAPI media_engine_SetErrorCode(IMFMediaEngine *iface, MF_MEDIA_ENGINE_ERR code) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %u.\n", iface, code); @@ -1436,76 +1359,66 @@ static HRESULT WINAPI media_engine_SetErrorCode(IMFMediaEngineEx *iface, MF_MEDI return hr; } -static HRESULT WINAPI media_engine_SetSourceElements(IMFMediaEngineEx *iface, IMFMediaEngineSrcElements *elements) +static HRESULT WINAPI media_engine_SetSourceElements(IMFMediaEngine *iface, IMFMediaEngineSrcElements *elements) { FIXME("(%p, %p): stub.\n", iface, elements); return E_NOTIMPL; } -static HRESULT media_engine_set_source(struct media_engine *engine, IMFByteStream *bytestream, BSTR url) +static HRESULT WINAPI media_engine_SetSource(IMFMediaEngine *iface, BSTR url) { - IPropertyStore *props = NULL; - unsigned int flags; + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; - SysFreeString(engine->current_source); - engine->current_source = NULL; - if (url) - engine->current_source = SysAllocString(url); - - engine->ready_state = MF_MEDIA_ENGINE_READY_HAVE_NOTHING; - - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0); + TRACE("%p, %s.\n", iface, debugstr_w(url)); - engine->network_state = MF_MEDIA_ENGINE_NETWORK_NO_SOURCE; + EnterCriticalSection(&engine->cs); - if (url || bytestream) + if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) + hr = MF_E_SHUTDOWN; + else { - flags = MF_RESOLUTION_MEDIASOURCE | MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE; - if (engine->flags & MF_MEDIA_ENGINE_DISABLE_LOCAL_PLUGINS) - flags |= MF_RESOLUTION_DISABLE_LOCAL_PLUGINS; + SysFreeString(engine->current_source); + engine->current_source = NULL; + if (url) + engine->current_source = SysAllocString(url); - IMFAttributes_GetUnknown(engine->attributes, &MF_MEDIA_ENGINE_SOURCE_RESOLVER_CONFIG_STORE, - &IID_IPropertyStore, (void **)&props); - if (bytestream) - hr = IMFSourceResolver_BeginCreateObjectFromByteStream(engine->resolver, bytestream, url, flags, - props, NULL, &engine->load_handler, (IUnknown *)bytestream); - else - hr = IMFSourceResolver_BeginCreateObjectFromURL(engine->resolver, url, flags, props, NULL, - &engine->load_handler, NULL); - if (SUCCEEDED(hr)) - media_engine_set_flag(engine, FLAGS_ENGINE_SOURCE_PENDING, TRUE); + engine->ready_state = MF_MEDIA_ENGINE_READY_HAVE_NOTHING; - if (props) - IPropertyStore_Release(props); - } + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0); - return hr; -} + engine->network_state = MF_MEDIA_ENGINE_NETWORK_NO_SOURCE; -static HRESULT WINAPI media_engine_SetSource(IMFMediaEngineEx *iface, BSTR url) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; + if (url) + { + IPropertyStore *props = NULL; + unsigned int flags; - TRACE("%p, %s.\n", iface, debugstr_w(url)); + flags = MF_RESOLUTION_MEDIASOURCE; + if (engine->flags & MF_MEDIA_ENGINE_DISABLE_LOCAL_PLUGINS) + flags |= MF_RESOLUTION_DISABLE_LOCAL_PLUGINS; - EnterCriticalSection(&engine->cs); + IMFAttributes_GetUnknown(engine->attributes, &MF_MEDIA_ENGINE_SOURCE_RESOLVER_CONFIG_STORE, + &IID_IPropertyStore, (void **)&props); + hr = IMFSourceResolver_BeginCreateObjectFromURL(engine->resolver, url, flags, props, NULL, + &engine->load_handler, NULL); + if (SUCCEEDED(hr)) + media_engine_set_flag(engine, FLAGS_ENGINE_SOURCE_PENDING, TRUE); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = media_engine_set_source(engine, NULL, url); + if (props) + IPropertyStore_Release(props); + } + } LeaveCriticalSection(&engine->cs); return hr; } -static HRESULT WINAPI media_engine_GetCurrentSource(IMFMediaEngineEx *iface, BSTR *url) +static HRESULT WINAPI media_engine_GetCurrentSource(IMFMediaEngine *iface, BSTR *url) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %p.\n", iface, url); @@ -1513,32 +1426,28 @@ static HRESULT WINAPI media_engine_GetCurrentSource(IMFMediaEngineEx *iface, BST *url = NULL; EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; if (engine->current_source) { if (!(*url = SysAllocString(engine->current_source))) hr = E_OUTOFMEMORY; } - LeaveCriticalSection(&engine->cs); return hr; } -static USHORT WINAPI media_engine_GetNetworkState(IMFMediaEngineEx *iface) +static USHORT WINAPI media_engine_GetNetworkState(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); TRACE("%p.\n", iface); return engine->network_state; } -static MF_MEDIA_ENGINE_PRELOAD WINAPI media_engine_GetPreload(IMFMediaEngineEx *iface) +static MF_MEDIA_ENGINE_PRELOAD WINAPI media_engine_GetPreload(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); MF_MEDIA_ENGINE_PRELOAD preload; TRACE("%p.\n", iface); @@ -1550,9 +1459,9 @@ static MF_MEDIA_ENGINE_PRELOAD WINAPI media_engine_GetPreload(IMFMediaEngineEx * return preload; } -static HRESULT WINAPI media_engine_SetPreload(IMFMediaEngineEx *iface, MF_MEDIA_ENGINE_PRELOAD preload) +static HRESULT WINAPI media_engine_SetPreload(IMFMediaEngine *iface, MF_MEDIA_ENGINE_PRELOAD preload) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); TRACE("%p, %d.\n", iface, preload); @@ -1563,9 +1472,9 @@ static HRESULT WINAPI media_engine_SetPreload(IMFMediaEngineEx *iface, MF_MEDIA_ return S_OK; } -static HRESULT WINAPI media_engine_GetBuffered(IMFMediaEngineEx *iface, IMFMediaTimeRange **range) +static HRESULT WINAPI media_engine_GetBuffered(IMFMediaEngine *iface, IMFMediaTimeRange **range) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr; TRACE("%p, %p.\n", iface, range); @@ -1574,54 +1483,30 @@ static HRESULT WINAPI media_engine_GetBuffered(IMFMediaEngineEx *iface, IMFMedia return hr; EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else if (!isnan(engine->duration)) + if (!isnan(engine->duration)) hr = IMFMediaTimeRange_AddRange(*range, 0.0, engine->duration); - LeaveCriticalSection(&engine->cs); return hr; } -static HRESULT WINAPI media_engine_Load(IMFMediaEngineEx *iface) +static HRESULT WINAPI media_engine_Load(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; - FIXME("(%p): stub.\n", iface); - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - - LeaveCriticalSection(&engine->cs); - - return hr; + return E_NOTIMPL; } -static HRESULT WINAPI media_engine_CanPlayType(IMFMediaEngineEx *iface, BSTR type, MF_MEDIA_ENGINE_CANPLAY *answer) +static HRESULT WINAPI media_engine_CanPlayType(IMFMediaEngine *iface, BSTR type, MF_MEDIA_ENGINE_CANPLAY *answer) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; - FIXME("(%p, %s, %p): stub.\n", iface, debugstr_w(type), answer); - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - - LeaveCriticalSection(&engine->cs); - - return hr; + return E_NOTIMPL; } -static USHORT WINAPI media_engine_GetReadyState(IMFMediaEngineEx *iface) +static USHORT WINAPI media_engine_GetReadyState(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); unsigned short state; TRACE("%p.\n", iface); @@ -1633,16 +1518,16 @@ static USHORT WINAPI media_engine_GetReadyState(IMFMediaEngineEx *iface) return state; } -static BOOL WINAPI media_engine_IsSeeking(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_IsSeeking(IMFMediaEngine *iface) { FIXME("(%p): stub.\n", iface); return FALSE; } -static double WINAPI media_engine_GetCurrentTime(IMFMediaEngineEx *iface) +static double WINAPI media_engine_GetCurrentTime(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); double ret = 0.0; MFTIME clocktime; @@ -1662,33 +1547,23 @@ static double WINAPI media_engine_GetCurrentTime(IMFMediaEngineEx *iface) return ret; } -static HRESULT WINAPI media_engine_SetCurrentTime(IMFMediaEngineEx *iface, double time) +static HRESULT WINAPI media_engine_SetCurrentTime(IMFMediaEngine *iface, double time) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; - FIXME("(%p, %f): stub.\n", iface, time); - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - - LeaveCriticalSection(&engine->cs); - - return hr; + return E_NOTIMPL; } -static double WINAPI media_engine_GetStartTime(IMFMediaEngineEx *iface) +static double WINAPI media_engine_GetStartTime(IMFMediaEngine *iface) { FIXME("(%p): stub.\n", iface); return 0.0; } -static double WINAPI media_engine_GetDuration(IMFMediaEngineEx *iface) +static double WINAPI media_engine_GetDuration(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); double value; TRACE("%p.\n", iface); @@ -1700,9 +1575,9 @@ static double WINAPI media_engine_GetDuration(IMFMediaEngineEx *iface) return value; } -static BOOL WINAPI media_engine_IsPaused(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_IsPaused(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL value; TRACE("%p.\n", iface); @@ -1714,9 +1589,9 @@ static BOOL WINAPI media_engine_IsPaused(IMFMediaEngineEx *iface) return value; } -static double WINAPI media_engine_GetDefaultPlaybackRate(IMFMediaEngineEx *iface) +static double WINAPI media_engine_GetDefaultPlaybackRate(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); double rate; TRACE("%p.\n", iface); @@ -1728,9 +1603,9 @@ static double WINAPI media_engine_GetDefaultPlaybackRate(IMFMediaEngineEx *iface return rate; } -static HRESULT WINAPI media_engine_SetDefaultPlaybackRate(IMFMediaEngineEx *iface, double rate) +static HRESULT WINAPI media_engine_SetDefaultPlaybackRate(IMFMediaEngine *iface, double rate) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %f.\n", iface, rate); @@ -1748,9 +1623,9 @@ static HRESULT WINAPI media_engine_SetDefaultPlaybackRate(IMFMediaEngineEx *ifac return hr; } -static double WINAPI media_engine_GetPlaybackRate(IMFMediaEngineEx *iface) +static double WINAPI media_engine_GetPlaybackRate(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); double rate; TRACE("%p.\n", iface); @@ -1762,9 +1637,9 @@ static double WINAPI media_engine_GetPlaybackRate(IMFMediaEngineEx *iface) return rate; } -static HRESULT WINAPI media_engine_SetPlaybackRate(IMFMediaEngineEx *iface, double rate) +static HRESULT WINAPI media_engine_SetPlaybackRate(IMFMediaEngine *iface, double rate) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %f.\n", iface, rate); @@ -1782,43 +1657,23 @@ static HRESULT WINAPI media_engine_SetPlaybackRate(IMFMediaEngineEx *iface, doub return hr; } -static HRESULT WINAPI media_engine_GetPlayed(IMFMediaEngineEx *iface, IMFMediaTimeRange **played) +static HRESULT WINAPI media_engine_GetPlayed(IMFMediaEngine *iface, IMFMediaTimeRange **played) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; - FIXME("(%p, %p): stub.\n", iface, played); - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - - LeaveCriticalSection(&engine->cs); - - return hr; + return E_NOTIMPL; } -static HRESULT WINAPI media_engine_GetSeekable(IMFMediaEngineEx *iface, IMFMediaTimeRange **seekable) +static HRESULT WINAPI media_engine_GetSeekable(IMFMediaEngine *iface, IMFMediaTimeRange **seekable) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_NOTIMPL; - FIXME("(%p, %p): stub.\n", iface, seekable); - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - - LeaveCriticalSection(&engine->cs); - - return hr; + return E_NOTIMPL; } -static BOOL WINAPI media_engine_IsEnded(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_IsEnded(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL value; TRACE("%p.\n", iface); @@ -1830,9 +1685,9 @@ static BOOL WINAPI media_engine_IsEnded(IMFMediaEngineEx *iface) return value; } -static BOOL WINAPI media_engine_GetAutoPlay(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_GetAutoPlay(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL value; TRACE("%p.\n", iface); @@ -1844,9 +1699,9 @@ static BOOL WINAPI media_engine_GetAutoPlay(IMFMediaEngineEx *iface) return value; } -static HRESULT WINAPI media_engine_SetAutoPlay(IMFMediaEngineEx *iface, BOOL autoplay) +static HRESULT WINAPI media_engine_SetAutoPlay(IMFMediaEngine *iface, BOOL autoplay) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); FIXME("(%p, %d): stub.\n", iface, autoplay); @@ -1857,9 +1712,9 @@ static HRESULT WINAPI media_engine_SetAutoPlay(IMFMediaEngineEx *iface, BOOL aut return S_OK; } -static BOOL WINAPI media_engine_GetLoop(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_GetLoop(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL value; TRACE("%p.\n", iface); @@ -1871,9 +1726,9 @@ static BOOL WINAPI media_engine_GetLoop(IMFMediaEngineEx *iface) return value; } -static HRESULT WINAPI media_engine_SetLoop(IMFMediaEngineEx *iface, BOOL loop) +static HRESULT WINAPI media_engine_SetLoop(IMFMediaEngine *iface, BOOL loop) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); FIXME("(%p, %d): stub.\n", iface, loop); @@ -1884,75 +1739,63 @@ static HRESULT WINAPI media_engine_SetLoop(IMFMediaEngineEx *iface, BOOL loop) return S_OK; } -static HRESULT WINAPI media_engine_Play(IMFMediaEngineEx *iface) +static HRESULT WINAPI media_engine_Play(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = S_OK; + struct media_engine *engine = impl_from_IMFMediaEngine(iface); TRACE("%p.\n", iface); EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - { - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0); - - if (!(engine->flags & FLAGS_ENGINE_WAITING)) - { - media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED | FLAGS_ENGINE_IS_ENDED, FALSE); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAY, 0, 0); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0); - if (!(engine->flags & FLAGS_ENGINE_SOURCE_PENDING)) - media_engine_start_playback(engine); - else - media_engine_set_flag(engine, FLAGS_ENGINE_PLAY_PENDING, TRUE); + if (!(engine->flags & FLAGS_ENGINE_WAITING)) + { + media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED | FLAGS_ENGINE_IS_ENDED, FALSE); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PLAY, 0, 0); - media_engine_set_flag(engine, FLAGS_ENGINE_WAITING, TRUE); - } + if (!(engine->flags & FLAGS_ENGINE_SOURCE_PENDING)) + media_engine_start_playback(engine); + else + media_engine_set_flag(engine, FLAGS_ENGINE_PLAY_PENDING, TRUE); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_WAITING, 0, 0); + media_engine_set_flag(engine, FLAGS_ENGINE_WAITING, TRUE); } + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_WAITING, 0, 0); + LeaveCriticalSection(&engine->cs); - return hr; + return S_OK; } -static HRESULT WINAPI media_engine_Pause(IMFMediaEngineEx *iface) +static HRESULT WINAPI media_engine_Pause(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = S_OK; + struct media_engine *engine = impl_from_IMFMediaEngine(iface); TRACE("%p.\n", iface); EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else + if (!(engine->flags & FLAGS_ENGINE_PAUSED)) { - if (!(engine->flags & FLAGS_ENGINE_PAUSED)) - { - media_engine_set_flag(engine, FLAGS_ENGINE_WAITING | FLAGS_ENGINE_IS_ENDED, FALSE); - media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED, TRUE); - - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PAUSE, 0, 0); - } + media_engine_set_flag(engine, FLAGS_ENGINE_WAITING | FLAGS_ENGINE_IS_ENDED, FALSE); + media_engine_set_flag(engine, FLAGS_ENGINE_PAUSED, TRUE); - IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, 0, 0); + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PAUSE, 0, 0); } + IMFMediaEngineNotify_EventNotify(engine->callback, MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, 0, 0); + LeaveCriticalSection(&engine->cs); - return hr; + return S_OK; } -static BOOL WINAPI media_engine_GetMuted(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_GetMuted(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL ret; TRACE("%p.\n", iface); @@ -1964,9 +1807,9 @@ static BOOL WINAPI media_engine_GetMuted(IMFMediaEngineEx *iface) return ret; } -static HRESULT WINAPI media_engine_SetMuted(IMFMediaEngineEx *iface, BOOL muted) +static HRESULT WINAPI media_engine_SetMuted(IMFMediaEngine *iface, BOOL muted) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %d.\n", iface, muted); @@ -1984,9 +1827,9 @@ static HRESULT WINAPI media_engine_SetMuted(IMFMediaEngineEx *iface, BOOL muted) return hr; } -static double WINAPI media_engine_GetVolume(IMFMediaEngineEx *iface) +static double WINAPI media_engine_GetVolume(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); double volume; TRACE("%p.\n", iface); @@ -1998,9 +1841,9 @@ static double WINAPI media_engine_GetVolume(IMFMediaEngineEx *iface) return volume; } -static HRESULT WINAPI media_engine_SetVolume(IMFMediaEngineEx *iface, double volume) +static HRESULT WINAPI media_engine_SetVolume(IMFMediaEngine *iface, double volume) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %f.\n", iface, volume); @@ -2018,9 +1861,9 @@ static HRESULT WINAPI media_engine_SetVolume(IMFMediaEngineEx *iface, double vol return hr; } -static BOOL WINAPI media_engine_HasVideo(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_HasVideo(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL value; TRACE("%p.\n", iface); @@ -2032,9 +1875,9 @@ static BOOL WINAPI media_engine_HasVideo(IMFMediaEngineEx *iface) return value; } -static BOOL WINAPI media_engine_HasAudio(IMFMediaEngineEx *iface) +static BOOL WINAPI media_engine_HasAudio(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); BOOL value; TRACE("%p.\n", iface); @@ -2046,9 +1889,9 @@ static BOOL WINAPI media_engine_HasAudio(IMFMediaEngineEx *iface) return value; } -static HRESULT WINAPI media_engine_GetNativeVideoSize(IMFMediaEngineEx *iface, DWORD *cx, DWORD *cy) +static HRESULT WINAPI media_engine_GetNativeVideoSize(IMFMediaEngine *iface, DWORD *cx, DWORD *cy) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %p, %p.\n", iface, cx, cy); @@ -2073,9 +1916,9 @@ static HRESULT WINAPI media_engine_GetNativeVideoSize(IMFMediaEngineEx *iface, D return hr; } -static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngineEx *iface, DWORD *cx, DWORD *cy) +static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngine *iface, DWORD *cx, DWORD *cy) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; TRACE("%p, %p, %p.\n", iface, cx, cy); @@ -2100,9 +1943,9 @@ static HRESULT WINAPI media_engine_GetVideoAspectRatio(IMFMediaEngineEx *iface, return hr; } -static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) +static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngine *iface) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr = S_OK; FIXME("(%p): stub.\n", iface); @@ -2113,7 +1956,6 @@ static HRESULT WINAPI media_engine_Shutdown(IMFMediaEngineEx *iface) else { media_engine_set_flag(engine, FLAGS_ENGINE_SHUT_DOWN, TRUE); - media_engine_clear_presentation(engine); IMFMediaSession_Shutdown(engine->session); } LeaveCriticalSection(&engine->cs); @@ -2215,7 +2057,7 @@ static HRESULT media_engine_transfer_to_d3d11_texture(struct media_engine *engin if (FAILED(hr = media_engine_create_d3d11_video_frame_resources(engine, device))) { - WARN("Failed to create d3d resources, hr %#lx.\n", hr); + WARN("Failed to create d3d resources, hr %#x.\n", hr); goto done; } @@ -2234,7 +2076,7 @@ static HRESULT media_engine_transfer_to_d3d11_texture(struct media_engine *engin if (FAILED(hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)texture, NULL, &rtv))) { - WARN("Failed to create an rtv, hr %#lx.\n", hr); + WARN("Failed to create an rtv, hr %#x.\n", hr); goto done; } @@ -2343,10 +2185,10 @@ done: return hr; } -static HRESULT WINAPI media_engine_TransferVideoFrame(IMFMediaEngineEx *iface, IUnknown *surface, +static HRESULT WINAPI media_engine_TransferVideoFrame(IMFMediaEngine *iface, IUnknown *surface, const MFVideoNormalizedRect *src_rect, const RECT *dst_rect, const MFARGB *color) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); ID3D11Texture2D *texture; HRESULT hr = E_NOINTERFACE; @@ -2371,9 +2213,9 @@ static HRESULT WINAPI media_engine_TransferVideoFrame(IMFMediaEngineEx *iface, I return hr; } -static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngineEx *iface, LONGLONG *pts) +static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngine *iface, LONGLONG *pts) { - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); + struct media_engine *engine = impl_from_IMFMediaEngine(iface); HRESULT hr; TRACE("%p, %p.\n", iface, pts); @@ -2395,390 +2237,7 @@ static HRESULT WINAPI media_engine_OnVideoStreamTick(IMFMediaEngineEx *iface, LO return hr; } -static HRESULT WINAPI media_engine_SetSourceFromByteStream(IMFMediaEngineEx *iface, IMFByteStream *bytestream, BSTR url) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; - - TRACE("%p, %p, %s.\n", iface, bytestream, debugstr_w(url)); - - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else if (!bytestream || !url) - hr = E_POINTER; - else - hr = media_engine_set_source(engine, bytestream, url); - - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_GetStatistics(IMFMediaEngineEx *iface, MF_MEDIA_ENGINE_STATISTIC stat_id, PROPVARIANT *stat) -{ - FIXME("%p, %x, %p stub.\n", iface, stat_id, stat); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_UpdateVideoStream(IMFMediaEngineEx *iface, const MFVideoNormalizedRect *src, - const RECT *dst, const MFARGB *border_color) -{ - FIXME("%p, %p, %p, %p stub.\n", iface, src, dst, border_color); - - return E_NOTIMPL; -} - -static double WINAPI media_engine_GetBalance(IMFMediaEngineEx *iface) -{ - FIXME("%p stub.\n", iface); - - return 0.0; -} - -static HRESULT WINAPI media_engine_SetBalance(IMFMediaEngineEx *iface, double balance) -{ - FIXME("%p, %f stub.\n", iface, balance); - - return E_NOTIMPL; -} - -static BOOL WINAPI media_engine_IsPlaybackRateSupported(IMFMediaEngineEx *iface, double rate) -{ - FIXME("%p, %f stub.\n", iface, rate); - - return FALSE; -} - -static HRESULT WINAPI media_engine_FrameStep(IMFMediaEngineEx *iface, BOOL forward) -{ - FIXME("%p, %d stub.\n", iface, forward); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_GetResourceCharacteristics(IMFMediaEngineEx *iface, DWORD *flags) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_FAIL; - - TRACE("%p, %p.\n", iface, flags); - - EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else if (engine->presentation.source) - hr = IMFMediaSource_GetCharacteristics(engine->presentation.source, flags); - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_GetPresentationAttribute(IMFMediaEngineEx *iface, REFGUID attribute, - PROPVARIANT *value) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = E_FAIL; - - TRACE("%p, %s, %p.\n", iface, debugstr_guid(attribute), value); - - EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else if (engine->presentation.pd) - hr = IMFPresentationDescriptor_GetItem(engine->presentation.pd, attribute, value); - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_GetNumberOfStreams(IMFMediaEngineEx *iface, DWORD *stream_count) -{ - FIXME("%p, %p stub.\n", iface, stream_count); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_GetStreamAttribute(IMFMediaEngineEx *iface, DWORD stream_index, REFGUID attribute, - PROPVARIANT *value) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - IMFStreamDescriptor *sd; - HRESULT hr = E_FAIL; - BOOL selected; - - TRACE("%p, %ld, %s, %p.\n", iface, stream_index, debugstr_guid(attribute), value); - - EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else if (engine->presentation.pd) - { - if (SUCCEEDED(hr = IMFPresentationDescriptor_GetStreamDescriptorByIndex(engine->presentation.pd, - stream_index, &selected, &sd))) - { - hr = IMFStreamDescriptor_GetItem(sd, attribute, value); - IMFStreamDescriptor_Release(sd); - } - } - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_GetStreamSelection(IMFMediaEngineEx *iface, DWORD stream_index, BOOL *enabled) -{ - FIXME("%p, %ld, %p stub.\n", iface, stream_index, enabled); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_SetStreamSelection(IMFMediaEngineEx *iface, DWORD stream_index, BOOL enabled) -{ - FIXME("%p, %ld, %d stub.\n", iface, stream_index, enabled); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_ApplyStreamSelections(IMFMediaEngineEx *iface) -{ - FIXME("%p stub.\n", iface); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_IsProtected(IMFMediaEngineEx *iface, BOOL *protected) -{ - FIXME("%p, %p stub.\n", iface, protected); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_InsertVideoEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) -{ - FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_InsertAudioEffect(IMFMediaEngineEx *iface, IUnknown *effect, BOOL is_optional) -{ - FIXME("%p, %p, %d stub.\n", iface, effect, is_optional); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_RemoveAllEffects(IMFMediaEngineEx *iface) -{ - FIXME("%p stub.\n", iface); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_SetTimelineMarkerTimer(IMFMediaEngineEx *iface, double timeout) -{ - FIXME("%p, %f stub.\n", iface, timeout); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_GetTimelineMarkerTimer(IMFMediaEngineEx *iface, double *timeout) -{ - FIXME("%p, %p stub.\n", iface, timeout); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_CancelTimelineMarkerTimer(IMFMediaEngineEx *iface) -{ - FIXME("%p stub.\n", iface); - - return E_NOTIMPL; -} - -static BOOL WINAPI media_engine_IsStereo3D(IMFMediaEngineEx *iface) -{ - FIXME("%p stub.\n", iface); - - return FALSE; -} - -static HRESULT WINAPI media_engine_GetStereo3DFramePackingMode(IMFMediaEngineEx *iface, MF_MEDIA_ENGINE_S3D_PACKING_MODE *mode) -{ - FIXME("%p, %p stub.\n", iface, mode); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_SetStereo3DFramePackingMode(IMFMediaEngineEx *iface, MF_MEDIA_ENGINE_S3D_PACKING_MODE mode) -{ - FIXME("%p, %#x stub.\n", iface, mode); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_GetStereo3DRenderMode(IMFMediaEngineEx *iface, MF3DVideoOutputType *output_type) -{ - FIXME("%p, %p stub.\n", iface, output_type); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_SetStereo3DRenderMode(IMFMediaEngineEx *iface, MF3DVideoOutputType output_type) -{ - FIXME("%p, %#x stub.\n", iface, output_type); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_EnableWindowlessSwapchainMode(IMFMediaEngineEx *iface, BOOL enable) -{ - FIXME("%p, %d stub.\n", iface, enable); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_GetVideoSwapchainHandle(IMFMediaEngineEx *iface, HANDLE *swapchain) -{ - FIXME("%p, %p stub.\n", iface, swapchain); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_EnableHorizontalMirrorMode(IMFMediaEngineEx *iface, BOOL enable) -{ - FIXME("%p, %d stub.\n", iface, enable); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_GetAudioStreamCategory(IMFMediaEngineEx *iface, UINT32 *category) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; - - TRACE("%p, %p.\n", iface, category); - - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = IMFAttributes_GetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_CATEGORY, category); - - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_SetAudioStreamCategory(IMFMediaEngineEx *iface, UINT32 category) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; - - TRACE("%p, %u.\n", iface, category); - - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = IMFAttributes_SetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_CATEGORY, category); - - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_GetAudioEndpointRole(IMFMediaEngineEx *iface, UINT32 *role) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; - - TRACE("%p, %p.\n", iface, role); - - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = IMFAttributes_GetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_ENDPOINT_ROLE, role); - - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_SetAudioEndpointRole(IMFMediaEngineEx *iface, UINT32 role) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr; - - TRACE("%p, %u.\n", iface, role); - - EnterCriticalSection(&engine->cs); - - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - hr = IMFAttributes_SetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_ENDPOINT_ROLE, role); - - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_GetRealTimeMode(IMFMediaEngineEx *iface, BOOL *enabled) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = S_OK; - - TRACE("%p, %p.\n", iface, enabled); - - EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - *enabled = !!(engine->flags & MF_MEDIA_ENGINE_REAL_TIME_MODE); - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_SetRealTimeMode(IMFMediaEngineEx *iface, BOOL enable) -{ - struct media_engine *engine = impl_from_IMFMediaEngineEx(iface); - HRESULT hr = S_OK; - - TRACE("%p, %d.\n", iface, enable); - - EnterCriticalSection(&engine->cs); - if (engine->flags & FLAGS_ENGINE_SHUT_DOWN) - hr = MF_E_SHUTDOWN; - else - media_engine_set_flag(engine, MF_MEDIA_ENGINE_REAL_TIME_MODE, enable); - LeaveCriticalSection(&engine->cs); - - return hr; -} - -static HRESULT WINAPI media_engine_SetCurrentTimeEx(IMFMediaEngineEx *iface, double seektime, MF_MEDIA_ENGINE_SEEK_MODE mode) -{ - FIXME("%p, %f, %#x stub.\n", iface, seektime, mode); - - return E_NOTIMPL; -} - -static HRESULT WINAPI media_engine_EnableTimeUpdateTimer(IMFMediaEngineEx *iface, BOOL enable) -{ - FIXME("%p, %d stub.\n", iface, enable); - - return E_NOTIMPL; -} - -static const IMFMediaEngineExVtbl media_engine_vtbl = +static const IMFMediaEngineVtbl media_engine_vtbl = { media_engine_QueryInterface, media_engine_AddRef, @@ -2825,77 +2284,6 @@ static const IMFMediaEngineExVtbl media_engine_vtbl = media_engine_Shutdown, media_engine_TransferVideoFrame, media_engine_OnVideoStreamTick, - media_engine_SetSourceFromByteStream, - media_engine_GetStatistics, - media_engine_UpdateVideoStream, - media_engine_GetBalance, - media_engine_SetBalance, - media_engine_IsPlaybackRateSupported, - media_engine_FrameStep, - media_engine_GetResourceCharacteristics, - media_engine_GetPresentationAttribute, - media_engine_GetNumberOfStreams, - media_engine_GetStreamAttribute, - media_engine_GetStreamSelection, - media_engine_SetStreamSelection, - media_engine_ApplyStreamSelections, - media_engine_IsProtected, - media_engine_InsertVideoEffect, - media_engine_InsertAudioEffect, - media_engine_RemoveAllEffects, - media_engine_SetTimelineMarkerTimer, - media_engine_GetTimelineMarkerTimer, - media_engine_CancelTimelineMarkerTimer, - media_engine_IsStereo3D, - media_engine_GetStereo3DFramePackingMode, - media_engine_SetStereo3DFramePackingMode, - media_engine_GetStereo3DRenderMode, - media_engine_SetStereo3DRenderMode, - media_engine_EnableWindowlessSwapchainMode, - media_engine_GetVideoSwapchainHandle, - media_engine_EnableHorizontalMirrorMode, - media_engine_GetAudioStreamCategory, - media_engine_SetAudioStreamCategory, - media_engine_GetAudioEndpointRole, - media_engine_SetAudioEndpointRole, - media_engine_GetRealTimeMode, - media_engine_SetRealTimeMode, - media_engine_SetCurrentTimeEx, - media_engine_EnableTimeUpdateTimer, -}; - -static HRESULT WINAPI media_engine_gs_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) -{ - struct media_engine *engine = impl_from_IMFGetService(iface); - return IMFMediaEngineEx_QueryInterface(&engine->IMFMediaEngineEx_iface, riid, obj); -} - -static ULONG WINAPI media_engine_gs_AddRef(IMFGetService *iface) -{ - struct media_engine *engine = impl_from_IMFGetService(iface); - return IMFMediaEngineEx_AddRef(&engine->IMFMediaEngineEx_iface); -} - -static ULONG WINAPI media_engine_gs_Release(IMFGetService *iface) -{ - struct media_engine *engine = impl_from_IMFGetService(iface); - return IMFMediaEngineEx_Release(&engine->IMFMediaEngineEx_iface); -} - -static HRESULT WINAPI media_engine_gs_GetService(IMFGetService *iface, REFGUID service, - REFIID riid, void **object) -{ - FIXME("%p, %s, %s, %p stub.\n", iface, debugstr_guid(service), debugstr_guid(riid), object); - - return E_NOTIMPL; -} - -static const IMFGetServiceVtbl media_engine_get_service_vtbl = -{ - media_engine_gs_QueryInterface, - media_engine_gs_AddRef, - media_engine_gs_Release, - media_engine_gs_GetService, }; static HRESULT WINAPI media_engine_grabber_callback_QueryInterface(IMFSampleGrabberSinkCallback *iface, @@ -2916,13 +2304,13 @@ static HRESULT WINAPI media_engine_grabber_callback_QueryInterface(IMFSampleGrab static ULONG WINAPI media_engine_grabber_callback_AddRef(IMFSampleGrabberSinkCallback *iface) { struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface); - return IMFMediaEngineEx_AddRef(&engine->IMFMediaEngineEx_iface); + return IMFMediaEngine_AddRef(&engine->IMFMediaEngine_iface); } static ULONG WINAPI media_engine_grabber_callback_Release(IMFSampleGrabberSinkCallback *iface) { struct media_engine *engine = impl_from_IMFSampleGrabberSinkCallback(iface); - return IMFMediaEngineEx_Release(&engine->IMFMediaEngineEx_iface); + return IMFMediaEngine_Release(&engine->IMFMediaEngine_iface); } static HRESULT WINAPI media_engine_grabber_callback_OnClockStart(IMFSampleGrabberSinkCallback *iface, @@ -3051,8 +2439,7 @@ static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct IMFClock *clock; HRESULT hr; - engine->IMFMediaEngineEx_iface.lpVtbl = &media_engine_vtbl; - engine->IMFGetService_iface.lpVtbl = &media_engine_get_service_vtbl; + engine->IMFMediaEngine_iface.lpVtbl = &media_engine_vtbl; engine->session_events.lpVtbl = &media_engine_session_events_vtbl; engine->load_handler.lpVtbl = &media_engine_load_handler_vtbl; engine->grabber_callback.lpVtbl = &media_engine_grabber_callback_vtbl; @@ -3096,12 +2483,6 @@ static HRESULT init_media_engine(DWORD flags, IMFAttributes *attributes, struct if (FAILED(hr = IMFAttributes_CopyAllItems(attributes, engine->attributes))) return hr; - /* Set default audio configuration */ - if (FAILED(IMFAttributes_GetItem(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_CATEGORY, NULL))) - IMFAttributes_SetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_CATEGORY, AudioCategory_Other); - if (FAILED(IMFAttributes_GetItem(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_ENDPOINT_ROLE, NULL))) - IMFAttributes_SetUINT32(engine->attributes, &MF_MEDIA_ENGINE_AUDIO_ENDPOINT_ROLE, eMultimedia); - IMFAttributes_GetUINT64(attributes, &MF_MEDIA_ENGINE_PLAYBACK_HWND, &playback_hwnd); hr = IMFAttributes_GetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, &output_format); if (playback_hwnd) /* FIXME: handle MF_MEDIA_ENGINE_PLAYBACK_VISUAL */ @@ -3123,7 +2504,7 @@ static HRESULT WINAPI media_engine_factory_CreateInstance(IMFMediaEngineClassFac struct media_engine *object; HRESULT hr; - TRACE("%p, %#lx, %p, %p.\n", iface, flags, attributes, engine); + TRACE("%p, %#x, %p, %p.\n", iface, flags, attributes, engine); if (!attributes || !engine) return E_POINTER; @@ -3139,7 +2520,7 @@ static HRESULT WINAPI media_engine_factory_CreateInstance(IMFMediaEngineClassFac return hr; } - *engine = (IMFMediaEngine *)&object->IMFMediaEngineEx_iface; + *engine = &object->IMFMediaEngine_iface; return S_OK; } diff --git a/dlls/mfmediaengine/tests/Makefile.in b/dlls/mfmediaengine/tests/Makefile.in index 421b75587a0..13bbef64df2 100644 --- wine/dlls/mfmediaengine/tests/Makefile.in +++ wine/dlls/mfmediaengine/tests/Makefile.in @@ -1,5 +1,6 @@ +EXTRADEFS = -DWINE_NO_LONG_TYPES TESTDLL = mfmediaengine.dll -IMPORTS = ole32 mfplat oleaut32 mfuuid uuid +IMPORTS = ole32 mfplat mfmediaengine oleaut32 mfuuid uuid C_SRCS = \ mfmediaengine.c diff --git a/dlls/mfmediaengine/tests/mfmediaengine.c b/dlls/mfmediaengine/tests/mfmediaengine.c index 008ee58849c..096f6e3f2a5 100644 --- wine/dlls/mfmediaengine/tests/mfmediaengine.c +++ wine/dlls/mfmediaengine/tests/mfmediaengine.c @@ -29,9 +29,8 @@ #include "mferror.h" #include "dxgi.h" #include "initguid.h" -#include "mmdeviceapi.h" -#include "audiosessiontypes.h" +#include "wine/heap.h" #include "wine/test.h" static HRESULT (WINAPI *pMFCreateDXGIDeviceManager)(UINT *token, IMFDXGIDeviceManager **manager); @@ -44,22 +43,7 @@ static void _expect_ref(IUnknown *obj, ULONG ref, int line) ULONG rc; IUnknown_AddRef(obj); rc = IUnknown_Release(obj); - ok_(__FILE__,line)(rc == ref, "Unexpected refcount %ld, expected %ld.\n", rc, ref); -} - -#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) -static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) -{ - IUnknown *iface = iface_ptr; - HRESULT hr, expected_hr; - IUnknown *unk; - - expected_hr = supported ? S_OK : E_NOINTERFACE; - - hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); - ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); - if (SUCCEEDED(hr)) - IUnknown_Release(unk); + ok_(__FILE__,line)(rc == ref, "Unexpected refcount %d, expected %d.\n", rc, ref); } static void init_functions(void) @@ -105,12 +89,7 @@ static ULONG WINAPI media_engine_notify_AddRef(IMFMediaEngineNotify *iface) static ULONG WINAPI media_engine_notify_Release(IMFMediaEngineNotify *iface) { struct media_engine_notify *notify = impl_from_IMFMediaEngineNotify(iface); - ULONG refcount = InterlockedDecrement(¬ify->refcount); - - if (!refcount) - free(notify); - - return refcount; + return InterlockedDecrement(¬ify->refcount); } static HRESULT WINAPI media_engine_notify_EventNotify(IMFMediaEngineNotify *iface, DWORD event, DWORD_PTR param1, DWORD param2) @@ -126,18 +105,6 @@ static IMFMediaEngineNotifyVtbl media_engine_notify_vtbl = media_engine_notify_EventNotify, }; -static struct media_engine_notify *create_callback(void) -{ - struct media_engine_notify *object; - - object = calloc(1, sizeof(*object)); - - object->IMFMediaEngineNotify_iface.lpVtbl = &media_engine_notify_vtbl; - object->refcount = 1; - - return object; -} - static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback) { IMFDXGIDeviceManager *manager; @@ -147,18 +114,18 @@ static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback) HRESULT hr; hr = pMFCreateDXGIDeviceManager(&token, &manager); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to create dxgi device manager, hr %#x.\n", hr); hr = MFCreateAttributes(&attributes, 3); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to create attributes, hr %#x.\n", hr); hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)callback); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_UNKNOWN); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); hr = IMFMediaEngineClassFactory_CreateInstance(factory, 0, attributes, &media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to create media engine, hr %#x.\n", hr); IMFAttributes_Release(attributes); IMFDXGIDeviceManager_Release(manager); @@ -166,24 +133,11 @@ static IMFMediaEngine *create_media_engine(IMFMediaEngineNotify *callback) return media_engine; } -static IMFMediaEngineEx *create_media_engine_ex(IMFMediaEngineNotify *callback) -{ - IMFMediaEngine *engine = create_media_engine(callback); - IMFMediaEngineEx *engine_ex = NULL; - - if (engine) - { - IMFMediaEngine_QueryInterface(engine, &IID_IMFMediaEngineEx, (void **)&engine_ex); - IMFMediaEngine_Release(engine); - } - - return engine_ex; -} - static void test_factory(void) { + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *notify = ¬ify_impl.IMFMediaEngineNotify_iface; IMFMediaEngineClassFactory *factory, *factory2; - struct media_engine_notify *notify; IMFDXGIDeviceManager *manager; IMFMediaEngine *media_engine; IMFAttributes *attributes; @@ -192,158 +146,118 @@ static void test_factory(void) hr = CoCreateInstance(&CLSID_MFMediaEngineClassFactory, NULL, CLSCTX_INPROC_SERVER, &IID_IMFMediaEngineClassFactory, (void **)&factory); - ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG) /* pre-win8 */, "Failed to create class factory, hr %#lx.\n", hr); + ok(hr == S_OK || broken(hr == REGDB_E_CLASSNOTREG) /* pre-win8 */, "Failed to create class factory, hr %#x.\n", hr); if (FAILED(hr)) { win_skip("Media Engine is not supported.\n"); return; } - notify = create_callback(); - /* Aggregation is not supported. */ hr = CoCreateInstance(&CLSID_MFMediaEngineClassFactory, (IUnknown *)factory, CLSCTX_INPROC_SERVER, &IID_IMFMediaEngineClassFactory, (void **)&factory2); - ok(hr == CLASS_E_NOAGGREGATION, "Unexpected hr %#lx.\n", hr); + ok(hr == CLASS_E_NOAGGREGATION, "Unexpected hr %#x.\n", hr); hr = pMFCreateDXGIDeviceManager(&token, &manager); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "MFCreateDXGIDeviceManager failed: %#x.\n", hr); hr = MFCreateAttributes(&attributes, 3); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "MFCreateAttributes failed: %#x.\n", hr); - hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); - ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, + attributes, &media_engine); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "IMFMediaEngineClassFactory_CreateInstance got %#x.\n", hr); hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_OPM_HWND, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); - ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "IMFAttributes_SetUnknown failed: %#x.\n", hr); + hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, + attributes, &media_engine); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "IMFMediaEngineClassFactory_CreateInstance got %#x.\n", hr); IMFAttributes_DeleteAllItems(attributes); - hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)¬ify->IMFMediaEngineNotify_iface); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)notify); + ok(hr == S_OK, "IMFAttributes_SetUnknown failed: %#x.\n", hr); hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_UNKNOWN); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "IMFAttributes_SetUINT32 failed: %#x.\n", hr); EXPECT_REF(factory, 1); - hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, + attributes, &media_engine); + ok(hr == S_OK, "IMFMediaEngineClassFactory_CreateInstance failed: %#x.\n", hr); EXPECT_REF(factory, 1); IMFMediaEngine_Release(media_engine); IMFAttributes_Release(attributes); IMFDXGIDeviceManager_Release(manager); IMFMediaEngineClassFactory_Release(factory); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_CreateInstance(void) { - struct media_engine_notify *notify; - IMFMediaEngineEx *media_engine_ex; + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *notify = ¬ify_impl.IMFMediaEngineNotify_iface; IMFDXGIDeviceManager *manager; IMFMediaEngine *media_engine; IMFAttributes *attributes; - IUnknown *unk; UINT token; HRESULT hr; - BOOL ret; - - notify = create_callback(); hr = pMFCreateDXGIDeviceManager(&token, &manager); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to create dxgi device manager, hr %#x.\n", hr); hr = MFCreateAttributes(&attributes, 3); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to create attributes, hr %#x.\n", hr); hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); - ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#x.\n", hr); hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_OPM_HWND, NULL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); - ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_ATTRIBUTENOTFOUND, "Unexpected hr %#x.\n", hr); IMFAttributes_DeleteAllItems(attributes); - hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)¬ify->IMFMediaEngineNotify_iface); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMFAttributes_SetUnknown(attributes, &MF_MEDIA_ENGINE_CALLBACK, (IUnknown *)notify); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); hr = IMFAttributes_SetUINT32(attributes, &MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, DXGI_FORMAT_UNKNOWN); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_REAL_TIME_MODE - | MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - check_interface(media_engine, &IID_IMFMediaEngine, TRUE); - - hr = IMFMediaEngine_QueryInterface(media_engine, &IID_IMFGetService, (void **)&unk); - ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* supported since win10 */, "Unexpected hr %#lx.\n", hr); - if (SUCCEEDED(hr)) - IUnknown_Release(unk); - - if (SUCCEEDED(IMFMediaEngine_QueryInterface(media_engine, &IID_IMFMediaEngineEx, (void **)&media_engine_ex))) - { - hr = IMFMediaEngineEx_GetRealTimeMode(media_engine_ex, &ret); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(ret, "Unexpected value.\n"); - - hr = IMFMediaEngineEx_SetRealTimeMode(media_engine_ex, FALSE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetRealTimeMode(media_engine_ex, &ret); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(!ret, "Unexpected value.\n"); + ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr); - hr = IMFMediaEngineEx_SetRealTimeMode(media_engine_ex, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetRealTimeMode(media_engine_ex, &ret); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(ret, "Unexpected value.\n"); - - IMFMediaEngineEx_Release(media_engine_ex); - } + hr = IMFMediaEngineClassFactory_CreateInstance(factory, MF_MEDIA_ENGINE_WAITFORSTABLE_STATE, attributes, &media_engine); + ok(hr == S_OK, "Failed to create media engine, hr %#x.\n", hr); IMFMediaEngine_Release(media_engine); IMFAttributes_Release(attributes); IMFDXGIDeviceManager_Release(manager); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_Shutdown(void) { - struct media_engine_notify *notify; - IMFMediaEngineEx *media_engine_ex; + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *callback = ¬ify_impl.IMFMediaEngineNotify_iface; IMFMediaTimeRange *time_range; IMFMediaEngine *media_engine; - PROPVARIANT propvar; - DWORD flags, cx, cy; unsigned int state; - UINT32 value; + DWORD cx, cy; double val; HRESULT hr; BSTR str; - BOOL ret; - notify = create_callback(); - - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(callback); hr = IMFMediaEngine_Shutdown(media_engine); - ok(hr == S_OK, "Failed to shut down, hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); hr = IMFMediaEngine_Shutdown(media_engine); - ok(hr == MF_E_SHUTDOWN || broken(hr == S_OK) /* before win10 */, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN || broken(hr == S_OK) /* before win10 */, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_SetSource(media_engine, NULL); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_GetCurrentSource(media_engine, &str); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_GetNetworkState(media_engine); ok(!state, "Unexpected state %d.\n", state); @@ -353,26 +267,29 @@ static void test_Shutdown(void) ok(!state, "Unexpected state %d.\n", state); hr = IMFMediaEngine_SetPreload(media_engine, MF_MEDIA_ENGINE_PRELOAD_AUTOMATIC); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_GetPreload(media_engine); ok(state == MF_MEDIA_ENGINE_PRELOAD_AUTOMATIC, "Unexpected state %d.\n", state); hr = IMFMediaEngine_SetPreload(media_engine, 100); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_GetPreload(media_engine); ok(state == 100, "Unexpected state %d.\n", state); hr = IMFMediaEngine_GetBuffered(media_engine, &time_range); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_Load(media_engine); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); str = SysAllocString(L"video/mp4"); hr = IMFMediaEngine_CanPlayType(media_engine, str, &state); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); SysFreeString(str); state = IMFMediaEngine_GetReadyState(media_engine); @@ -385,7 +302,8 @@ static void test_Shutdown(void) ok(val == 0.0, "Unexpected time %f.\n", val); hr = IMFMediaEngine_SetCurrentTime(media_engine, 1.0); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); val = IMFMediaEngine_GetStartTime(media_engine); ok(val == 0.0, "Unexpected time %f.\n", val); @@ -397,16 +315,18 @@ static void test_Shutdown(void) ok(val == 1.0, "Unexpected rate %f.\n", val); hr = IMFMediaEngine_SetDefaultPlaybackRate(media_engine, 2.0); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); val = IMFMediaEngine_GetPlaybackRate(media_engine); ok(val == 1.0, "Unexpected rate %f.\n", val); hr = IMFMediaEngine_GetPlayed(media_engine, &time_range); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_GetSeekable(media_engine, &time_range); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_IsEnded(media_engine); ok(!state, "Unexpected state %d.\n", state); @@ -416,7 +336,7 @@ static void test_Shutdown(void) ok(!state, "Unexpected state.\n"); hr = IMFMediaEngine_SetAutoPlay(media_engine, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_GetAutoPlay(media_engine); ok(!!state, "Unexpected state.\n"); @@ -426,28 +346,30 @@ static void test_Shutdown(void) ok(!state, "Unexpected state.\n"); hr = IMFMediaEngine_SetLoop(media_engine, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_GetLoop(media_engine); ok(!!state, "Unexpected state.\n"); hr = IMFMediaEngine_Play(media_engine); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_Pause(media_engine); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_GetMuted(media_engine); ok(!state, "Unexpected state.\n"); hr = IMFMediaEngine_SetMuted(media_engine, TRUE); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); val = IMFMediaEngine_GetVolume(media_engine); ok(val == 1.0, "Unexpected value %f.\n", val); hr = IMFMediaEngine_SetVolume(media_engine, 2.0); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); state = IMFMediaEngine_HasVideo(media_engine); ok(!state, "Unexpected state.\n"); @@ -456,59 +378,18 @@ static void test_Shutdown(void) ok(!state, "Unexpected state.\n"); hr = IMFMediaEngine_GetNativeVideoSize(media_engine, &cx, &cy); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_GetVideoAspectRatio(media_engine, &cx, &cy); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - if (SUCCEEDED(IMFMediaEngine_QueryInterface(media_engine, &IID_IMFMediaEngineEx, (void **)&media_engine_ex))) - { - hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine_ex, NULL, NULL); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetAudioStreamCategory(media_engine_ex, &value); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetAudioEndpointRole(media_engine_ex, &value); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_SetAudioStreamCategory(media_engine_ex, AudioCategory_ForegroundOnlyMedia); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_SetAudioEndpointRole(media_engine_ex, eConsole); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetResourceCharacteristics(media_engine_ex, NULL); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetResourceCharacteristics(media_engine_ex, &flags); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetRealTimeMode(media_engine_ex, NULL); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetRealTimeMode(media_engine_ex, &ret); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_SetRealTimeMode(media_engine_ex, TRUE); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetPresentationAttribute(media_engine_ex, &MF_PD_DURATION, &propvar); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetStreamAttribute(media_engine_ex, 0, &MF_SD_PROTECTED, &propvar); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); - - IMFMediaEngineEx_Release(media_engine_ex); - } + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); IMFMediaEngine_Release(media_engine); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_Play(void) { - struct media_engine_notify *notify; + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *callback = ¬ify_impl.IMFMediaEngineNotify_iface; IMFMediaTimeRange *range, *range1; IMFMediaEngine *media_engine; LONGLONG pts; @@ -516,18 +397,16 @@ static void test_Play(void) HRESULT hr; BOOL ret; - notify = create_callback(); - - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(callback); hr = IMFMediaEngine_GetBuffered(media_engine, &range); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_GetBuffered(media_engine, &range1); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(range != range1, "Unexpected pointer.\n"); count = IMFMediaTimeRange_GetLength(range); - ok(!count, "Unexpected count %lu.\n", count); + ok(!count, "Unexpected count %u.\n", count); IMFMediaTimeRange_Release(range); IMFMediaTimeRange_Release(range1); @@ -536,30 +415,30 @@ static void test_Play(void) ok(ret, "Unexpected state %d.\n", ret); hr = IMFMediaEngine_OnVideoStreamTick(media_engine, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr); pts = 0; hr = IMFMediaEngine_OnVideoStreamTick(media_engine, &pts); - ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + ok(hr == S_FALSE, "Unexpected hr %#x.\n", hr); ok(pts == MINLONGLONG, "Unexpected timestamp.\n"); hr = IMFMediaEngine_Play(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ret = IMFMediaEngine_IsPaused(media_engine); ok(!ret, "Unexpected state %d.\n", ret); hr = IMFMediaEngine_Play(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_Shutdown(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_OnVideoStreamTick(media_engine, NULL); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_OnVideoStreamTick(media_engine, &pts); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); ret = IMFMediaEngine_IsPaused(media_engine); ok(!ret, "Unexpected state %d.\n", ret); @@ -567,34 +446,32 @@ static void test_Play(void) IMFMediaEngine_Release(media_engine); /* Play -> Pause */ - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(callback); hr = IMFMediaEngine_Play(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ret = IMFMediaEngine_IsPaused(media_engine); ok(!ret, "Unexpected state %d.\n", ret); hr = IMFMediaEngine_Pause(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ret = IMFMediaEngine_IsPaused(media_engine); ok(!!ret, "Unexpected state %d.\n", ret); IMFMediaEngine_Release(media_engine); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_playback_rate(void) { - struct media_engine_notify *notify; + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *callback = ¬ify_impl.IMFMediaEngineNotify_iface; IMFMediaEngine *media_engine; double rate; HRESULT hr; - notify = create_callback(); - - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(callback); rate = IMFMediaEngine_GetDefaultPlaybackRate(media_engine); ok(rate == 1.0, "Unexpected default rate.\n"); @@ -603,132 +480,127 @@ static void test_playback_rate(void) ok(rate == 1.0, "Unexpected default rate.\n"); hr = IMFMediaEngine_SetPlaybackRate(media_engine, 0.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); rate = IMFMediaEngine_GetPlaybackRate(media_engine); ok(rate == 0.0, "Unexpected default rate.\n"); hr = IMFMediaEngine_SetDefaultPlaybackRate(media_engine, 0.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); IMFMediaEngine_Release(media_engine); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_mute(void) { - struct media_engine_notify *notify; + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *callback = ¬ify_impl.IMFMediaEngineNotify_iface; IMFMediaEngine *media_engine; HRESULT hr; BOOL ret; - notify = create_callback(); - - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(callback); ret = IMFMediaEngine_GetMuted(media_engine); ok(!ret, "Unexpected state.\n"); hr = IMFMediaEngine_SetMuted(media_engine, TRUE); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ret = IMFMediaEngine_GetMuted(media_engine); ok(ret, "Unexpected state.\n"); hr = IMFMediaEngine_Shutdown(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ret = IMFMediaEngine_GetMuted(media_engine); ok(ret, "Unexpected state.\n"); IMFMediaEngine_Release(media_engine); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_error(void) { - struct media_engine_notify *notify; + struct media_engine_notify notify_impl = {{&media_engine_notify_vtbl}, 1}; + IMFMediaEngineNotify *callback = ¬ify_impl.IMFMediaEngineNotify_iface; IMFMediaEngine *media_engine; IMFMediaError *eo, *eo2; unsigned int code; HRESULT hr; - notify = create_callback(); - - media_engine = create_media_engine(¬ify->IMFMediaEngineNotify_iface); + media_engine = create_media_engine(callback); eo = (void *)0xdeadbeef; hr = IMFMediaEngine_GetError(media_engine, &eo); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(!eo, "Unexpected instance.\n"); hr = IMFMediaEngine_SetErrorCode(media_engine, MF_MEDIA_ENGINE_ERR_ENCRYPTED + 1); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); hr = IMFMediaEngine_SetErrorCode(media_engine, MF_MEDIA_ENGINE_ERR_ABORTED); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); eo = NULL; hr = IMFMediaEngine_GetError(media_engine, &eo); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(!!eo, "Unexpected instance.\n"); eo2 = NULL; hr = IMFMediaEngine_GetError(media_engine, &eo2); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(eo2 != eo, "Unexpected instance.\n"); IMFMediaError_Release(eo2); IMFMediaError_Release(eo); hr = IMFMediaEngine_SetErrorCode(media_engine, MF_MEDIA_ENGINE_ERR_NOERROR); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); eo = (void *)0xdeadbeef; hr = IMFMediaEngine_GetError(media_engine, &eo); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(!eo, "Unexpected instance.\n"); hr = IMFMediaEngine_Shutdown(media_engine); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to shut down, hr %#x.\n", hr); eo = (void *)0xdeadbeef; hr = IMFMediaEngine_GetError(media_engine, &eo); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); ok(!eo, "Unexpected instance.\n"); hr = IMFMediaEngine_SetErrorCode(media_engine, MF_MEDIA_ENGINE_ERR_NOERROR); - ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#lx.\n", hr); + ok(hr == MF_E_SHUTDOWN, "Unexpected hr %#x.\n", hr); IMFMediaEngine_Release(media_engine); /* Error object. */ hr = IMFMediaEngineClassFactory_CreateError(factory, &eo); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Failed to create error object, hr %#x.\n", hr); code = IMFMediaError_GetErrorCode(eo); ok(code == MF_MEDIA_ENGINE_ERR_NOERROR, "Unexpected code %u.\n", code); hr = IMFMediaError_GetExtendedErrorCode(eo); - ok(hr == S_OK, "Unexpected code %#lx.\n", hr); + ok(hr == S_OK, "Unexpected code %#x.\n", hr); hr = IMFMediaError_SetErrorCode(eo, MF_MEDIA_ENGINE_ERR_ENCRYPTED + 1); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); hr = IMFMediaError_SetErrorCode(eo, MF_MEDIA_ENGINE_ERR_ABORTED); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); code = IMFMediaError_GetErrorCode(eo); ok(code == MF_MEDIA_ENGINE_ERR_ABORTED, "Unexpected code %u.\n", code); hr = IMFMediaError_SetExtendedErrorCode(eo, E_FAIL); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); hr = IMFMediaError_GetExtendedErrorCode(eo); - ok(hr == E_FAIL, "Unexpected code %#lx.\n", hr); + ok(hr == E_FAIL, "Unexpected code %#x.\n", hr); IMFMediaError_Release(eo); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); } static void test_time_range(void) @@ -740,11 +612,11 @@ static void test_time_range(void) BOOL ret; hr = IMFMediaEngineClassFactory_CreateTimeRange(factory, &range); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); /* Empty ranges. */ hr = IMFMediaTimeRange_Clear(range); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ret = IMFMediaTimeRange_ContainsTime(range, 10.0); ok(!ret, "Unexpected return value %d.\n", ret); @@ -753,225 +625,81 @@ static void test_time_range(void) ok(!count, "Unexpected range count.\n"); hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); /* Add a range. */ hr = IMFMediaTimeRange_AddRange(range, 10.0, 1.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); count = IMFMediaTimeRange_GetLength(range); ok(count == 1, "Unexpected range count.\n"); hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(start == 10.0, "Unexpected start %.e.\n", start); hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(end == 1.0, "Unexpected end %.e.\n", end); hr = IMFMediaTimeRange_AddRange(range, 2.0, 3.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); count = IMFMediaTimeRange_GetLength(range); ok(count == 1, "Unexpected range count.\n"); hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); +todo_wine ok(start == 2.0, "Unexpected start %.8e.\n", start); hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); +todo_wine ok(end == 3.0, "Unexpected end %.8e.\n", end); hr = IMFMediaTimeRange_AddRange(range, 10.0, 9.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +todo_wine + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); count = IMFMediaTimeRange_GetLength(range); +todo_wine ok(count == 2, "Unexpected range count.\n"); hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); +todo_wine ok(start == 2.0, "Unexpected start %.8e.\n", start); hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); +todo_wine ok(end == 3.0, "Unexpected end %.8e.\n", end); start = 0.0; hr = IMFMediaTimeRange_GetStart(range, 1, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 10.0, "Unexpected start %.8e.\n", start); - - hr = IMFMediaTimeRange_GetEnd(range, 1, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(end == 9.0, "Unexpected end %.8e.\n", end); - - hr = IMFMediaTimeRange_AddRange(range, 2.0, 9.1); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - count = IMFMediaTimeRange_GetLength(range); - ok(count == 2, "Unexpected range count.\n"); - - hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 2.0, "Unexpected start %.8e.\n", start); - - hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(end == 9.1, "Unexpected end %.8e.\n", end); - - hr = IMFMediaTimeRange_GetStart(range, 1, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(start == 10.0, "Unexpected start %.8e.\n", start); - +} hr = IMFMediaTimeRange_GetEnd(range, 1, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); ok(end == 9.0, "Unexpected end %.8e.\n", end); - - hr = IMFMediaTimeRange_AddRange(range, 8.5, 2.5); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - count = IMFMediaTimeRange_GetLength(range); - ok(count == 2, "Unexpected range count.\n"); - - hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 2.0, "Unexpected start %.8e.\n", start); - - hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(end == 9.1, "Unexpected end %.8e.\n", end); - - hr = IMFMediaTimeRange_AddRange(range, 20.0, 20.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - count = IMFMediaTimeRange_GetLength(range); - ok(count == 3, "Unexpected range count.\n"); - +} hr = IMFMediaTimeRange_Clear(range); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(hr == S_OK, "Unexpected hr %#x.\n", hr); count = IMFMediaTimeRange_GetLength(range); ok(!count, "Unexpected range count.\n"); - /* Intersect */ - hr = IMFMediaTimeRange_AddRange(range, 5.0, 10.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaTimeRange_AddRange(range, 6.0, 12.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 5.0, "Unexpected start %.8e.\n", start); - - hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(end == 12.0, "Unexpected end %.8e.\n", end); - - count = IMFMediaTimeRange_GetLength(range); - ok(count == 1, "Unexpected range count.\n"); - - hr = IMFMediaTimeRange_AddRange(range, 4.0, 6.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - count = IMFMediaTimeRange_GetLength(range); - ok(count == 1, "Unexpected range count.\n"); - - hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 4.0, "Unexpected start %.8e.\n", start); - - hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(end == 12.0, "Unexpected end %.8e.\n", end); - - hr = IMFMediaTimeRange_AddRange(range, 5.0, 3.0); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - - count = IMFMediaTimeRange_GetLength(range); - ok(count == 1, "Unexpected range count.\n"); - - hr = IMFMediaTimeRange_GetStart(range, 0, &start); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(start == 4.0, "Unexpected start %.8e.\n", start); - - hr = IMFMediaTimeRange_GetEnd(range, 0, &end); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(end == 12.0, "Unexpected end %.8e.\n", end); - IMFMediaTimeRange_Release(range); } -static void test_SetSourceFromByteStream(void) -{ - struct media_engine_notify *notify; - IMFMediaEngineEx *media_engine; - PROPVARIANT propvar; - DWORD flags; - HRESULT hr; - - notify = create_callback(); - - media_engine = create_media_engine_ex(¬ify->IMFMediaEngineNotify_iface); - if (!media_engine) - { - win_skip("IMFMediaEngineEx is not supported.\n"); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); - return; - } - - hr = IMFMediaEngineEx_SetSourceFromByteStream(media_engine, NULL, NULL); - ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetResourceCharacteristics(media_engine, NULL); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetResourceCharacteristics(media_engine, &flags); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetPresentationAttribute(media_engine, &MF_PD_DURATION, &propvar); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - - hr = IMFMediaEngineEx_GetStreamAttribute(media_engine, 0, &MF_SD_PROTECTED, &propvar); - ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); - - IMFMediaEngineEx_Release(media_engine); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); -} - -static void test_audio_configuration(void) -{ - struct media_engine_notify *notify; - IMFMediaEngineEx *media_engine; - UINT32 value; - HRESULT hr; - - notify = create_callback(); - - media_engine = create_media_engine_ex(¬ify->IMFMediaEngineNotify_iface); - if (!media_engine) - { - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); - return; - } - - hr = IMFMediaEngineEx_GetAudioStreamCategory(media_engine, &value); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(value == AudioCategory_Other, "Unexpected value %u.\n", value); - - hr = IMFMediaEngineEx_GetAudioEndpointRole(media_engine, &value); - ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); - ok(value == eMultimedia, "Unexpected value %u.\n", value); - - IMFMediaEngineEx_Release(media_engine); - IMFMediaEngineNotify_Release(¬ify->IMFMediaEngineNotify_iface); -} - START_TEST(mfmediaengine) { HRESULT hr; @@ -990,7 +718,7 @@ START_TEST(mfmediaengine) init_functions(); hr = MFStartup(MF_VERSION, MFSTARTUP_FULL); - ok(hr == S_OK, "MFStartup failed: %#lx.\n", hr); + ok(hr == S_OK, "MFStartup failed: %#x.\n", hr); test_factory(); test_CreateInstance(); @@ -1000,8 +728,6 @@ START_TEST(mfmediaengine) test_mute(); test_error(); test_time_range(); - test_SetSourceFromByteStream(); - test_audio_configuration(); IMFMediaEngineClassFactory_Release(factory); diff --git a/dlls/mfplat/Makefile.in b/dlls/mfplat/Makefile.in index 4515d652c58..79af7650de6 100644 --- wine/dlls/mfplat/Makefile.in +++ wine/dlls/mfplat/Makefile.in @@ -1,7 +1,7 @@ +EXTRADEFS = -DWINE_NO_LONG_TYPES MODULE = mfplat.dll IMPORTLIB = mfplat -IMPORTS = advapi32 ole32 mfuuid propsys rtworkq kernelbase -DELAYIMPORTS = bcrypt +IMPORTS = advapi32 ole32 mfuuid propsys rtworkq EXTRADLLFLAGS = -Wb,--prefer-native diff --git a/dlls/mfplat/aac_decoder.c b/dlls/mfplat/aac_decoder.c new file mode 100644 index 00000000000..97f2824039d --- /dev/null +++ wine/dlls/mfplat/aac_decoder.c @@ -0,0 +1,620 @@ +/* AAC Decoder Transform + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mftransform.h" +#include "wmcodecdsp.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +static const GUID *aac_decoder_input_types[] = +{ + &MFAudioFormat_AAC, +}; +static const GUID *aac_decoder_output_types[] = +{ + &MFAudioFormat_PCM, + &MFAudioFormat_Float, +}; + +struct aac_decoder +{ + IMFTransform IMFTransform_iface; + LONG refcount; + IMFMediaType *input_type; + IMFMediaType *output_type; + + IMFSample *input_sample; + struct wg_transform *wg_transform; +}; + +static struct aac_decoder *impl_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct aac_decoder, IMFTransform_iface); +} + +static void try_create_wg_transform(struct aac_decoder *decoder) +{ + struct wg_encoded_format input_format; + struct wg_format output_format; + + if (!decoder->input_type || !decoder->output_type) + return; + + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); + + mf_media_type_to_wg_encoded_format(decoder->input_type, &input_format); + if (input_format.encoded_type == WG_ENCODED_TYPE_UNKNOWN) + return; + + mf_media_type_to_wg_format(decoder->output_type, &output_format); + if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) + return; + + decoder->wg_transform = wg_transform_create(&input_format, &output_format); + if (!decoder->wg_transform) + WARN("Failed to create wg_transform.\n"); +} + +static HRESULT WINAPI aac_decoder_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IMFTransform)) + *out = &decoder->IMFTransform_iface; + else + { + *out = NULL; + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI aac_decoder_AddRef(IMFTransform *iface) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&decoder->refcount); + + TRACE("iface %p increasing refcount to %u.\n", decoder, refcount); + + return refcount; +} + +static ULONG WINAPI aac_decoder_Release(IMFTransform *iface) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&decoder->refcount); + + TRACE("iface %p decreasing refcount to %u.\n", decoder, refcount); + + if (!refcount) + { + if (decoder->input_sample) + IMFSample_Release(decoder->input_sample); + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); + if (decoder->input_type) + IMFMediaType_Release(decoder->input_type); + if (decoder->output_type) + IMFMediaType_Release(decoder->output_type); + free(decoder); + } + + return refcount; +} + +static HRESULT WINAPI aac_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + FIXME("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p stub!\n", + iface, input_minimum, input_maximum, output_minimum, output_maximum); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + FIXME("iface %p, inputs %p, outputs %p stub!\n", iface, inputs, outputs); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + FIXME("iface %p, input_size %u, inputs %p, output_size %u, outputs %p stub!\n", + iface, input_size, inputs, output_size, outputs); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + UINT32 block_alignment; + HRESULT hr; + + TRACE("iface %p, id %u, info %p.\n", iface, id, info); + + if (!decoder->input_type || !decoder->output_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment))) + return hr; + + info->hnsMaxLatency = 0; + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES|MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER + |MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE|MFT_INPUT_STREAM_HOLDS_BUFFERS; + info->cbSize = 0; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + + return S_OK; +} + +static HRESULT WINAPI aac_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + UINT32 channel_count, block_alignment; + HRESULT hr; + + TRACE("iface %p, id %u, info %p.\n", iface, id, info); + + if (!decoder->input_type || !decoder->output_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->output_type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count))) + return hr; + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->output_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment))) + return hr; + + info->dwFlags = 0; + info->cbSize = 0x1800 * block_alignment * channel_count; + info->cbAlignment = 0; + + return S_OK; +} + +static HRESULT WINAPI aac_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + FIXME("iface %p, attributes %p stub!\n", iface, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("iface %p, id %u, attributes %p stub!\n", iface, id, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("iface %p, id %u, attributes %p stub!\n", iface, id, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + FIXME("iface %p, id %u stub!\n", iface, id); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + FIXME("iface %p, streams %u, ids %p stub!\n", iface, streams, ids); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + FIXME("iface %p, id %u, index %u, type %p stub!\n", iface, id, index, type); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + UINT32 channel_count, sample_size, sample_rate, block_alignment; + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + IMFMediaType *media_type; + const GUID *output_type; + HRESULT hr; + + TRACE("iface %p, id %u, index %u, type %p.\n", iface, id, index, type); + + if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + *type = NULL; + + if (index >= ARRAY_SIZE(aac_decoder_output_types)) + return MF_E_NO_MORE_TYPES; + index = ARRAY_SIZE(aac_decoder_output_types) - index - 1; + output_type = aac_decoder_output_types[index]; + + if (FAILED(hr = MFCreateMediaType(&media_type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, output_type))) + goto done; + + if (IsEqualGUID(output_type, &MFAudioFormat_Float)) + sample_size = 32; + else if (IsEqualGUID(output_type, &MFAudioFormat_PCM)) + sample_size = 16; + else + { + FIXME("Subtype %s not implemented!\n", debugstr_guid(output_type)); + hr = E_NOTIMPL; + goto done; + } + + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, sample_size))) + goto done; + + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_NUM_CHANNELS, &channel_count))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_NUM_CHANNELS, channel_count))) + goto done; + + if (FAILED(hr = IMFMediaType_GetUINT32(decoder->input_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &sample_rate))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, sample_rate))) + goto done; + + block_alignment = sample_size * channel_count / 8; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, block_alignment))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, sample_rate * block_alignment))) + goto done; + + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, 1))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_FIXED_SIZE_SAMPLES, 1))) + goto done; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, 1))) + goto done; + +done: + if (SUCCEEDED(hr)) + IMFMediaType_AddRef((*type = media_type)); + + IMFMediaType_Release(media_type); + return hr; +} + +static HRESULT WINAPI aac_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + MF_ATTRIBUTE_TYPE item_type; + GUID major, subtype; + HRESULT hr; + ULONG i; + + TRACE("iface %p, id %u, type %p, flags %#x.\n", iface, id, type, flags); + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) || + FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (!IsEqualGUID(&major, &MFMediaType_Audio)) + return MF_E_INVALIDMEDIATYPE; + + for (i = 0; i < ARRAY_SIZE(aac_decoder_input_types); ++i) + if (IsEqualGUID(&subtype, aac_decoder_input_types[i])) + break; + if (i == ARRAY_SIZE(aac_decoder_input_types)) + return MF_E_INVALIDMEDIATYPE; + + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_USER_DATA, &item_type)) || + item_type != MF_ATTRIBUTE_BLOB) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_NUM_CHANNELS, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_PREFER_WAVEFORMATEX, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + + if (!decoder->input_type && FAILED(hr = MFCreateMediaType(&decoder->input_type))) + return hr; + + if (decoder->output_type) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + return IMFMediaType_CopyAllItems(type, (IMFAttributes *)decoder->input_type); +} + +static HRESULT WINAPI aac_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + MF_ATTRIBUTE_TYPE item_type; + ULONG i, sample_size; + GUID major, subtype; + HRESULT hr; + + TRACE("iface %p, id %u, type %p, flags %#x.\n", iface, id, type, flags); + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) || + FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (!IsEqualGUID(&major, &MFMediaType_Audio)) + return MF_E_INVALIDMEDIATYPE; + + for (i = 0; i < ARRAY_SIZE(aac_decoder_output_types); ++i) + if (IsEqualGUID(&subtype, aac_decoder_output_types[i])) + break; + if (i == ARRAY_SIZE(aac_decoder_output_types)) + return MF_E_INVALIDMEDIATYPE; + + if (IsEqualGUID(&subtype, &MFAudioFormat_Float)) + sample_size = 32; + else if (IsEqualGUID(&subtype, &MFAudioFormat_PCM)) + sample_size = 16; + else + { + FIXME("Subtype %s not implemented!\n", debugstr_guid(&subtype)); + hr = E_NOTIMPL; + return hr; + } + + if (FAILED(IMFMediaType_SetUINT32(decoder->input_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, sample_size))) + return MF_E_INVALIDMEDIATYPE; + + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_NUM_CHANNELS, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + if (FAILED(IMFMediaType_GetItemType(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &item_type)) || + item_type != MF_ATTRIBUTE_UINT32) + return MF_E_INVALIDMEDIATYPE; + + if (!decoder->output_type && FAILED(hr = MFCreateMediaType(&decoder->output_type))) + return hr; + + if (FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *)decoder->output_type))) + return hr; + + try_create_wg_transform(decoder); + return S_OK; +} + +static HRESULT WINAPI aac_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("iface %p, id %u, type %p stub!\n", iface, id, type); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("iface %p, id %u, type %p stub!\n", iface, id, type); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + FIXME("iface %p, id %u, flags %p stub!\n", iface, id, flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("iface %p, flags %p stub!\n", iface, flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("iface %p, lower %s, upper %s stub!\n", iface, + wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + FIXME("iface %p, id %u, event %p stub!\n", iface, id, event); + return E_NOTIMPL; +} + +static HRESULT WINAPI aac_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + return S_OK; +} + +static HRESULT WINAPI aac_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + IMFMediaBuffer *media_buffer; + MFT_INPUT_STREAM_INFO info; + UINT32 buffer_size; + BYTE *buffer; + HRESULT hr; + + TRACE("iface %p, id %u, sample %p, flags %#x.\n", iface, id, sample, flags); + + if (FAILED(hr = IMFTransform_GetInputStreamInfo(iface, 0, &info))) + return hr; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (decoder->input_sample) + return MF_E_NOTACCEPTING; + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &media_buffer))) + return hr; + + if (FAILED(hr = IMFMediaBuffer_Lock(media_buffer, &buffer, NULL, &buffer_size))) + goto done; + + if (SUCCEEDED(hr = wg_transform_push_data(decoder->wg_transform, buffer, buffer_size))) + IMFSample_AddRef((decoder->input_sample = sample)); + + IMFMediaBuffer_Unlock(media_buffer); + +done: + IMFMediaBuffer_Release(media_buffer); + return hr; +} + +static HRESULT WINAPI aac_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct aac_decoder *decoder = impl_from_IMFTransform(iface); + struct wg_sample wg_sample = {0}; + IMFMediaBuffer *media_buffer; + MFT_OUTPUT_STREAM_INFO info; + HRESULT hr; + + TRACE("iface %p, flags %#x, count %u, samples %p, status %p.\n", iface, flags, count, samples, status); + + if (count > 1) + { + FIXME("Not implemented count %u\n", count); + return E_NOTIMPL; + } + + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) + return hr; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + *status = 0; + samples[0].dwStatus = 0; + if (!samples[0].pSample) + { + samples[0].dwStatus = MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &media_buffer))) + return hr; + + if (FAILED(hr = IMFMediaBuffer_Lock(media_buffer, &wg_sample.data, &wg_sample.size, NULL))) + goto done; + + if (wg_sample.size < info.cbSize) + hr = MF_E_BUFFERTOOSMALL; + else if (SUCCEEDED(hr = wg_transform_read_data(decoder->wg_transform, &wg_sample))) + { + if (wg_sample.flags & WG_SAMPLE_FLAG_INCOMPLETE) + samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_INCOMPLETE; + } + else + { + if (decoder->input_sample) + IMFSample_Release(decoder->input_sample); + decoder->input_sample = NULL; + } + + IMFMediaBuffer_Unlock(media_buffer); + +done: + IMFMediaBuffer_SetCurrentLength(media_buffer, wg_sample.size); + IMFMediaBuffer_Release(media_buffer); + return hr; +} + +static const IMFTransformVtbl aac_decoder_vtbl = +{ + aac_decoder_QueryInterface, + aac_decoder_AddRef, + aac_decoder_Release, + aac_decoder_GetStreamLimits, + aac_decoder_GetStreamCount, + aac_decoder_GetStreamIDs, + aac_decoder_GetInputStreamInfo, + aac_decoder_GetOutputStreamInfo, + aac_decoder_GetAttributes, + aac_decoder_GetInputStreamAttributes, + aac_decoder_GetOutputStreamAttributes, + aac_decoder_DeleteInputStream, + aac_decoder_AddInputStreams, + aac_decoder_GetInputAvailableType, + aac_decoder_GetOutputAvailableType, + aac_decoder_SetInputType, + aac_decoder_SetOutputType, + aac_decoder_GetInputCurrentType, + aac_decoder_GetOutputCurrentType, + aac_decoder_GetInputStatus, + aac_decoder_GetOutputStatus, + aac_decoder_SetOutputBounds, + aac_decoder_ProcessEvent, + aac_decoder_ProcessMessage, + aac_decoder_ProcessInput, + aac_decoder_ProcessOutput, +}; + +HRESULT aac_decoder_create(REFIID riid, void **ret) +{ + struct aac_decoder *decoder; + + TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); + + if (!(decoder = calloc(1, sizeof(*decoder)))) + return E_OUTOFMEMORY; + + decoder->IMFTransform_iface.lpVtbl = &aac_decoder_vtbl; + decoder->refcount = 1; + + *ret = &decoder->IMFTransform_iface; + TRACE("Created decoder %p\n", *ret); + return S_OK; +} diff --git a/dlls/mfplat/audioconvert.c b/dlls/mfplat/audioconvert.c new file mode 100644 index 00000000000..2e16c9c78f5 --- /dev/null +++ wine/dlls/mfplat/audioconvert.c @@ -0,0 +1,910 @@ +/* GStreamer Audio Converter + * + * Copyright 2020 Derek Lesho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" +#include "ks.h" +#include "ksmedia.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct audio_converter +{ + IMFTransform IMFTransform_iface; + LONG refcount; + IMFMediaType *input_type; + IMFMediaType *output_type; + CRITICAL_SECTION cs; + BOOL buffer_inflight; + LONGLONG buffer_pts, buffer_dur; + struct wg_parser *parser; + struct wg_parser_stream *stream; + IMFAttributes *attributes, *output_attributes; +}; + +static struct audio_converter *impl_audio_converter_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct audio_converter, IMFTransform_iface); +} + +static HRESULT WINAPI audio_converter_QueryInterface(IMFTransform *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFTransform) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFTransform_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI audio_converter_AddRef(IMFTransform *iface) +{ + struct audio_converter *transform = impl_audio_converter_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&transform->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI audio_converter_Release(IMFTransform *iface) +{ + struct audio_converter *transform = impl_audio_converter_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&transform->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + transform->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&transform->cs); + if (transform->attributes) + IMFAttributes_Release(transform->attributes); + if (transform->output_attributes) + IMFAttributes_Release(transform->output_attributes); + if (transform->stream) + wg_parser_disconnect(transform->parser); + if (transform->parser) + wg_parser_destroy(transform->parser); + free(transform); + } + + return refcount; +} + +static HRESULT WINAPI audio_converter_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + + return S_OK; +} + +static HRESULT WINAPI audio_converter_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + TRACE("%p, %p, %p.\n", iface, inputs, outputs); + + *inputs = *outputs = 1; + + return S_OK; +} + +static HRESULT WINAPI audio_converter_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + + TRACE("%p %u %p.\n", iface, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + info->hnsMaxLatency = 0; + info->cbSize = 0; + + EnterCriticalSection(&converter->cs); + + if (converter->input_type) + IMFMediaType_GetUINT32(converter->input_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &info->cbSize); + + LeaveCriticalSection(&converter->cs); + + return S_OK; +} + +static HRESULT WINAPI audio_converter_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + + TRACE("%p %u %p.\n", iface, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + info->dwFlags = MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES | MFT_OUTPUT_STREAM_WHOLE_SAMPLES; + info->cbAlignment = 0; + info->cbSize = 0; + + EnterCriticalSection(&converter->cs); + + if (converter->output_type) + IMFMediaType_GetUINT32(converter->output_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &info->cbSize); + + LeaveCriticalSection(&converter->cs); + + return S_OK; +} + +static HRESULT WINAPI audio_converter_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + + TRACE("%p, %p.\n", iface, attributes); + + *attributes = converter->attributes; + IMFAttributes_AddRef(*attributes); + + return S_OK; +} + +static HRESULT WINAPI audio_converter_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p.\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + + TRACE("%p, %u, %p.\n", iface, id, attributes); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + *attributes = converter->output_attributes; + IMFAttributes_AddRef(*attributes); + + return S_OK; +} + +static HRESULT WINAPI audio_converter_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + TRACE("%p, %u.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + TRACE("%p, %u, %p.\n", iface, streams, ids); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + IMFMediaType *ret; + HRESULT hr; + + TRACE("%p, %u, %u, %p.\n", iface, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= 2) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&ret))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) + { + IMFMediaType_Release(ret); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_SUBTYPE, index ? &MFAudioFormat_Float : &MFAudioFormat_PCM))) + { + IMFMediaType_Release(ret); + return hr; + } + + *type = ret; + + return S_OK; +} + +static HRESULT WINAPI audio_converter_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + IMFMediaType *output_type; + HRESULT hr; + + static const struct + { + const GUID *subtype; + DWORD depth; + } + formats[] = + { + {&MFAudioFormat_PCM, 16}, + {&MFAudioFormat_PCM, 24}, + {&MFAudioFormat_PCM, 32}, + {&MFAudioFormat_Float, 32}, + }; + + static const DWORD rates[] = {44100, 48000}; + static const DWORD channel_cnts[] = {1, 2, 6}; + const GUID *subtype; + DWORD rate, channels, bps; + + TRACE("%p, %u, %u, %p.\n", iface, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= ARRAY_SIZE(formats) * 2/*rates*/ * 3/*layouts*/) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&output_type))) + return hr; + + subtype = formats[index / 6].subtype; + bps = formats[index / 6].depth; + rate = rates[index % 2]; + channels = channel_cnts[(index / 2) % 3]; + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio))) + goto fail; + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, subtype))) + goto fail; + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, rate))) + goto fail; + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_NUM_CHANNELS, channels))) + goto fail; + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, bps))) + goto fail; + + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, channels * bps / 8))) + goto fail; + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, rate * channels * bps / 8))) + goto fail; + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_AUDIO_CHANNEL_MASK, + channels == 1 ? KSAUDIO_SPEAKER_MONO : + channels == 2 ? KSAUDIO_SPEAKER_STEREO : + /*channels == 6*/ KSAUDIO_SPEAKER_5POINT1))) + goto fail; + if (FAILED(hr = IMFMediaType_SetUINT32(output_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE))) + goto fail; + + *type = output_type; + + return S_OK; +fail: + IMFMediaType_Release(output_type); + return hr; +} + +static HRESULT WINAPI audio_converter_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + GUID major_type, subtype; + struct wg_format format; + DWORD unused; + HRESULT hr; + + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + + TRACE("%p, %u, %p, %#x.\n", iface, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!type) + { + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + if (converter->input_type) + { + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + IMFMediaType_Release(converter->input_type); + converter->input_type = NULL; + } + + LeaveCriticalSection(&converter->cs); + + return S_OK; + } + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &unused))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &unused))) + return MF_E_INVALIDTYPE; + if (IsEqualGUID(&subtype, &MFAudioFormat_PCM) && FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &unused))) + return MF_E_INVALIDTYPE; + + if (!(IsEqualGUID(&major_type, &MFMediaType_Audio))) + return MF_E_INVALIDTYPE; + + if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float)) + return MF_E_INVALIDTYPE; + + mf_media_type_to_wg_format(type, &format); + if (!format.major_type) + return MF_E_INVALIDTYPE; + + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + hr = S_OK; + + if (!converter->input_type) + hr = MFCreateMediaType(&converter->input_type); + + if (SUCCEEDED(hr)) + hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *) converter->input_type); + + if (FAILED(hr)) + { + IMFMediaType_Release(converter->input_type); + converter->input_type = NULL; + } + + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + + if (converter->input_type && converter->output_type) + { + struct wg_format output_format; + mf_media_type_to_wg_format(converter->output_type, &output_format); + + if (SUCCEEDED(hr = wg_parser_connect_unseekable(converter->parser, &format, 1, &output_format, NULL))) + converter->stream = wg_parser_get_stream(converter->parser, 0); + } + + LeaveCriticalSection(&converter->cs); + + return hr; +} + +static HRESULT WINAPI audio_converter_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + GUID major_type, subtype; + struct wg_format format; + DWORD unused; + HRESULT hr; + + TRACE("%p, %u, %p, %#x.\n", iface, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!type) + { + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + if (converter->output_type) + { + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + IMFMediaType_Release(converter->output_type); + converter->output_type = NULL; + } + + LeaveCriticalSection(&converter->cs); + + return S_OK; + } + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &unused))) + return MF_E_INVALIDTYPE; + if (IsEqualGUID(&subtype, &MFAudioFormat_PCM) && FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &unused))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &unused))) + return MF_E_INVALIDTYPE; + + if (!(IsEqualGUID(&major_type, &MFMediaType_Audio))) + return MF_E_INVALIDTYPE; + + if (!IsEqualGUID(&subtype, &MFAudioFormat_PCM) && !IsEqualGUID(&subtype, &MFAudioFormat_Float)) + return MF_E_INVALIDTYPE; + + mf_media_type_to_wg_format(type, &format); + if (!format.major_type) + return MF_E_INVALIDTYPE; + + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + hr = S_OK; + + if (!converter->output_type) + hr = MFCreateMediaType(&converter->output_type); + + if (SUCCEEDED(hr)) + hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *) converter->output_type); + + if (FAILED(hr)) + { + IMFMediaType_Release(converter->output_type); + converter->output_type = NULL; + } + + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + + if (converter->input_type && converter->output_type) + { + struct wg_format input_format; + mf_media_type_to_wg_format(converter->input_type, &input_format); + + if (SUCCEEDED(hr = wg_parser_connect_unseekable(converter->parser, &input_format, 1, &format, NULL))) + converter->stream = wg_parser_get_stream(converter->parser, 0); + } + + LeaveCriticalSection(&converter->cs); + + return hr; +} + +static HRESULT WINAPI audio_converter_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + IMFMediaType *ret; + HRESULT hr; + + TRACE("%p, %u, %p.\n", converter, id, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (FAILED(hr = MFCreateMediaType(&ret))) + return hr; + + EnterCriticalSection(&converter->cs); + + if (converter->input_type) + hr = IMFMediaType_CopyAllItems(converter->input_type, (IMFAttributes *)ret); + else + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + + LeaveCriticalSection(&converter->cs); + + if (SUCCEEDED(hr)) + *type = ret; + else + IMFMediaType_Release(ret); + + return hr; +} + +static HRESULT WINAPI audio_converter_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + IMFMediaType *ret; + HRESULT hr; + + TRACE("%p, %u, %p.\n", converter, id, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (FAILED(hr = MFCreateMediaType(&ret))) + return hr; + + EnterCriticalSection(&converter->cs); + + if (converter->output_type) + hr = IMFMediaType_CopyAllItems(converter->output_type, (IMFAttributes *)ret); + else + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + + LeaveCriticalSection(&converter->cs); + + if (SUCCEEDED(hr)) + *type = ret; + else + IMFMediaType_Release(ret); + + return hr; +} + +static HRESULT WINAPI audio_converter_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + FIXME("%p, %u, %p.\n", iface, id, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("%p, %p.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("%p, %s, %s.\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + TRACE("%p, %u, %p.\n", iface, id, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI audio_converter_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + struct wg_parser_buffer wg_buffer; + + TRACE("%p, %u %lu.\n", iface, message, param); + + switch(message) + { + case MFT_MESSAGE_COMMAND_FLUSH: + { + EnterCriticalSection(&converter->cs); + if (!converter->buffer_inflight) + { + LeaveCriticalSection(&converter->cs); + return S_OK; + } + + wg_parser_stream_get_buffer(converter->stream, &wg_buffer); + wg_parser_stream_release_buffer(converter->stream); + converter->buffer_inflight = FALSE; + + LeaveCriticalSection(&converter->cs); + return S_OK; + } + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + return S_OK; + default: + FIXME("Unhandled message type %x.\n", message); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI audio_converter_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + IMFMediaBuffer *buffer = NULL; + unsigned char *buffer_data; + DWORD buffer_size; + uint64_t offset; + uint32_t size; + HRESULT hr; + + TRACE("%p, %u, %p, %#x.\n", iface, id, sample, flags); + + if (flags) + WARN("Unsupported flags %#x.\n", flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&converter->cs); + + if (!converter->stream) + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + goto done; + } + + if (converter->buffer_inflight) + { + hr = MF_E_NOTACCEPTING; + goto done; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer))) + goto done; + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &buffer_data, NULL, &buffer_size))) + goto done; + + if (!wg_parser_get_next_read_offset(converter->parser, &offset, &size)) + { + hr = MF_E_UNEXPECTED; + IMFMediaBuffer_Unlock(buffer); + goto done; + } + + wg_parser_push_data(converter->parser, WG_READ_SUCCESS, buffer_data, buffer_size); + + IMFMediaBuffer_Unlock(buffer); + converter->buffer_inflight = TRUE; + if (FAILED(IMFSample_GetSampleTime(sample, &converter->buffer_pts))) + converter->buffer_pts = -1; + if (FAILED(IMFSample_GetSampleDuration(sample, &converter->buffer_dur))) + converter->buffer_dur = -1; + +done: + if (buffer) + IMFMediaBuffer_Release(buffer); + LeaveCriticalSection(&converter->cs); + return hr; +} + +static HRESULT WINAPI audio_converter_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct audio_converter *converter = impl_audio_converter_from_IMFTransform(iface); + IMFSample *allocated_sample = NULL; + struct wg_parser_buffer wg_buffer; + IMFMediaBuffer *buffer = NULL; + unsigned char *buffer_data; + DWORD buffer_len; + HRESULT hr = S_OK; + + TRACE("%p, %#x, %u, %p, %p.\n", iface, flags, count, samples, status); + + if (flags) + WARN("Unsupported flags %#x.\n", flags); + + if (!count) + return S_OK; + + if (count != 1) + return MF_E_INVALIDSTREAMNUMBER; + + if (samples[0].dwStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&converter->cs); + + if (!converter->stream) + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + goto done; + } + + if (!converter->buffer_inflight) + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + goto done; + } + + if (!wg_parser_stream_get_buffer(converter->stream, &wg_buffer)) + assert(0); + + if (!samples[0].pSample) + { + if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer.size, &buffer))) + { + ERR("Failed to create buffer, hr %#x.\n", hr); + goto done; + } + + if (FAILED(hr = MFCreateSample(&allocated_sample))) + { + ERR("Failed to create sample, hr %#x.\n", hr); + goto done; + } + + samples[0].pSample = allocated_sample; + + if (FAILED(hr = IMFSample_AddBuffer(samples[0].pSample, buffer))) + { + ERR("Failed to add buffer, hr %#x.\n", hr); + goto done; + } + + IMFMediaBuffer_Release(buffer); + buffer = NULL; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &buffer))) + { + ERR("Failed to get buffer from sample, hr %#x.\n", hr); + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_GetMaxLength(buffer, &buffer_len))) + { + ERR("Failed to get buffer size, hr %#x.\n", hr); + goto done; + } + + if (buffer_len < wg_buffer.size) + { + WARN("Client's buffer is smaller (%u bytes) than the output sample (%u bytes)\n", + buffer_len, wg_buffer.size); + + hr = MF_E_BUFFERTOOSMALL; + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer.size))) + { + ERR("Failed to set size, hr %#x.\n", hr); + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &buffer_data, NULL, NULL))) + { + ERR("Failed to lock buffer hr %#x.\n", hr); + goto done; + } + + if (!wg_parser_stream_copy_buffer(converter->stream, buffer_data, 0, wg_buffer.size)) + { + ERR("Failed to copy buffer.\n"); + IMFMediaBuffer_Unlock(buffer); + hr = E_FAIL; + goto done; + } + + IMFMediaBuffer_Unlock(buffer); + + wg_parser_stream_release_buffer(converter->stream); + converter->buffer_inflight = FALSE; + + if (converter->buffer_pts != -1) + IMFSample_SetSampleTime(samples[0].pSample, converter->buffer_pts); + if (converter->buffer_dur != -1) + IMFSample_SetSampleDuration(samples[0].pSample, converter->buffer_dur); + + samples[0].dwStatus = 0; + samples[0].pEvents = NULL; + + done: + if (buffer) + IMFMediaBuffer_Release(buffer); + if (allocated_sample && FAILED(hr)) + { + IMFSample_Release(allocated_sample); + samples[0].pSample = NULL; + } + LeaveCriticalSection(&converter->cs); + return hr; +} + +static const IMFTransformVtbl audio_converter_vtbl = +{ + audio_converter_QueryInterface, + audio_converter_AddRef, + audio_converter_Release, + audio_converter_GetStreamLimits, + audio_converter_GetStreamCount, + audio_converter_GetStreamIDs, + audio_converter_GetInputStreamInfo, + audio_converter_GetOutputStreamInfo, + audio_converter_GetAttributes, + audio_converter_GetInputStreamAttributes, + audio_converter_GetOutputStreamAttributes, + audio_converter_DeleteInputStream, + audio_converter_AddInputStreams, + audio_converter_GetInputAvailableType, + audio_converter_GetOutputAvailableType, + audio_converter_SetInputType, + audio_converter_SetOutputType, + audio_converter_GetInputCurrentType, + audio_converter_GetOutputCurrentType, + audio_converter_GetInputStatus, + audio_converter_GetOutputStatus, + audio_converter_SetOutputBounds, + audio_converter_ProcessEvent, + audio_converter_ProcessMessage, + audio_converter_ProcessInput, + audio_converter_ProcessOutput, +}; + +HRESULT audio_converter_create(REFIID riid, void **ret) +{ + struct audio_converter *object; + HRESULT hr; + + TRACE("%s %p\n", debugstr_guid(riid), ret); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFTransform_iface.lpVtbl = &audio_converter_vtbl; + object->refcount = 1; + + InitializeCriticalSection(&object->cs); + object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": audio_converter_lock"); + + if (FAILED(hr = MFCreateAttributes(&object->attributes, 0))) + { + IMFTransform_Release(&object->IMFTransform_iface); + return hr; + } + + if (FAILED(hr = MFCreateAttributes(&object->output_attributes, 0))) + { + IMFTransform_Release(&object->IMFTransform_iface); + return hr; + } + + if (!(object->parser = wg_parser_create(WG_PARSER_AUDIOCONV, true))) + { + ERR("Failed to create audio converter due to GStreamer error.\n"); + IMFTransform_Release(&object->IMFTransform_iface); + return E_OUTOFMEMORY; + } + + *ret = &object->IMFTransform_iface; + return S_OK; +} diff --git a/dlls/mfplat/buffer.c b/dlls/mfplat/buffer.c index eada3df18ad..9081dc39eab 100644 --- wine/dlls/mfplat/buffer.c +++ wine/dlls/mfplat/buffer.c @@ -18,8 +18,6 @@ #define COBJMACROS -#include - #include "mfplat_private.h" #include "rtworkq.h" @@ -28,6 +26,8 @@ #include "d3d9.h" #include "evr.h" +#include "wine/debug.h" + WINE_DEFAULT_DEBUG_CHANNEL(mfplat); #define ALIGN_SIZE(size, alignment) (((size) + (alignment)) & ~((alignment))) @@ -49,7 +49,7 @@ struct buffer struct { BYTE *linear_buffer; - DWORD plane_size; + unsigned int plane_size; BYTE *scanline0; unsigned int width; @@ -148,7 +148,7 @@ static ULONG WINAPI memory_buffer_AddRef(IMFMediaBuffer *iface) struct buffer *buffer = impl_from_IMFMediaBuffer(iface); ULONG refcount = InterlockedIncrement(&buffer->refcount); - TRACE("%p, refcount %lu.\n", buffer, refcount); + TRACE("%p, refcount %u.\n", buffer, refcount); return refcount; } @@ -158,7 +158,7 @@ static ULONG WINAPI memory_buffer_Release(IMFMediaBuffer *iface) struct buffer *buffer = impl_from_IMFMediaBuffer(iface); ULONG refcount = InterlockedDecrement(&buffer->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -173,7 +173,7 @@ static ULONG WINAPI memory_buffer_Release(IMFMediaBuffer *iface) } DeleteCriticalSection(&buffer->cs); free(buffer->_2d.linear_buffer); - _aligned_free(buffer->data); + free(buffer->data); free(buffer); } @@ -223,7 +223,7 @@ static HRESULT WINAPI memory_buffer_SetCurrentLength(IMFMediaBuffer *iface, DWOR { struct buffer *buffer = impl_from_IMFMediaBuffer(iface); - TRACE("%p, %lu.\n", iface, current_length); + TRACE("%p, %u.\n", iface, current_length); if (current_length > buffer->max_length) return E_INVALIDARG; @@ -309,12 +309,8 @@ static HRESULT WINAPI memory_1d_2d_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat hr = MF_E_INVALIDREQUEST; else if (!buffer->_2d.linear_buffer) { - if (!(buffer->_2d.linear_buffer = malloc(buffer->_2d.plane_size))) + if (!(buffer->_2d.linear_buffer = malloc(ALIGN_SIZE(buffer->_2d.plane_size, MF_64_BYTE_ALIGNMENT)))) hr = E_OUTOFMEMORY; - - if (SUCCEEDED(hr)) - copy_image(buffer, buffer->_2d.linear_buffer, buffer->_2d.width, buffer->data, buffer->_2d.pitch, - buffer->_2d.width, buffer->_2d.height); } if (SUCCEEDED(hr)) @@ -384,7 +380,7 @@ static HRESULT WINAPI d3d9_surface_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat { D3DLOCKED_RECT rect; - if (!(buffer->_2d.linear_buffer = malloc(buffer->_2d.plane_size))) + if (!(buffer->_2d.linear_buffer = malloc(ALIGN_SIZE(buffer->_2d.plane_size, MF_64_BYTE_ALIGNMENT)))) hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) @@ -449,7 +445,7 @@ static HRESULT WINAPI d3d9_surface_buffer_SetCurrentLength(IMFMediaBuffer *iface { struct buffer *buffer = impl_from_IMFMediaBuffer(iface); - TRACE("%p, %lu.\n", iface, current_length); + TRACE("%p, %u.\n", iface, current_length); buffer->current_length = current_length; @@ -601,14 +597,14 @@ static HRESULT WINAPI memory_2d_buffer_GetContiguousLength(IMF2DBuffer2 *iface, static HRESULT WINAPI memory_2d_buffer_ContiguousCopyTo(IMF2DBuffer2 *iface, BYTE *dest_buffer, DWORD dest_length) { - FIXME("%p, %p, %lu.\n", iface, dest_buffer, dest_length); + FIXME("%p, %p, %u.\n", iface, dest_buffer, dest_length); return E_NOTIMPL; } static HRESULT WINAPI memory_2d_buffer_ContiguousCopyFrom(IMF2DBuffer2 *iface, const BYTE *src_buffer, DWORD src_length) { - FIXME("%p, %p, %lu.\n", iface, src_buffer, src_length); + FIXME("%p, %p, %u.\n", iface, src_buffer, src_length); return E_NOTIMPL; } @@ -670,14 +666,13 @@ static HRESULT WINAPI d3d9_surface_buffer_Lock2D(IMF2DBuffer2 *iface, BYTE **sca if (buffer->_2d.linear_buffer) hr = MF_E_UNEXPECTED; - else if (!buffer->_2d.locks) + else if (!buffer->_2d.locks++) { hr = IDirect3DSurface9_LockRect(buffer->d3d9_surface.surface, &buffer->d3d9_surface.rect, NULL, 0); } if (SUCCEEDED(hr)) { - buffer->_2d.locks++; *scanline0 = buffer->d3d9_surface.rect.pBits; *pitch = buffer->d3d9_surface.rect.Pitch; } @@ -756,14 +751,13 @@ static HRESULT WINAPI d3d9_surface_buffer_Lock2DSize(IMF2DBuffer2 *iface, MF2DBu if (buffer->_2d.linear_buffer) hr = MF_E_UNEXPECTED; - else if (!buffer->_2d.locks) + else if (!buffer->_2d.locks++) { hr = IDirect3DSurface9_LockRect(buffer->d3d9_surface.surface, &buffer->d3d9_surface.rect, NULL, 0); } if (SUCCEEDED(hr)) { - buffer->_2d.locks++; *scanline0 = buffer->d3d9_surface.rect.pBits; *pitch = buffer->d3d9_surface.rect.Pitch; if (buffer_start) @@ -897,7 +891,7 @@ static HRESULT dxgi_surface_buffer_create_readback_texture(struct buffer *buffer texture_desc.MiscFlags = 0; texture_desc.MipLevels = 1; if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &texture_desc, NULL, &buffer->dxgi_surface.rb_texture))) - WARN("Failed to create readback texture, hr %#lx.\n", hr); + WARN("Failed to create readback texture, hr %#x.\n", hr); ID3D11Device_Release(device); @@ -922,7 +916,7 @@ static HRESULT dxgi_surface_buffer_map(struct buffer *buffer) if (FAILED(hr = ID3D11DeviceContext_Map(immediate_context, (ID3D11Resource *)buffer->dxgi_surface.rb_texture, 0, D3D11_MAP_READ_WRITE, 0, &buffer->dxgi_surface.map_desc))) { - WARN("Failed to map readback texture, hr %#lx.\n", hr); + WARN("Failed to map readback texture, hr %#x.\n", hr); } ID3D11DeviceContext_Release(immediate_context); @@ -965,7 +959,7 @@ static HRESULT WINAPI dxgi_surface_buffer_Lock(IMFMediaBuffer *iface, BYTE **dat hr = MF_E_INVALIDREQUEST; else if (!buffer->_2d.linear_buffer) { - if (!(buffer->_2d.linear_buffer = malloc(buffer->_2d.plane_size))) + if (!(buffer->_2d.linear_buffer = malloc(ALIGN_SIZE(buffer->_2d.plane_size, MF_64_BYTE_ALIGNMENT)))) hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) @@ -1024,7 +1018,7 @@ static HRESULT WINAPI dxgi_surface_buffer_SetCurrentLength(IMFMediaBuffer *iface { struct buffer *buffer = impl_from_IMFMediaBuffer(iface); - TRACE("%p, %lu.\n", iface, current_length); + TRACE("%p, %u.\n", iface, current_length); buffer->current_length = current_length; @@ -1262,24 +1256,8 @@ static const IMFDXGIBufferVtbl dxgi_buffer_vtbl = static HRESULT memory_buffer_init(struct buffer *buffer, DWORD max_length, DWORD alignment, const IMFMediaBufferVtbl *vtbl) { - if (alignment < MF_16_BYTE_ALIGNMENT) - alignment = MF_16_BYTE_ALIGNMENT; - alignment++; - - if (alignment & (alignment - 1)) - { - alignment--; - alignment |= alignment >> 1; - alignment |= alignment >> 2; - alignment |= alignment >> 4; - alignment |= alignment >> 8; - alignment |= alignment >> 16; - alignment++; - } - - if (!(buffer->data = _aligned_malloc(max_length, alignment))) + if (!(buffer->data = calloc(1, ALIGN_SIZE(max_length, alignment)))) return E_OUTOFMEMORY; - memset(buffer->data, 0, max_length); buffer->IMFMediaBuffer_iface.lpVtbl = vtbl; buffer->refcount = 1; @@ -1328,10 +1306,9 @@ static p_copy_image_func get_2d_buffer_copy_func(DWORD fourcc) static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bottom_up, IMFMediaBuffer **buffer) { - unsigned int stride, max_length; - unsigned int row_alignment; + unsigned int stride, max_length, plane_size; struct buffer *object; - DWORD plane_size; + unsigned int row_alignment; GUID subtype; BOOL is_yuv; HRESULT hr; @@ -1389,7 +1366,7 @@ static HRESULT create_2d_buffer(DWORD width, DWORD height, DWORD fourcc, BOOL bo max_length = pitch * height; } - if (FAILED(hr = memory_buffer_init(object, max_length, row_alignment, &memory_1d_2d_buffer_vtbl))) + if (FAILED(hr = memory_buffer_init(object, max_length, MF_1_BYTE_ALIGNMENT, &memory_1d_2d_buffer_vtbl))) { free(object); return hr; @@ -1462,7 +1439,7 @@ static HRESULT create_dxgi_surface_buffer(IUnknown *surface, unsigned int sub_re if (FAILED(hr = IUnknown_QueryInterface(surface, &IID_ID3D11Texture2D, (void **)&texture))) { - WARN("Failed to get texture interface, hr %#lx.\n", hr); + WARN("Failed to get texture interface, hr %#x.\n", hr); return hr; } @@ -1514,7 +1491,7 @@ static HRESULT create_dxgi_surface_buffer(IUnknown *surface, unsigned int sub_re */ HRESULT WINAPI MFCreateMemoryBuffer(DWORD max_length, IMFMediaBuffer **buffer) { - TRACE("%lu, %p.\n", max_length, buffer); + TRACE("%u, %p.\n", max_length, buffer); return create_1d_buffer(max_length, MF_1_BYTE_ALIGNMENT, buffer); } @@ -1524,7 +1501,7 @@ HRESULT WINAPI MFCreateMemoryBuffer(DWORD max_length, IMFMediaBuffer **buffer) */ HRESULT WINAPI MFCreateAlignedMemoryBuffer(DWORD max_length, DWORD alignment, IMFMediaBuffer **buffer) { - TRACE("%lu, %lu, %p.\n", max_length, alignment, buffer); + TRACE("%u, %u, %p.\n", max_length, alignment, buffer); return create_1d_buffer(max_length, alignment, buffer); } @@ -1534,7 +1511,7 @@ HRESULT WINAPI MFCreateAlignedMemoryBuffer(DWORD max_length, DWORD alignment, IM */ HRESULT WINAPI MFCreate2DMediaBuffer(DWORD width, DWORD height, DWORD fourcc, BOOL bottom_up, IMFMediaBuffer **buffer) { - TRACE("%lu, %lu, %s, %d, %p.\n", width, height, debugstr_fourcc(fourcc), bottom_up, buffer); + TRACE("%u, %u, %s, %d, %p.\n", width, height, debugstr_fourcc(fourcc), bottom_up, buffer); return create_2d_buffer(width, height, fourcc, bottom_up, buffer); } @@ -1582,7 +1559,7 @@ HRESULT WINAPI MFCreateMediaBufferFromMediaType(IMFMediaType *media_type, LONGLO HRESULT hr; GUID major; - TRACE("%p, %s, %lu, %lu, %p.\n", media_type, debugstr_time(duration), min_length, alignment, buffer); + TRACE("%p, %s, %u, %u, %p.\n", media_type, debugstr_time(duration), min_length, alignment, buffer); if (!media_type) return E_INVALIDARG; @@ -1596,8 +1573,6 @@ HRESULT WINAPI MFCreateMediaBufferFromMediaType(IMFMediaType *media_type, LONGLO if (FAILED(IMFMediaType_GetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_alignment))) WARN("Block alignment was not specified.\n"); - alignment = max(16, alignment); - if (block_alignment) { avg_length = 0; @@ -1612,6 +1587,8 @@ HRESULT WINAPI MFCreateMediaBufferFromMediaType(IMFMediaType *media_type, LONGLO } } + alignment = max(16, alignment); + length = buffer_get_aligned_length(avg_length + 1, alignment); length = buffer_get_aligned_length(length, block_alignment); } @@ -1620,7 +1597,7 @@ HRESULT WINAPI MFCreateMediaBufferFromMediaType(IMFMediaType *media_type, LONGLO length = max(length, min_length); - return create_1d_buffer(length, alignment - 1, buffer); + return create_1d_buffer(length, MF_1_BYTE_ALIGNMENT, buffer); } else FIXME("Major type %s is not supported.\n", debugstr_guid(&major)); diff --git a/dlls/mfplat/colorconvert.c b/dlls/mfplat/colorconvert.c new file mode 100644 index 00000000000..92322e877ec --- /dev/null +++ wine/dlls/mfplat/colorconvert.c @@ -0,0 +1,901 @@ +/* GStreamer Color Converter + * + * Copyright 2020 Derek Lesho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +static const GUID *raw_types[] = { + &MFVideoFormat_RGB24, + &MFVideoFormat_RGB32, + &MFVideoFormat_RGB555, + &MFVideoFormat_RGB8, + &MFVideoFormat_AYUV, + &MFVideoFormat_I420, + &MFVideoFormat_IYUV, + &MFVideoFormat_NV11, + &MFVideoFormat_NV12, + &MFVideoFormat_UYVY, + &MFVideoFormat_v216, + &MFVideoFormat_v410, + &MFVideoFormat_YUY2, + &MFVideoFormat_YVYU, + &MFVideoFormat_YVYU, +}; + +struct color_converter +{ + IMFTransform IMFTransform_iface; + LONG refcount; + IMFMediaType *input_type; + IMFMediaType *output_type; + CRITICAL_SECTION cs; + BOOL buffer_inflight; + LONGLONG buffer_pts, buffer_dur; + struct wg_parser *parser; + struct wg_parser_stream *stream; +}; + +static struct color_converter *impl_color_converter_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct color_converter, IMFTransform_iface); +} + +static HRESULT WINAPI color_converter_QueryInterface(IMFTransform *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IMFTransform) || + IsEqualGUID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFTransform_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI color_converter_AddRef(IMFTransform *iface) +{ + struct color_converter *transform = impl_color_converter_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&transform->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI color_converter_Release(IMFTransform *iface) +{ + struct color_converter *transform = impl_color_converter_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&transform->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + transform->cs.DebugInfo->Spare[0] = 0; + DeleteCriticalSection(&transform->cs); + if (transform->output_type) + IMFMediaType_Release(transform->output_type); + if (transform->stream) + wg_parser_disconnect(transform->parser); + if (transform->parser) + wg_parser_destroy(transform->parser); + free(transform); + } + + return refcount; +} + +static HRESULT WINAPI color_converter_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + + return S_OK; +} + +static HRESULT WINAPI color_converter_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + TRACE("%p, %p, %p.\n", iface, inputs, outputs); + + *inputs = *outputs = 1; + + return S_OK; +} + +static HRESULT WINAPI color_converter_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + UINT64 framesize; + GUID subtype; + + TRACE("%p %u %p.\n", iface, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + info->hnsMaxLatency = 0; + info->cbSize = 0; + + EnterCriticalSection(&converter->cs); + + if (converter->input_type) + { + if (SUCCEEDED(IMFMediaType_GetGUID(converter->input_type, &MF_MT_SUBTYPE, &subtype)) && + SUCCEEDED(IMFMediaType_GetUINT64(converter->input_type, &MF_MT_FRAME_SIZE, &framesize))) + { + MFCalculateImageSize(&subtype, framesize >> 32, (UINT32) framesize, &info->cbSize); + } + + if (!info->cbSize) + WARN("Failed to get desired input buffer size, the non-provided sample path will likely break\n"); + } + + LeaveCriticalSection(&converter->cs); + + return S_OK; +} + +static HRESULT WINAPI color_converter_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + UINT64 framesize; + GUID subtype; + + TRACE("%p %u %p.\n", iface, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + info->dwFlags = MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES | MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER; + info->cbAlignment = 0; + info->cbSize = 0; + + EnterCriticalSection(&converter->cs); + + if (converter->output_type) + { + if (SUCCEEDED(IMFMediaType_GetGUID(converter->output_type, &MF_MT_SUBTYPE, &subtype)) && + SUCCEEDED(IMFMediaType_GetUINT64(converter->output_type, &MF_MT_FRAME_SIZE, &framesize))) + { + MFCalculateImageSize(&subtype, framesize >> 32, (UINT32) framesize, &info->cbSize); + } + + if (!info->cbSize) + WARN("Failed to get desired output buffer size, the non-provided sample path will likely break\n"); + } + + LeaveCriticalSection(&converter->cs); + + return S_OK; +} + +static HRESULT WINAPI color_converter_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + FIXME("%p, %p.\n", iface, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p.\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p.\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + TRACE("%p, %u.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + TRACE("%p, %u, %p.\n", iface, streams, ids); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + IMFMediaType *ret; + HRESULT hr; + + TRACE("%p, %u, %u, %p.\n", iface, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= ARRAY_SIZE(raw_types)) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&ret))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_MAJOR_TYPE, &MFMediaType_Video))) + { + IMFMediaType_Release(ret); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_SUBTYPE, raw_types[index]))) + { + IMFMediaType_Release(ret); + return hr; + } + + *type = ret; + + return S_OK; +} + +static HRESULT WINAPI color_converter_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + IMFMediaType *ret; + HRESULT hr; + + TRACE("%p, %u, %u, %p.\n", iface, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= ARRAY_SIZE(raw_types)) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&ret))) + return hr; + + EnterCriticalSection(&converter->cs); + + if (converter->input_type) + IMFMediaType_CopyAllItems(converter->input_type, (IMFAttributes *) ret); + + LeaveCriticalSection(&converter->cs); + + if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_MAJOR_TYPE, &MFMediaType_Video))) + { + IMFMediaType_Release(ret); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(ret, &MF_MT_SUBTYPE, raw_types[index]))) + { + IMFMediaType_Release(ret); + return hr; + } + + *type = ret; + + return S_OK; +} + +static HRESULT WINAPI color_converter_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + UINT64 input_framesize, output_framesize; + GUID major_type, subtype; + struct wg_format format; + unsigned int i; + HRESULT hr; + + TRACE("%p, %u, %p, %#x.\n", iface, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!type) + { + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + if (converter->input_type) + { + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + IMFMediaType_Release(converter->input_type); + converter->input_type = NULL; + } + + LeaveCriticalSection(&converter->cs); + + return S_OK; + } + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDTYPE; + + if (!IsEqualGUID(&major_type, &MFMediaType_Video)) + return MF_E_INVALIDTYPE; + + for (i = 0; i < ARRAY_SIZE(raw_types); i++) + { + if (IsEqualGUID(&subtype, raw_types[i])) + break; + } + + if (i == ARRAY_SIZE(raw_types)) + return MF_E_INVALIDTYPE; + + EnterCriticalSection(&converter->cs); + + if(converter->output_type + && SUCCEEDED(IMFMediaType_GetUINT64(converter->output_type, &MF_MT_FRAME_SIZE, &output_framesize)) + && SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &input_framesize)) + && input_framesize != output_framesize) + { + LeaveCriticalSection(&converter->cs); + return MF_E_INVALIDTYPE; + } + + LeaveCriticalSection(&converter->cs); + + mf_media_type_to_wg_format(type, &format); + if (!format.major_type) + return MF_E_INVALIDTYPE; + + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + hr = S_OK; + + if (!converter->input_type) + hr = MFCreateMediaType(&converter->input_type); + + if (SUCCEEDED(hr)) + hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *) converter->input_type); + + if (FAILED(hr)) + { + IMFMediaType_Release(converter->input_type); + converter->input_type = NULL; + } + + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + + if (converter->input_type && converter->output_type) + { + struct wg_format output_format; + mf_media_type_to_wg_format(converter->output_type, &output_format); + + if (SUCCEEDED(hr = wg_parser_connect_unseekable(converter->parser, &format, 1, &output_format, NULL))) + converter->stream = wg_parser_get_stream(converter->parser, 0); + } + + LeaveCriticalSection(&converter->cs); + + return hr; +} + +static HRESULT WINAPI color_converter_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + UINT64 input_framesize, output_framesize; + GUID major_type, subtype; + struct wg_format format; + unsigned int i; + HRESULT hr; + + TRACE("%p, %u, %p, %#x.\n", iface, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!type) + { + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + if (converter->output_type) + { + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + IMFMediaType_Release(converter->output_type); + converter->output_type = NULL; + } + + LeaveCriticalSection(&converter->cs); + + return S_OK; + } + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDTYPE; + + if (!IsEqualGUID(&major_type, &MFMediaType_Video)) + return MF_E_INVALIDTYPE; + + for (i = 0; i < ARRAY_SIZE(raw_types); i++) + { + if (IsEqualGUID(&subtype, raw_types[i])) + break; + } + + if (i == ARRAY_SIZE(raw_types)) + return MF_E_INVALIDTYPE; + + EnterCriticalSection(&converter->cs); + + if(converter->input_type + && SUCCEEDED(IMFMediaType_GetUINT64(converter->input_type, &MF_MT_FRAME_SIZE, &input_framesize)) + && SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &output_framesize)) + && input_framesize != output_framesize) + { + LeaveCriticalSection(&converter->cs); + return MF_E_INVALIDTYPE; + } + + LeaveCriticalSection(&converter->cs); + + mf_media_type_to_wg_format(type, &format); + if (!format.major_type) + return MF_E_INVALIDTYPE; + + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&converter->cs); + + hr = S_OK; + + if (!converter->output_type) + hr = MFCreateMediaType(&converter->output_type); + + if (SUCCEEDED(hr)) + hr = IMFMediaType_CopyAllItems(type, (IMFAttributes *) converter->output_type); + + if (FAILED(hr)) + { + IMFMediaType_Release(converter->output_type); + converter->output_type = NULL; + } + + if (converter->stream) + { + wg_parser_disconnect(converter->parser); + converter->stream = NULL; + } + + if (converter->input_type && converter->output_type) + { + struct wg_format input_format; + mf_media_type_to_wg_format(converter->input_type, &input_format); + + if (SUCCEEDED(hr = wg_parser_connect_unseekable(converter->parser, &input_format, 1, &format, NULL))) + converter->stream = wg_parser_get_stream(converter->parser, 0); + } + + LeaveCriticalSection(&converter->cs); + + return hr; +} + +static HRESULT WINAPI color_converter_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p.\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + IMFMediaType *ret; + HRESULT hr; + + TRACE("%p, %u, %p.\n", converter, id, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (FAILED(hr = MFCreateMediaType(&ret))) + return hr; + + EnterCriticalSection(&converter->cs); + + if (converter->output_type) + hr = IMFMediaType_CopyAllItems(converter->output_type, (IMFAttributes *)ret); + else + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + + LeaveCriticalSection(&converter->cs); + + if (SUCCEEDED(hr)) + *type = ret; + else + IMFMediaType_Release(ret); + + return hr; +} + +static HRESULT WINAPI color_converter_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + FIXME("%p, %u, %p.\n", iface, id, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("%p, %p.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("%p, %s, %s.\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + TRACE("%p, %u, %p.\n", iface, id, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI color_converter_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + struct wg_parser_buffer wg_buffer; + + TRACE("%p, %u %lu.\n", iface, message, param); + + switch(message) + { + case MFT_MESSAGE_COMMAND_FLUSH: + { + EnterCriticalSection(&converter->cs); + if (!converter->buffer_inflight) + { + LeaveCriticalSection(&converter->cs); + return S_OK; + } + + wg_parser_stream_get_buffer(converter->stream, &wg_buffer); + wg_parser_stream_release_buffer(converter->stream); + converter->buffer_inflight = FALSE; + + LeaveCriticalSection(&converter->cs); + return S_OK; + } + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + return S_OK; + default: + FIXME("Unhandled message type %x.\n", message); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI color_converter_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + IMFMediaBuffer *buffer = NULL; + unsigned char *buffer_data; + DWORD buffer_size; + uint64_t offset; + uint32_t size; + HRESULT hr; + + TRACE("%p, %u, %p, %#x.\n", iface, id, sample, flags); + + if (flags) + WARN("Unsupported flags %#x.\n", flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&converter->cs); + + if (!converter->stream) + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + goto done; + } + + if (converter->buffer_inflight) + { + hr = MF_E_NOTACCEPTING; + goto done; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer))) + goto done; + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &buffer_data, NULL, &buffer_size))) + goto done; + + for (;;) + { + if (!wg_parser_get_next_read_offset(converter->parser, &offset, &size)) + { + TRACE("sink unconnected\n"); + continue; + } + + wg_parser_push_data(converter->parser, WG_READ_SUCCESS, buffer_data, min(buffer_size, size)); + + if (buffer_size <= size) + break; + + buffer_data += size; + buffer_size -= size; + } + + IMFMediaBuffer_Unlock(buffer); + converter->buffer_inflight = TRUE; + if (FAILED(IMFSample_GetSampleTime(sample, &converter->buffer_pts))) + converter->buffer_pts = -1; + if (FAILED(IMFSample_GetSampleDuration(sample, &converter->buffer_dur))) + converter->buffer_dur = -1; + +done: + if (buffer) + IMFMediaBuffer_Release(buffer); + LeaveCriticalSection(&converter->cs); + return hr; +} + +static HRESULT WINAPI color_converter_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct color_converter *converter = impl_color_converter_from_IMFTransform(iface); + IMFSample *allocated_sample = NULL; + struct wg_parser_buffer wg_buffer; + IMFMediaBuffer *buffer = NULL; + unsigned char *buffer_data; + DWORD buffer_len; + HRESULT hr = S_OK; + + TRACE("%p, %#x, %u, %p, %p.\n", iface, flags, count, samples, status); + + if (flags) + WARN("Unsupported flags %#x.\n", flags); + + if (!count) + return S_OK; + + if (count != 1) + return MF_E_INVALIDSTREAMNUMBER; + + if (samples[0].dwStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&converter->cs); + + if (!converter->stream) + { + hr = MF_E_TRANSFORM_TYPE_NOT_SET; + goto done; + } + + if (!converter->buffer_inflight) + { + hr = MF_E_TRANSFORM_NEED_MORE_INPUT; + goto done; + } + + if (!wg_parser_stream_get_buffer(converter->stream, &wg_buffer)) + assert(0); + + if (!samples[0].pSample) + { + if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer.size, &buffer))) + { + ERR("Failed to create buffer, hr %#x.\n", hr); + goto done; + } + + if (FAILED(hr = MFCreateSample(&allocated_sample))) + { + ERR("Failed to create sample, hr %#x.\n", hr); + goto done; + } + + samples[0].pSample = allocated_sample; + + if (FAILED(hr = IMFSample_AddBuffer(samples[0].pSample, buffer))) + { + ERR("Failed to add buffer, hr %#x.\n", hr); + goto done; + } + + IMFMediaBuffer_Release(buffer); + buffer = NULL; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &buffer))) + { + ERR("Failed to get buffer from sample, hr %#x.\n", hr); + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_GetMaxLength(buffer, &buffer_len))) + { + ERR("Failed to get buffer size, hr %#x.\n", hr); + goto done; + } + + if (buffer_len < wg_buffer.size) + { + WARN("Client's buffer is smaller (%u bytes) than the output sample (%u bytes)\n", + buffer_len, wg_buffer.size); + + hr = MF_E_BUFFERTOOSMALL; + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer.size))) + { + ERR("Failed to set size, hr %#x.\n", hr); + goto done; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &buffer_data, NULL, NULL))) + { + ERR("Failed to lock buffer hr %#x.\n", hr); + goto done; + } + + if (!wg_parser_stream_copy_buffer(converter->stream, buffer_data, 0, wg_buffer.size)) + { + ERR("Failed to copy buffer.\n"); + IMFMediaBuffer_Unlock(buffer); + hr = E_FAIL; + goto done; + } + + IMFMediaBuffer_Unlock(buffer); + + wg_parser_stream_release_buffer(converter->stream); + converter->buffer_inflight = FALSE; + + if (converter->buffer_pts != -1) + IMFSample_SetSampleTime(samples[0].pSample, converter->buffer_pts); + if (converter->buffer_dur != -1) + IMFSample_SetSampleDuration(samples[0].pSample, converter->buffer_dur); + + samples[0].dwStatus = 0; + samples[0].pEvents = NULL; + + done: + if (buffer) + IMFMediaBuffer_Release(buffer); + if (FAILED(hr) && allocated_sample) + { + IMFSample_Release(allocated_sample); + samples[0].pSample = NULL; + } + LeaveCriticalSection(&converter->cs); + return hr; +} + +static const IMFTransformVtbl color_converter_vtbl = +{ + color_converter_QueryInterface, + color_converter_AddRef, + color_converter_Release, + color_converter_GetStreamLimits, + color_converter_GetStreamCount, + color_converter_GetStreamIDs, + color_converter_GetInputStreamInfo, + color_converter_GetOutputStreamInfo, + color_converter_GetAttributes, + color_converter_GetInputStreamAttributes, + color_converter_GetOutputStreamAttributes, + color_converter_DeleteInputStream, + color_converter_AddInputStreams, + color_converter_GetInputAvailableType, + color_converter_GetOutputAvailableType, + color_converter_SetInputType, + color_converter_SetOutputType, + color_converter_GetInputCurrentType, + color_converter_GetOutputCurrentType, + color_converter_GetInputStatus, + color_converter_GetOutputStatus, + color_converter_SetOutputBounds, + color_converter_ProcessEvent, + color_converter_ProcessMessage, + color_converter_ProcessInput, + color_converter_ProcessOutput, +}; + +HRESULT color_converter_create(REFIID riid, void **ret) +{ + struct color_converter *object; + + TRACE("%s %p\n", debugstr_guid(riid), ret); + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFTransform_iface.lpVtbl = &color_converter_vtbl; + object->refcount = 1; + + InitializeCriticalSection(&object->cs); + object->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": color_converter_lock"); + + if (!(object->parser = wg_parser_create(WG_PARSER_VIDEOCONV, true))) + { + ERR("Failed to create video converter due to GStreamer error.\n"); + IMFTransform_Release(&object->IMFTransform_iface); + return E_OUTOFMEMORY; + } + + *ret = &object->IMFTransform_iface; + return S_OK; +} diff --git a/dlls/mfplat/decode_transform.c b/dlls/mfplat/decode_transform.c new file mode 100644 index 00000000000..fb7f432923f --- /dev/null +++ wine/dlls/mfplat/decode_transform.c @@ -0,0 +1,1218 @@ +/* GStreamer Decoder Transform + * + * Copyright 2021 Derek Lesho + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mftransform.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +const GUID *h264_input_types[] = {&MFVideoFormat_H264}; +/* NV12 comes first https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order . thanks to @vitorhnn */ +const GUID *h264_output_types[] = {&MFVideoFormat_NV12, &MFVideoFormat_I420, &MFVideoFormat_IYUV, &MFVideoFormat_YUY2, &MFVideoFormat_YV12}; + +const GUID *aac_input_types[] = {&MFAudioFormat_AAC}; +const GUID *aac_output_types[] = {&MFAudioFormat_Float}; + +static struct decoder_desc +{ + const GUID *major_type; + const GUID **input_types; + unsigned int input_types_count; + const GUID **output_types; + unsigned int output_types_count; +} decoder_descs[] = +{ + { /* DECODER_TYPE_H264 */ + &MFMediaType_Video, + h264_input_types, + ARRAY_SIZE(h264_input_types), + h264_output_types, + ARRAY_SIZE(h264_output_types), + }, + { /* DECODER_TYPE_AAC */ + &MFMediaType_Audio, + aac_input_types, + ARRAY_SIZE(aac_input_types), + aac_output_types, + ARRAY_SIZE(aac_output_types), + } +}; + +struct pipeline_event +{ + enum + { + PIPELINE_EVENT_NONE, + PIPELINE_EVENT_PARSER_STARTED, + PIPELINE_EVENT_READ_REQUEST, + } type; + union + { + struct + { + struct wg_parser_stream *stream; + } parser_started; + } u; +}; + +struct mf_decoder +{ + IMFTransform IMFTransform_iface; + LONG refcount; + enum decoder_type type; + IMFMediaType *input_type, *output_type; + CRITICAL_SECTION cs, help_cs, event_cs; + CONDITION_VARIABLE help_cv, event_cv; + BOOL flushing, draining, eos, helper_thread_shutdown, video; + HANDLE helper_thread, read_thread; + uint64_t offset_tracker; + struct wg_parser *wg_parser; + struct wg_parser_stream *wg_stream; + + struct + { + enum + { + HELP_REQ_NONE, + HELP_REQ_START_PARSER, + } type; + } help_request; + + struct pipeline_event event; +}; + +static struct mf_decoder *impl_mf_decoder_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct mf_decoder, IMFTransform_iface); +} + +static HRESULT WINAPI mf_decoder_QueryInterface (IMFTransform *iface, REFIID riid, void **out) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFTransform) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = iface; + IMFTransform_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *out = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI mf_decoder_AddRef(IMFTransform *iface) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&decoder->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI mf_decoder_Release(IMFTransform *iface) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&decoder->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (decoder->input_type) + { + IMFMediaType_Release(decoder->input_type); + decoder->input_type = NULL; + } + + if (decoder->output_type) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + if (decoder->wg_parser) + { + /* NULL wg_parser is possible if the wg_parser creation failed. */ + + if (decoder->wg_stream) + wg_parser_disconnect(decoder->wg_parser); + + EnterCriticalSection(&decoder->event_cs); + decoder->helper_thread_shutdown = TRUE; + WakeAllConditionVariable(&decoder->event_cv); + LeaveCriticalSection(&decoder->event_cs); + + EnterCriticalSection(&decoder->help_cs); + WakeAllConditionVariable(&decoder->help_cv); + LeaveCriticalSection(&decoder->help_cs); + + if (WaitForSingleObject(decoder->helper_thread, 10000) != WAIT_OBJECT_0) + FIXME("Failed waiting for helper thread to terminate.\n"); + CloseHandle(decoder->helper_thread); + if (WaitForSingleObject(decoder->read_thread, 10000) != WAIT_OBJECT_0) + FIXME("Failed waiting for read thread to terminate.\n"); + CloseHandle(decoder->read_thread); + + wg_parser_destroy(decoder->wg_parser); + } + + DeleteCriticalSection(&decoder->cs); + DeleteCriticalSection(&decoder->help_cs); + DeleteCriticalSection(&decoder->event_cs); + + heap_free(decoder); + } + + return refcount; +} + +static HRESULT WINAPI mf_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + TRACE("%p %p %p.\n", iface, inputs, outputs); + + *inputs = *outputs = 1; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + TRACE("%p %u %p %u %p.\n", iface, input_size, inputs, output_size, outputs); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + + TRACE("%p %u %p\n", decoder, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_DOES_NOT_ADDREF; + info->cbAlignment = 0; + info->cbSize = 0; + /* TODO: retrieve following fields from gstreamer */ + info->hnsMaxLatency = 0; + info->cbMaxLookahead = 0; + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + MFT_OUTPUT_STREAM_INFO stream_info = {}; + GUID output_subtype; + UINT64 framesize; + + TRACE("%p %u %p\n", decoder, id, info); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&decoder->cs); + + if (!decoder->output_type) + { + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + if (decoder->video) + { + stream_info.dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | + MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES; + stream_info.cbSize = 0; + if (SUCCEEDED(IMFMediaType_GetGUID(decoder->output_type, &MF_MT_SUBTYPE, &output_subtype)) && + SUCCEEDED(IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_SIZE, &framesize))) + { + MFCalculateImageSize(&output_subtype, framesize >> 32, (UINT32) framesize, &stream_info.cbSize); + } + if (!stream_info.cbSize) + ERR("Failed to get desired output buffer size\n"); + } + else + { + stream_info.dwFlags = MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE | MFT_OUTPUT_STREAM_CAN_PROVIDE_SAMPLES; + stream_info.cbSize = 4; + } + stream_info.cbAlignment = 0; + + LeaveCriticalSection(&decoder->cs); + + *info = stream_info; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + FIXME("%p, %p. semi-stub!\n", iface, attributes); + + return MFCreateAttributes(attributes, 0); +} + +static HRESULT WINAPI mf_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p.\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("%p, %u, %p.\n", iface, id, attributes); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + TRACE("%p, %u.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + TRACE("%p, %u, %p.\n", iface, streams, ids); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + IMFMediaType *input_type; + HRESULT hr; + + TRACE("%p, %u, %u, %p\n", decoder, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= decoder_descs[decoder->type].input_types_count) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&input_type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_MAJOR_TYPE, decoder_descs[decoder->type].major_type))) + { + IMFMediaType_Release(input_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(input_type, &MF_MT_SUBTYPE, decoder_descs[decoder->type].input_types[index]))) + { + IMFMediaType_Release(input_type); + return hr; + } + + *type = input_type; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + IMFMediaType *output_type; + HRESULT hr; + + TRACE("%p, %u, %u, %p\n", decoder, id, index, type); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (index >= decoder_descs[decoder->type].output_types_count) + return MF_E_NO_MORE_TYPES; + + if (FAILED(hr = MFCreateMediaType(&output_type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_MAJOR_TYPE, decoder_descs[decoder->type].major_type))) + { + IMFMediaType_Release(output_type); + return hr; + } + + if (FAILED(hr = IMFMediaType_SetGUID(output_type, &MF_MT_SUBTYPE, decoder_descs[decoder->type].output_types[index]))) + { + IMFMediaType_Release(output_type); + return hr; + } + + *type = output_type; + + return S_OK; +} + +static HRESULT WINAPI mf_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + struct wg_format input_format; + GUID major_type, subtype; + unsigned int i; + HRESULT hr; + + TRACE("%p, %u, %p, %#x.\n", decoder, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!type) + { + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&decoder->cs); + + if (decoder->wg_stream) + { + decoder->wg_stream = NULL; + wg_parser_disconnect(decoder->wg_parser); + } + + if (decoder->input_type) + { + IMFMediaType_Release(decoder->input_type); + decoder->input_type = NULL; + } + + LeaveCriticalSection(&decoder->cs); + + return S_OK; + } + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDTYPE; + + if (!(IsEqualGUID(&major_type, decoder_descs[decoder->type].major_type))) + return MF_E_INVALIDTYPE; + + for (i = 0; i < decoder_descs[decoder->type].input_types_count; i++) + { + if (IsEqualGUID(&subtype, decoder_descs[decoder->type].input_types[i])) + break; + if (i == decoder_descs[decoder->type].input_types_count) + return MF_E_INVALIDTYPE; + } + + mf_media_type_to_wg_format(type, &input_format); + if (!input_format.major_type) + return MF_E_INVALIDTYPE; + + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&decoder->cs); + + hr = S_OK; + + if (decoder->wg_stream) + { + decoder->wg_stream = NULL; + wg_parser_disconnect(decoder->wg_parser); + } + + if (!decoder->input_type) + hr = MFCreateMediaType(&decoder->input_type); + + if (SUCCEEDED(hr) && FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) decoder->input_type))) + { + IMFMediaType_Release(decoder->input_type); + decoder->input_type = NULL; + } + + if (decoder->input_type && decoder->output_type) + { + EnterCriticalSection(&decoder->help_cs); + while(decoder->help_request.type != HELP_REQ_NONE) + SleepConditionVariableCS(&decoder->help_cv, &decoder->help_cs, INFINITE); + decoder->help_request.type = HELP_REQ_START_PARSER; + LeaveCriticalSection(&decoder->help_cs); + WakeAllConditionVariable(&decoder->help_cv); + } + + LeaveCriticalSection(&decoder->cs); + return hr; +} + +static HRESULT WINAPI mf_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + struct wg_format output_format; + GUID major_type, subtype; + HRESULT hr; + unsigned int i; + + TRACE("%p, %u, %p, %#x.\n", decoder, id, type, flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (!type) + { + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&decoder->cs); + + if (decoder->wg_stream) + { + decoder->wg_stream = NULL; + wg_parser_disconnect(decoder->wg_parser); + } + + if (decoder->output_type) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + LeaveCriticalSection(&decoder->cs); + + return S_OK; + } + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major_type))) + return MF_E_INVALIDTYPE; + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return MF_E_INVALIDTYPE; + + if (!(IsEqualGUID(&major_type, decoder_descs[decoder->type].major_type))) + return MF_E_INVALIDTYPE; + + for (i = 0; i < decoder_descs[decoder->type].output_types_count; i++) + { + if (IsEqualGUID(&subtype, decoder_descs[decoder->type].output_types[i])) + break; + if (i == decoder_descs[decoder->type].output_types_count) + return MF_E_INVALIDTYPE; + } + + mf_media_type_to_wg_format(type, &output_format); + if (!output_format.major_type) + return MF_E_INVALIDTYPE; + + if (flags & MFT_SET_TYPE_TEST_ONLY) + return S_OK; + + EnterCriticalSection(&decoder->cs); + + hr = S_OK; + + if (decoder->wg_stream) + { + decoder->wg_stream = NULL; + wg_parser_disconnect(decoder->wg_parser); + } + + if (!decoder->output_type) + hr = MFCreateMediaType(&decoder->output_type); + + if (SUCCEEDED(hr) && FAILED(hr = IMFMediaType_CopyAllItems(type, (IMFAttributes*) decoder->output_type))) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + if (decoder->input_type && decoder->output_type) + { + EnterCriticalSection(&decoder->help_cs); + while(decoder->help_request.type != HELP_REQ_NONE) + SleepConditionVariableCS(&decoder->help_cv, &decoder->help_cs, INFINITE); + decoder->help_request.type = HELP_REQ_START_PARSER; + LeaveCriticalSection(&decoder->help_cs); + WakeAllConditionVariable(&decoder->help_cv); + } + + LeaveCriticalSection(&decoder->cs); + return hr; +} + +static HRESULT WINAPI mf_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p.\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p.\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + FIXME("%p, %u, %p\n", iface, id, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("%p, %p.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("%p, %s, %s.\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI mf_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + FIXME("%p, %u, %p.\n", iface, id, event); + + return E_NOTIMPL; +} + +static DWORD CALLBACK helper_thread_func(PVOID ctx) +{ + struct mf_decoder *decoder = (struct mf_decoder *)ctx; + + for(;;) + { + EnterCriticalSection(&decoder->help_cs); + + while(!decoder->helper_thread_shutdown && decoder->help_request.type == HELP_REQ_NONE) + SleepConditionVariableCS(&decoder->help_cv, &decoder->help_cs, INFINITE); + if (decoder->helper_thread_shutdown) + { + LeaveCriticalSection(&decoder->help_cs); + return 0; + } + + switch(decoder->help_request.type) + { + case HELP_REQ_START_PARSER: + { + struct wg_format input_format, output_format; + struct wg_rect wg_aperture = {0}; + MFVideoArea *aperture = NULL; + UINT32 aperture_size; + + decoder->help_request.type = HELP_REQ_NONE; + LeaveCriticalSection(&decoder->help_cs); + + mf_media_type_to_wg_format(decoder->input_type, &input_format); + mf_media_type_to_wg_format(decoder->output_type, &output_format); + + if (SUCCEEDED(IMFMediaType_GetAllocatedBlob(decoder->output_type, + &MF_MT_MINIMUM_DISPLAY_APERTURE, (UINT8 **) &aperture, &aperture_size))) + { + TRACE("Decoded media's aperture: x: %u %u/65536, y: %u %u/65536, area: %u x %u\n", + aperture->OffsetX.value, aperture->OffsetX.fract, + aperture->OffsetY.value, aperture->OffsetY.fract, aperture->Area.cx, aperture->Area.cy); + + /* TODO: verify aperture params? */ + + wg_aperture.left = aperture->OffsetX.value; + wg_aperture.top = aperture->OffsetY.value; + wg_aperture.right = aperture->Area.cx; + wg_aperture.bottom = aperture->Area.cy; + + CoTaskMemFree(aperture); + } + + wg_parser_connect_unseekable(decoder->wg_parser, + &input_format, 1, &output_format, aperture ? &wg_aperture : NULL); + + EnterCriticalSection(&decoder->event_cs); + while (!decoder->helper_thread_shutdown && decoder->event.type != PIPELINE_EVENT_NONE) + SleepConditionVariableCS(&decoder->event_cv, &decoder->event_cs, INFINITE); + + if (decoder->helper_thread_shutdown) + { + LeaveCriticalSection(&decoder->event_cs); + return 0; + } + + decoder->event.type = PIPELINE_EVENT_PARSER_STARTED; + decoder->event.u.parser_started.stream = wg_parser_get_stream(decoder->wg_parser, 0); + + LeaveCriticalSection(&decoder->event_cs); + WakeAllConditionVariable(&decoder->event_cv); + + break; + } + default: + assert(0); + } + } +} + +/* We use a separate thread to wait for reads, as we may want to wait to WAIT_ANY + on a read and another event. */ +static DWORD CALLBACK read_thread_func(PVOID ctx) +{ + struct mf_decoder *decoder = (struct mf_decoder *)ctx; + uint64_t offset; + uint32_t size; + + for (;;) + { + if (decoder->helper_thread_shutdown) + break; + + if (!wg_parser_get_next_read_offset(decoder->wg_parser, &offset, &size)) + continue; + + EnterCriticalSection(&decoder->event_cs); + while (!decoder->helper_thread_shutdown && decoder->event.type != PIPELINE_EVENT_NONE) + SleepConditionVariableCS(&decoder->event_cv, &decoder->event_cs, INFINITE); + + if (decoder->helper_thread_shutdown) + { + LeaveCriticalSection(&decoder->event_cs); + break; + } + + decoder->event.type = PIPELINE_EVENT_READ_REQUEST; + WakeAllConditionVariable(&decoder->event_cv); + while (!decoder->helper_thread_shutdown && decoder->event.type == PIPELINE_EVENT_READ_REQUEST) + SleepConditionVariableCS(&decoder->event_cv, &decoder->event_cs, INFINITE); + LeaveCriticalSection(&decoder->event_cs); + } + + return 0; +} + +static struct pipeline_event get_pipeline_event(struct mf_decoder *decoder) +{ + struct pipeline_event ret; + + EnterCriticalSection(&decoder->event_cs); + while(decoder->event.type == PIPELINE_EVENT_NONE) + SleepConditionVariableCS(&decoder->event_cv, &decoder->event_cs, INFINITE); + + ret = decoder->event; + + if (ret.type != PIPELINE_EVENT_READ_REQUEST) + { + decoder->event.type = PIPELINE_EVENT_NONE; + WakeAllConditionVariable(&decoder->event_cv); + } + + LeaveCriticalSection(&decoder->event_cs); + + return ret; +} + +static HRESULT WINAPI mf_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + HRESULT hr; + + TRACE("%p, %x %lu.\n", decoder, message, param); + + EnterCriticalSection(&decoder->cs); + if (!decoder->input_type || !decoder->output_type) + { + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + hr = S_OK; + + switch (message) + { + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + case MFT_MESSAGE_NOTIFY_START_OF_STREAM: + break; + case MFT_MESSAGE_NOTIFY_END_OF_STREAM: + { + if (param) + { + hr = MF_E_INVALIDSTREAMNUMBER; + break; + } + if (!decoder->wg_stream) + { + ERR("End-Of-Stream marked on a decoder MFT which hasn't finished initialization\n"); + hr = E_FAIL; + break; + } + + decoder->eos = TRUE; + break; + } + case MFT_MESSAGE_COMMAND_DRAIN: + { + struct pipeline_event pip_event; + + if (!decoder->wg_stream) + { + ERR("Drain requested on a decoder MFT which hasn't finished initialization\n"); + hr = E_FAIL; + break; + } + + pip_event = get_pipeline_event(decoder); + assert(pip_event.type == PIPELINE_EVENT_READ_REQUEST); + + wg_parser_push_data(decoder->wg_parser, WG_READ_EOS, NULL, 0); + + EnterCriticalSection(&decoder->event_cs); + decoder->event.type = PIPELINE_EVENT_NONE; + LeaveCriticalSection(&decoder->event_cs); + WakeAllConditionVariable(&decoder->event_cv); + + decoder->draining = TRUE; + decoder->offset_tracker = 0; + break; + } + case MFT_MESSAGE_COMMAND_FLUSH: + { + struct pipeline_event pip_event; + + if (!decoder->wg_stream) + { + ERR("Flush requested on a decoder MFT which hasn't finished initialization\n"); + hr = E_FAIL; + break; + } + + pip_event = get_pipeline_event(decoder); + assert(pip_event.type == PIPELINE_EVENT_READ_REQUEST); + + wg_parser_push_data(decoder->wg_parser, WG_READ_FLUSHING, NULL, 0); + + EnterCriticalSection(&decoder->event_cs); + decoder->event.type = PIPELINE_EVENT_NONE; + LeaveCriticalSection(&decoder->event_cs); + WakeAllConditionVariable(&decoder->event_cv); + + decoder->offset_tracker = 0; + break; + } + default: + { + ERR("Unhandled message type %x.\n", message); + hr = E_FAIL; + break; + } + } + + LeaveCriticalSection(&decoder->cs); + return hr; +} + +static HRESULT WINAPI mf_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + struct pipeline_event pip_event; + IMFMediaBuffer *buffer = NULL; + HRESULT hr = S_OK; + BYTE *buffer_data; + DWORD buffer_size; + uint32_t size = 0; + uint64_t offset; + + TRACE("%p, %u, %p, %#x.\n", decoder, id, sample, flags); + + if (flags) + WARN("Unsupported flags %#x\n", flags); + + if (id != 0) + return MF_E_INVALIDSTREAMNUMBER; + + EnterCriticalSection(&decoder->cs); + + if (!decoder->input_type || !decoder->output_type) + { + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + if (decoder->draining) + { + LeaveCriticalSection(&decoder->cs); + return MF_E_NOTACCEPTING; + } + + if (!decoder->wg_stream) + { + pip_event = get_pipeline_event(decoder); + + switch (pip_event.type) + { + case PIPELINE_EVENT_PARSER_STARTED: + decoder->wg_stream = pip_event.u.parser_started.stream; + break; + case PIPELINE_EVENT_READ_REQUEST: + break; + default: + assert(0); + } + } + + if (decoder->wg_stream && !wg_parser_stream_drain(decoder->wg_stream)) + { + LeaveCriticalSection(&decoder->cs); + return MF_E_NOTACCEPTING; + } + + /* At this point, we either have a pre-init read request, or drained pipeline */ + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &buffer))) + goto done; + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &buffer_data, NULL, &buffer_size))) + goto done; + + pip_event = get_pipeline_event(decoder); + assert(pip_event.type == PIPELINE_EVENT_READ_REQUEST); + + for(;;) + { + uint32_t copy_size; + + if (!wg_parser_get_next_read_offset(decoder->wg_parser, &offset, &size)) + continue; + + copy_size = min(size, buffer_size); + + if (offset != decoder->offset_tracker) + { + ERR("A seek is needed, MFTs don't support this!\n"); + wg_parser_push_data(decoder->wg_parser, WG_READ_FAILURE, NULL, 0); + IMFMediaBuffer_Unlock(buffer); + hr = E_FAIL; + goto done; + } + + wg_parser_push_data(decoder->wg_parser, WG_READ_SUCCESS, buffer_data, buffer_size); + + decoder->offset_tracker += copy_size; + + if (buffer_size <= size) + break; + + buffer_data += copy_size; + buffer_size -= copy_size; + + WARN("Input sample split into multiple read requests\n"); + } + + EnterCriticalSection(&decoder->event_cs); + decoder->event.type = PIPELINE_EVENT_NONE; + LeaveCriticalSection(&decoder->event_cs); + WakeAllConditionVariable(&decoder->event_cv); + + IMFMediaBuffer_Unlock(buffer); + + done: + if (buffer) + IMFMediaBuffer_Release(buffer); + LeaveCriticalSection(&decoder->cs); + return hr; +} + +static HRESULT WINAPI mf_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct mf_decoder *decoder = impl_mf_decoder_from_IMFTransform(iface); + MFT_OUTPUT_DATA_BUFFER *relevant_buffer = NULL; + struct wg_parser_buffer wg_buffer; + struct pipeline_event pip_event; + IMFMediaBuffer *buffer; + DWORD buffer_len; + unsigned int i; + BYTE *data; + HRESULT hr; + + TRACE("%p, %#x, %u, %p, %p.\n", iface, flags, count, samples, status); + + if (flags) + WARN("Unsupported flags %#x\n", flags); + + for (i = 0; i < count; i++) + { + MFT_OUTPUT_DATA_BUFFER *out_buffer = &samples[i]; + + if (out_buffer->dwStreamID != 0) + return MF_E_INVALIDSTREAMNUMBER; + + if (relevant_buffer) + return MF_E_INVALIDSTREAMNUMBER; + + relevant_buffer = out_buffer; + } + + if (!relevant_buffer) + return S_OK; + + EnterCriticalSection(&decoder->cs); + + if (!decoder->input_type || !decoder->output_type) + { + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_TYPE_NOT_SET; + } + + if (!decoder->wg_stream) + { + pip_event = get_pipeline_event(decoder); + + switch (pip_event.type) + { + case PIPELINE_EVENT_PARSER_STARTED: + decoder->wg_stream = pip_event.u.parser_started.stream; + break; + case PIPELINE_EVENT_READ_REQUEST: + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_NEED_MORE_INPUT; + default: + assert(0); + } + } + + if (wg_parser_stream_drain(decoder->wg_stream)) + { + /* this would be unexpected, as we should get the EOS-event when a drain command completes. */ + assert (!decoder->draining); + + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + if (!wg_parser_stream_get_buffer(decoder->wg_stream, &wg_buffer)) + { + if (!decoder->draining) + { + LeaveCriticalSection(&decoder->cs); + WARN("Received EOS event while not draining\n"); + return E_FAIL; + } + decoder->draining = FALSE; + LeaveCriticalSection(&decoder->cs); + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + if (relevant_buffer->pSample) + { + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(relevant_buffer->pSample, &buffer))) + { + ERR("Failed to get buffer from sample, hr %#x.\n", hr); + LeaveCriticalSection(&decoder->cs); + return hr; + } + } + else + { + if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer.size, &buffer))) + { + ERR("Failed to create buffer, hr %#x.\n", hr); + LeaveCriticalSection(&decoder->cs); + return hr; + } + + if (FAILED(hr = MFCreateSample(&relevant_buffer->pSample))) + { + ERR("Failed to create sample, hr %#x.\n", hr); + LeaveCriticalSection(&decoder->cs); + IMFMediaBuffer_Release(buffer); + return hr; + } + + if (FAILED(hr = IMFSample_AddBuffer(relevant_buffer->pSample, buffer))) + { + ERR("Failed to add buffer, hr %#x.\n", hr); + goto out; + } + } + + if (FAILED(hr = IMFMediaBuffer_GetMaxLength(buffer, &buffer_len))) + { + ERR("Failed to get buffer size, hr %#x.\n", hr); + goto out; + } + + if (buffer_len < wg_buffer.size) + { + WARN("Client's buffer is smaller (%u bytes) than the output sample (%u bytes)\n", + buffer_len, wg_buffer.size); + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, buffer_len))) + { + ERR("Failed to set size, hr %#x.\n", hr); + goto out; + } + } + else if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer.size))) + { + ERR("Failed to set size, hr %#x.\n", hr); + goto out; + } + + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) + { + ERR("Failed to lock buffer, hr %#x.\n", hr); + goto out; + } + + if (!wg_parser_stream_copy_buffer(decoder->wg_stream, data, 0, min(buffer_len, wg_buffer.size))) + { + hr = E_FAIL; + goto out; + } + + if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) + { + ERR("Failed to unlock buffer, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = IMFSample_SetSampleTime(relevant_buffer->pSample, wg_buffer.pts))) + { + ERR("Failed to set sample time, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = IMFSample_SetSampleDuration(relevant_buffer->pSample, wg_buffer.duration))) + { + ERR("Failed to set sample duration, hr %#x.\n", hr); + goto out; + } + + relevant_buffer->dwStatus = 0; + relevant_buffer->pEvents = NULL; + *status = 0; + + out: + if (SUCCEEDED(hr)) + wg_parser_stream_release_buffer(decoder->wg_stream); + LeaveCriticalSection(&decoder->cs); + + if (FAILED(hr)) + { + IMFSample_Release(relevant_buffer->pSample); + relevant_buffer->pSample = NULL; + } + + IMFMediaBuffer_Release(buffer); + + return hr; +} + +static const IMFTransformVtbl mf_decoder_vtbl = +{ + mf_decoder_QueryInterface, + mf_decoder_AddRef, + mf_decoder_Release, + mf_decoder_GetStreamLimits, + mf_decoder_GetStreamCount, + mf_decoder_GetStreamIDs, + mf_decoder_GetInputStreamInfo, + mf_decoder_GetOutputStreamInfo, + mf_decoder_GetAttributes, + mf_decoder_GetInputStreamAttributes, + mf_decoder_GetOutputStreamAttributes, + mf_decoder_DeleteInputStream, + mf_decoder_AddInputStreams, + mf_decoder_GetInputAvailableType, + mf_decoder_GetOutputAvailableType, + mf_decoder_SetInputType, + mf_decoder_SetOutputType, + mf_decoder_GetInputCurrentType, + mf_decoder_GetOutputCurrentType, + mf_decoder_GetInputStatus, + mf_decoder_GetOutputStatus, + mf_decoder_SetOutputBounds, + mf_decoder_ProcessEvent, + mf_decoder_ProcessMessage, + mf_decoder_ProcessInput, + mf_decoder_ProcessOutput, +}; + +HRESULT decode_transform_create(REFIID riid, void **obj, enum decoder_type type) +{ + struct mf_decoder *object; + struct wg_parser *parser; + + TRACE("%s, %p %u.\n", debugstr_guid(riid), obj, type); + + if (!(object = heap_alloc_zero(sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFTransform_iface.lpVtbl = &mf_decoder_vtbl; + object->refcount = 1; + + object->type = type; + object->video = decoder_descs[type].major_type == &MFMediaType_Video; + + InitializeCriticalSection(&object->cs); + InitializeCriticalSection(&object->help_cs); + InitializeCriticalSection(&object->event_cs); + InitializeConditionVariable(&object->help_cv); + InitializeConditionVariable(&object->event_cv); + + if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, TRUE))) + { + ERR("Failed to create Decoder MFT type %u: Unspecified GStreamer error\n", type); + IMFTransform_Release(&object->IMFTransform_iface); + return E_OUTOFMEMORY; + } + object->wg_parser = parser; + + object->helper_thread = CreateThread(NULL, 0, helper_thread_func, object, 0, NULL); + object->read_thread = CreateThread(NULL, 0, read_thread_func, object, 0, NULL); + + *obj = &object->IMFTransform_iface; + return S_OK; +} diff --git a/dlls/mfplat/gst_guids.h b/dlls/mfplat/gst_guids.h new file mode 100644 index 00000000000..ea859586d7f --- /dev/null +++ wine/dlls/mfplat/gst_guids.h @@ -0,0 +1,23 @@ +/* + * GStreamer Guids + * + * Copyright 2010 Maarten Lankhorst for CodeWeavers + * Copyright 2010 Aric Stewart for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +DEFINE_GUID(CLSID_decodebin_parser, 0xf9d8d64e, 0xa144, 0x47dc, 0x8e, 0xe0, 0xf5, 0x34, 0x98, 0x37, 0x2c, 0x29); +DEFINE_GUID(WINESUBTYPE_Gstreamer, 0xffffffff, 0x128f, 0x4dd1, 0xad, 0x22, 0xbe, 0xcf, 0xa6, 0x6c, 0xe7, 0xaa); diff --git a/dlls/mfplat/gst_private.h b/dlls/mfplat/gst_private.h new file mode 100644 index 00000000000..c6b256b4fdd --- /dev/null +++ wine/dlls/mfplat/gst_private.h @@ -0,0 +1,217 @@ +/* + * GStreamer splitter + decoder, adapted from parser.c + * + * Copyright 2010 Maarten Lankhorst for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#ifndef __GST_PRIVATE_INCLUDED__ +#define __GST_PRIVATE_INCLUDED__ + +#include +#include +#include +#include +#include +#include + +#define COBJMACROS +#define NONAMELESSSTRUCT +#define NONAMELESSUNION +#include "dshow.h" +#include "mfidl.h" +#include "wmsdk.h" +#include "wine/debug.h" +#include "wine/strmbase.h" + +#include "unixlib.h" + +bool array_reserve(void **elements, size_t *capacity, size_t count, size_t size) DECLSPEC_HIDDEN; + +static inline const char *debugstr_time(REFERENCE_TIME time) +{ + ULONGLONG abstime = time >= 0 ? time : -time; + unsigned int i = 0, j = 0; + char buffer[23], rev[23]; + + while (abstime || i <= 8) + { + buffer[i++] = '0' + (abstime % 10); + abstime /= 10; + if (i == 7) buffer[i++] = '.'; + } + if (time < 0) buffer[i++] = '-'; + + while (i--) rev[j++] = buffer[i]; + while (rev[j-1] == '0' && rev[j-2] != '.') --j; + rev[j] = 0; + + return wine_dbg_sprintf("%s", rev); +} + +#define MEDIATIME_FROM_BYTES(x) ((LONGLONG)(x) * 10000000) + +struct wg_parser *wg_parser_create(enum wg_parser_type type, bool unlimited_buffering) DECLSPEC_HIDDEN; +void wg_parser_destroy(struct wg_parser *parser) DECLSPEC_HIDDEN; + +HRESULT wg_parser_connect(struct wg_parser *parser, uint64_t file_size) DECLSPEC_HIDDEN; +HRESULT wg_parser_connect_unseekable(struct wg_parser *parser, const struct wg_format *in_format, + uint32_t stream_count, const struct wg_format *out_formats, const struct wg_rect *apertures) DECLSPEC_HIDDEN; +void wg_parser_disconnect(struct wg_parser *parser) DECLSPEC_HIDDEN; + +bool wg_parser_get_next_read_offset(struct wg_parser *parser, uint64_t *offset, uint32_t *size) DECLSPEC_HIDDEN; +void wg_parser_push_data(struct wg_parser *parser, enum wg_read_result result, const void *data, uint32_t size) DECLSPEC_HIDDEN; + +uint32_t wg_parser_get_stream_count(struct wg_parser *parser) DECLSPEC_HIDDEN; +struct wg_parser_stream *wg_parser_get_stream(struct wg_parser *parser, uint32_t index) DECLSPEC_HIDDEN; + +void wg_parser_stream_get_preferred_format(struct wg_parser_stream *stream, struct wg_format *format) DECLSPEC_HIDDEN; +void wg_parser_stream_enable(struct wg_parser_stream *stream, const struct wg_format *format, const struct wg_rect *aperture, uint32_t flags) DECLSPEC_HIDDEN; +void wg_parser_stream_disable(struct wg_parser_stream *stream) DECLSPEC_HIDDEN; + +bool wg_parser_stream_get_buffer(struct wg_parser_stream *stream, struct wg_parser_buffer *buffer) DECLSPEC_HIDDEN; +bool wg_parser_stream_copy_buffer(struct wg_parser_stream *stream, + void *data, uint32_t offset, uint32_t size) DECLSPEC_HIDDEN; +void wg_parser_stream_release_buffer(struct wg_parser_stream *stream) DECLSPEC_HIDDEN; +void wg_parser_stream_notify_qos(struct wg_parser_stream *stream, + bool underflow, double proportion, int64_t diff, uint64_t timestamp) DECLSPEC_HIDDEN; + +/* Returns the duration in 100-nanosecond units. */ +uint64_t wg_parser_stream_get_duration(struct wg_parser_stream *stream) DECLSPEC_HIDDEN; +bool wg_parser_stream_get_language(struct wg_parser_stream *stream, char *buffer, uint32_t size) DECLSPEC_HIDDEN; +/* start_pos and stop_pos are in 100-nanosecond units. */ +void wg_parser_stream_seek(struct wg_parser_stream *stream, double rate, + uint64_t start_pos, uint64_t stop_pos, DWORD start_flags, DWORD stop_flags) DECLSPEC_HIDDEN; +bool wg_parser_stream_drain(struct wg_parser_stream *stream) DECLSPEC_HIDDEN; + +struct wg_transform *wg_transform_create(const struct wg_encoded_format *input_format, + const struct wg_format *output_format) DECLSPEC_HIDDEN; +void wg_transform_destroy(struct wg_transform *transform) DECLSPEC_HIDDEN; +HRESULT wg_transform_push_data(struct wg_transform *transform, const void *data, uint32_t size) DECLSPEC_HIDDEN; +HRESULT wg_transform_read_data(struct wg_transform *transform, struct wg_sample *sample) DECLSPEC_HIDDEN; + +unsigned int wg_format_get_max_size(const struct wg_format *format); + +HRESULT avi_splitter_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; +HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; +HRESULT mpeg_splitter_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; +HRESULT wave_parser_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; +HRESULT wma_decoder_create(IUnknown *outer, IUnknown **out) DECLSPEC_HIDDEN; + +bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm); +bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format); + +BOOL init_gstreamer(void) DECLSPEC_HIDDEN; + +extern HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; +extern HRESULT mfplat_DllRegisterServer(void) DECLSPEC_HIDDEN; + +IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) DECLSPEC_HIDDEN; +void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) DECLSPEC_HIDDEN; +void mf_media_type_to_wg_encoded_format(IMFMediaType *type, struct wg_encoded_format *format) DECLSPEC_HIDDEN; + +HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) DECLSPEC_HIDDEN; + +HRESULT aac_decoder_create(REFIID riid, void **ret) DECLSPEC_HIDDEN; +HRESULT h264_decoder_create(REFIID riid, void **ret) DECLSPEC_HIDDEN; +HRESULT audio_converter_create(REFIID riid, void **ret) DECLSPEC_HIDDEN; +HRESULT color_converter_create(REFIID riid, void **ret) DECLSPEC_HIDDEN; + +enum decoder_type +{ + DECODER_TYPE_H264, + DECODER_TYPE_AAC, +}; +HRESULT decode_transform_create(REFIID riid, void **obj, enum decoder_type) DECLSPEC_HIDDEN; + +struct wm_stream +{ + struct wm_reader *reader; + struct wg_parser_stream *wg_stream; + struct wg_format format; + WMT_STREAM_SELECTION selection; + WORD index; + bool eos; + bool allocate_output; + bool allocate_stream; + /* Note that we only pretend to read compressed samples, and instead output + * uncompressed samples regardless of whether we are configured to read + * compressed samples. Rather, the behaviour of the reader objects differs + * in nontrivial ways depending on this field. */ + bool read_compressed; +}; + +struct wm_reader +{ + IWMHeaderInfo3 IWMHeaderInfo3_iface; + IWMLanguageList IWMLanguageList_iface; + IWMPacketSize2 IWMPacketSize2_iface; + IWMProfile3 IWMProfile3_iface; + IWMReaderPlaylistBurn IWMReaderPlaylistBurn_iface; + IWMReaderTimecode IWMReaderTimecode_iface; + LONG refcount; + CRITICAL_SECTION cs; + + QWORD start_time; + + IStream *source_stream; + HANDLE file; + HANDLE read_thread; + bool read_thread_shutdown; + struct wg_parser *wg_parser; + + struct wm_stream *streams; + WORD stream_count; + + IWMReaderCallbackAdvanced *callback_advanced; + + const struct wm_reader_ops *ops; +}; + +struct wm_reader_ops +{ + void *(*query_interface)(struct wm_reader *reader, REFIID iid); + void (*destroy)(struct wm_reader *reader); +}; + +void wm_reader_cleanup(struct wm_reader *reader); +HRESULT wm_reader_close(struct wm_reader *reader); +HRESULT wm_reader_get_max_stream_size(struct wm_reader *reader, WORD stream_number, DWORD *size); +HRESULT wm_reader_get_output_format(struct wm_reader *reader, DWORD output, + DWORD index, IWMOutputMediaProps **props); +HRESULT wm_reader_get_output_format_count(struct wm_reader *reader, DWORD output, DWORD *count); +HRESULT wm_reader_get_output_props(struct wm_reader *reader, DWORD output, + IWMOutputMediaProps **props); +struct wm_stream *wm_reader_get_stream_by_stream_number(struct wm_reader *reader, + WORD stream_number); +HRESULT wm_reader_get_stream_sample(struct wm_reader *reader, WORD stream_number, + INSSBuffer **ret_sample, QWORD *pts, QWORD *duration, DWORD *flags, WORD *ret_stream_number); +HRESULT wm_reader_get_stream_selection(struct wm_reader *reader, + WORD stream_number, WMT_STREAM_SELECTION *selection); +void wm_reader_init(struct wm_reader *reader, const struct wm_reader_ops *ops); +HRESULT wm_reader_open_file(struct wm_reader *reader, const WCHAR *filename); +HRESULT wm_reader_open_stream(struct wm_reader *reader, IStream *stream); +void wm_reader_seek(struct wm_reader *reader, QWORD start, LONGLONG duration); +HRESULT wm_reader_set_allocate_for_output(struct wm_reader *reader, DWORD output, BOOL allocate); +HRESULT wm_reader_set_allocate_for_stream(struct wm_reader *reader, WORD stream_number, BOOL allocate); +HRESULT wm_reader_set_output_props(struct wm_reader *reader, DWORD output, + IWMOutputMediaProps *props); +HRESULT wm_reader_set_read_compressed(struct wm_reader *reader, + WORD stream_number, BOOL compressed); +HRESULT wm_reader_set_streams_selected(struct wm_reader *reader, WORD count, + const WORD *stream_numbers, const WMT_STREAM_SELECTION *selections); + +#endif /* __GST_PRIVATE_INCLUDED__ */ diff --git a/dlls/mfplat/h264_decoder.c b/dlls/mfplat/h264_decoder.c new file mode 100644 index 00000000000..f6a4d47188f --- /dev/null +++ wine/dlls/mfplat/h264_decoder.c @@ -0,0 +1,727 @@ +/* H264 Decoder Transform + * + * Copyright 2022 RĂ©mi Bernon for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" +#include "mfobjects.h" +#include "mftransform.h" +#include "wmcodecdsp.h" + +#include "wine/debug.h" +#include "wine/heap.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +static const GUID *h264_decoder_input_types[] = +{ + &MFVideoFormat_H264, +}; +static const GUID *h264_decoder_output_types[] = +{ + &MFVideoFormat_NV12, + &MFVideoFormat_YV12, + &MFVideoFormat_IYUV, + &MFVideoFormat_I420, + &MFVideoFormat_YUY2, +}; + +struct h264_decoder +{ + IMFTransform IMFTransform_iface; + LONG refcount; + IMFMediaType *input_type; + IMFMediaType *output_type; + + struct wg_transform *wg_transform; + struct wg_format wg_format; + ULONGLONG last_pts; +}; + +static struct h264_decoder *impl_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct h264_decoder, IMFTransform_iface); +} + +static HRESULT try_create_wg_transform(struct h264_decoder *decoder) +{ + struct wg_encoded_format input_format; + struct wg_format output_format; + + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); + + mf_media_type_to_wg_encoded_format(decoder->input_type, &input_format); + if (input_format.encoded_type == WG_ENCODED_TYPE_UNKNOWN) + return MF_E_INVALIDMEDIATYPE; + + mf_media_type_to_wg_format(decoder->output_type, &output_format); + if (output_format.major_type == WG_MAJOR_TYPE_UNKNOWN) + return MF_E_INVALIDMEDIATYPE; + + decoder->last_pts = 0; + decoder->wg_transform = wg_transform_create(&input_format, &output_format); + if (decoder->wg_transform) + return S_OK; + + WARN("Failed to create H264 wg_transform.\n"); + return E_FAIL; +} + +static HRESULT fill_output_media_type(IMFMediaType *media_type, IMFMediaType *default_type) +{ + UINT32 value, width, height; + MFVideoArea aperture = {0}; + UINT64 value64; + GUID subtype; + HRESULT hr; + + if (FAILED(hr = IMFMediaType_GetGUID(media_type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (FAILED(hr = IMFMediaType_GetUINT64(media_type, &MF_MT_FRAME_SIZE, &value64))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT64(default_type, &MF_MT_FRAME_SIZE, &value64))) + value64 = (UINT64)1920 << 32 | 1080; + if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, value64))) + return hr; + } + width = value64 >> 32; + height = value64; + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_FRAME_RATE, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT64(default_type, &MF_MT_FRAME_RATE, &value64))) + value64 = (UINT64)30000 << 32 | 1001; + if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_RATE, value64))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_PIXEL_ASPECT_RATIO, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT64(default_type, &MF_MT_PIXEL_ASPECT_RATIO, &value64))) + value64 = (UINT64)1 << 32 | 1; + if (FAILED(hr = IMFMediaType_SetUINT64(media_type, &MF_MT_PIXEL_ASPECT_RATIO, value64))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_SAMPLE_SIZE, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_SAMPLE_SIZE, &value))) + { + if (IsEqualGUID(&subtype, &MFVideoFormat_YUY2)) + value = width * height * 2; + else + value = width * height * 3 / 2; + } + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_SAMPLE_SIZE, value))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_DEFAULT_STRIDE, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_DEFAULT_STRIDE, &value))) + { + if (IsEqualGUID(&subtype, &MFVideoFormat_YUY2)) + value = width * 2; + else + value = width; + } + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_DEFAULT_STRIDE, value))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_INTERLACE_MODE, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_INTERLACE_MODE, &value))) + value = MFVideoInterlace_MixedInterlaceOrProgressive; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_INTERLACE_MODE, value))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, &value))) + value = 1; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_ALL_SAMPLES_INDEPENDENT, value))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_VIDEO_ROTATION, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_VIDEO_ROTATION, &value))) + value = 0; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_VIDEO_ROTATION, value))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_FIXED_SIZE_SAMPLES, NULL))) + { + if (!default_type || FAILED(hr = IMFMediaType_GetUINT32(default_type, &MF_MT_FIXED_SIZE_SAMPLES, &value))) + value = 1; + if (FAILED(hr = IMFMediaType_SetUINT32(media_type, &MF_MT_FIXED_SIZE_SAMPLES, value))) + return hr; + } + + if (FAILED(hr = IMFMediaType_GetItem(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, NULL))) + { + if (default_type && SUCCEEDED(hr = IMFMediaType_GetBlob(default_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + (BYTE *)&aperture, sizeof(aperture), NULL))) + { + if (FAILED(hr = IMFMediaType_SetBlob(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + (BYTE *)&aperture, sizeof(aperture)))) + return hr; + } + } + + return S_OK; +} + +static HRESULT WINAPI h264_decoder_QueryInterface(IMFTransform *iface, REFIID iid, void **out) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out); + + if (IsEqualGUID(iid, &IID_IUnknown) || IsEqualGUID(iid, &IID_IMFTransform)) + *out = &decoder->IMFTransform_iface; + else + { + *out = NULL; + WARN("%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid(iid)); + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown *)*out); + return S_OK; +} + +static ULONG WINAPI h264_decoder_AddRef(IMFTransform *iface) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&decoder->refcount); + + TRACE("iface %p increasing refcount to %u.\n", decoder, refcount); + + return refcount; +} + +static ULONG WINAPI h264_decoder_Release(IMFTransform *iface) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&decoder->refcount); + + TRACE("iface %p decreasing refcount to %u.\n", decoder, refcount); + + if (!refcount) + { + if (decoder->wg_transform) + wg_transform_destroy(decoder->wg_transform); + if (decoder->input_type) + IMFMediaType_Release(decoder->input_type); + if (decoder->output_type) + IMFMediaType_Release(decoder->output_type); + free(decoder); + } + + return refcount; +} + +static HRESULT WINAPI h264_decoder_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + FIXME("iface %p, input_minimum %p, input_maximum %p, output_minimum %p, output_maximum %p stub!\n", + iface, input_minimum, input_maximum, output_minimum, output_maximum); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + FIXME("iface %p, inputs %p, outputs %p stub!\n", iface, inputs, outputs); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + FIXME("iface %p, input_size %u, inputs %p, output_size %u, outputs %p stub!\n", + iface, input_size, inputs, output_size, outputs); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + + TRACE("iface %p, id %u, info %p.\n", iface, id, info); + + if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + info->hnsMaxLatency = 0; + info->dwFlags = MFT_INPUT_STREAM_WHOLE_SAMPLES | MFT_INPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_INPUT_STREAM_FIXED_SAMPLE_SIZE; + info->cbSize = 0x1000; + info->cbMaxLookahead = 0; + info->cbAlignment = 0; + + return S_OK; +} + +static HRESULT WINAPI h264_decoder_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + IMFMediaType *media_type; + HRESULT hr; + + TRACE("iface %p, id %u, info %p.\n", iface, id, info); + + if (!decoder->input_type || !decoder->output_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + media_type = decoder->output_type; + + info->dwFlags = MFT_OUTPUT_STREAM_WHOLE_SAMPLES | MFT_OUTPUT_STREAM_SINGLE_SAMPLE_PER_BUFFER | MFT_OUTPUT_STREAM_FIXED_SAMPLE_SIZE; + if (FAILED(hr = IMFMediaType_GetUINT32(media_type, &MF_MT_SAMPLE_SIZE, &info->cbSize))) + info->cbSize = 1920 * 1080 * 2; + info->cbAlignment = 0; + + return S_OK; +} + +static HRESULT WINAPI h264_decoder_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + FIXME("iface %p, attributes %p stub!\n", iface, attributes); + + return MFCreateAttributes(attributes, 0); +} + +static HRESULT WINAPI h264_decoder_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("iface %p, id %u, attributes %p stub!\n", iface, id, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + FIXME("iface %p, id %u, attributes %p stub!\n", iface, id, attributes); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + FIXME("iface %p, id %u stub!\n", iface, id); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + FIXME("iface %p, streams %u, ids %p stub!\n", iface, streams, ids); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + IMFMediaType *media_type; + const GUID *subtype; + HRESULT hr; + + TRACE("iface %p, id %u, index %u, type %p.\n", iface, id, index, type); + + *type = NULL; + + if (index >= ARRAY_SIZE(h264_decoder_input_types)) + return MF_E_NO_MORE_TYPES; + subtype = h264_decoder_input_types[index]; + + if (FAILED(hr = MFCreateMediaType(&media_type))) + return hr; + + if (SUCCEEDED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video)) && + SUCCEEDED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, subtype))) + IMFMediaType_AddRef((*type = media_type)); + + IMFMediaType_Release(media_type); + return hr; +} + +static HRESULT WINAPI h264_decoder_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + IMFMediaType *media_type; + const GUID *output_type; + HRESULT hr; + + TRACE("iface %p, id %u, index %u, type %p.\n", iface, id, index, type); + + if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + *type = NULL; + + if (index >= ARRAY_SIZE(h264_decoder_output_types)) + return MF_E_NO_MORE_TYPES; + output_type = h264_decoder_output_types[index]; + + if (FAILED(hr = MFCreateMediaType(&media_type))) + return hr; + + if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, output_type))) + goto done; + + hr = fill_output_media_type(media_type, decoder->output_type); + +done: + if (SUCCEEDED(hr)) + IMFMediaType_AddRef((*type = media_type)); + + IMFMediaType_Release(media_type); + return hr; +} + +static HRESULT WINAPI h264_decoder_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + GUID major, subtype; + HRESULT hr; + ULONG i; + + TRACE("iface %p, id %u, type %p, flags %#x.\n", iface, id, type, flags); + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) || + FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return E_INVALIDARG; + + if (!IsEqualGUID(&major, &MFMediaType_Video)) + return MF_E_INVALIDMEDIATYPE; + + for (i = 0; i < ARRAY_SIZE(h264_decoder_input_types); ++i) + if (IsEqualGUID(&subtype, h264_decoder_input_types[i])) + break; + if (i == ARRAY_SIZE(h264_decoder_input_types)) + return MF_E_INVALIDMEDIATYPE; + + if (decoder->output_type) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + if (decoder->input_type) + IMFMediaType_Release(decoder->input_type); + IMFMediaType_AddRef((decoder->input_type = type)); + + return S_OK; +} + +static HRESULT WINAPI h264_decoder_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + GUID major, subtype; + BOOL identical; + HRESULT hr; + ULONG i; + + TRACE("iface %p, id %u, type %p, flags %#x.\n", iface, id, type, flags); + + if (!decoder->input_type) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_MAJOR_TYPE, &major)) || + FAILED(hr = IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + return hr; + + if (!IsEqualGUID(&major, &MFMediaType_Video)) + return MF_E_INVALIDMEDIATYPE; + + for (i = 0; i < ARRAY_SIZE(h264_decoder_output_types); ++i) + if (IsEqualGUID(&subtype, h264_decoder_output_types[i])) + break; + if (i == ARRAY_SIZE(h264_decoder_output_types)) + return MF_E_INVALIDMEDIATYPE; + + if (decoder->output_type) + { + if (SUCCEEDED(hr = IMFMediaType_Compare(decoder->output_type, (IMFAttributes *)type, + MF_ATTRIBUTES_MATCH_THEIR_ITEMS, &identical)) && identical) + return S_OK; + IMFMediaType_Release(decoder->output_type); + } + + IMFMediaType_AddRef((decoder->output_type = type)); + + if (FAILED(hr = try_create_wg_transform(decoder))) + { + IMFMediaType_Release(decoder->output_type); + decoder->output_type = NULL; + } + + return hr; +} + +static HRESULT WINAPI h264_decoder_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("iface %p, id %u, type %p stub!\n", iface, id, type); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("iface %p, id %u, type %p stub!\n", iface, id, type); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + FIXME("iface %p, id %u, flags %p stub!\n", iface, id, flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("iface %p, flags %p stub!\n", iface, flags); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("iface %p, lower %s, upper %s stub!\n", iface, + wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + FIXME("iface %p, id %u, event %p stub!\n", iface, id, event); + return E_NOTIMPL; +} + +static HRESULT WINAPI h264_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + + switch (message) + { + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + memset(&decoder->wg_format, 0, sizeof(decoder->wg_format)); + break; + default: + break; + } + + return S_OK; +} + +static HRESULT WINAPI h264_decoder_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + IMFMediaBuffer *media_buffer; + MFT_INPUT_STREAM_INFO info; + UINT32 buffer_size; + BYTE *buffer; + HRESULT hr; + + TRACE("iface %p, id %u, sample %p, flags %#x.\n", iface, id, sample, flags); + + if (FAILED(hr = IMFTransform_GetInputStreamInfo(iface, 0, &info))) + return hr; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(sample, &media_buffer))) + return hr; + + if (FAILED(hr = IMFMediaBuffer_Lock(media_buffer, &buffer, NULL, &buffer_size))) + goto done; + + hr = wg_transform_push_data(decoder->wg_transform, buffer, buffer_size); + + IMFMediaBuffer_Unlock(media_buffer); + +done: + IMFMediaBuffer_Release(media_buffer); + return hr; +} + +static HRESULT WINAPI h264_decoder_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + struct wg_sample wg_sample = {0}; + IMFMediaBuffer *media_buffer; + MFT_OUTPUT_STREAM_INFO info; + MFVideoArea aperture = {0}; + IMFMediaType *media_type; + UINT32 align, offset; + UINT64 framerate; + HRESULT hr; + + TRACE("iface %p, flags %#x, count %u, samples %p, status %p.\n", iface, flags, count, samples, status); + + if (count > 1) + { + FIXME("Not implemented count %u\n", count); + return E_NOTIMPL; + } + + if (FAILED(hr = IMFTransform_GetOutputStreamInfo(iface, 0, &info))) + return hr; + + if (!decoder->wg_transform) + return MF_E_TRANSFORM_TYPE_NOT_SET; + + *status = 0; + samples[0].dwStatus = 0; + if (!samples[0].pSample) + { + samples[0].dwStatus = MFT_OUTPUT_DATA_BUFFER_NO_SAMPLE; + return MF_E_TRANSFORM_NEED_MORE_INPUT; + } + + if (FAILED(hr = IMFSample_ConvertToContiguousBuffer(samples[0].pSample, &media_buffer))) + return hr; + + if (FAILED(hr = IMFMediaBuffer_Lock(media_buffer, &wg_sample.data, &wg_sample.size, NULL))) + goto done; + + wg_sample.format = &decoder->wg_format; + if (wg_sample.size < info.cbSize) + hr = MF_E_BUFFERTOOSMALL; + else if (SUCCEEDED(hr = wg_transform_read_data(decoder->wg_transform, &wg_sample))) + { + if (!(wg_sample.flags & (WG_SAMPLE_FLAG_HAS_PTS|WG_SAMPLE_FLAG_HAS_DURATION))) + { + IMFMediaType_GetUINT64(decoder->output_type, &MF_MT_FRAME_RATE, &framerate); + wg_sample.pts = decoder->last_pts; + wg_sample.duration = (UINT64)10000000 * (UINT32)framerate / (framerate >> 32); + wg_sample.flags |= (WG_SAMPLE_FLAG_HAS_PTS|WG_SAMPLE_FLAG_HAS_DURATION); + decoder->last_pts += wg_sample.duration; + } + + if (wg_sample.flags & WG_SAMPLE_FLAG_HAS_PTS) + IMFSample_SetSampleTime(samples[0].pSample, wg_sample.pts); + if (wg_sample.flags & WG_SAMPLE_FLAG_HAS_DURATION) + IMFSample_SetSampleDuration(samples[0].pSample, wg_sample.duration); + + if (decoder->wg_format.u.video.format == WG_VIDEO_FORMAT_NV12 && + (align = decoder->wg_format.u.video.height & 15)) + { + offset = decoder->wg_format.u.video.width * decoder->wg_format.u.video.height; + align = (16 - align) * decoder->wg_format.u.video.width; + memmove(wg_sample.data + offset + align, wg_sample.data + offset, + wg_sample.size - offset); + wg_sample.size += align; + } + + hr = IMFMediaBuffer_SetCurrentLength(media_buffer, wg_sample.size); + } + else if (hr == MF_E_TRANSFORM_STREAM_CHANGE) + { + media_type = mf_media_type_from_wg_format(&decoder->wg_format); + IMFMediaType_SetUINT32(media_type, &MF_MT_SAMPLE_SIZE, wg_sample.size); + IMFMediaType_DeleteItem(media_type, &MF_MT_FRAME_RATE); + IMFMediaType_DeleteItem(decoder->output_type, &MF_MT_DEFAULT_STRIDE); + fill_output_media_type(media_type, decoder->output_type); + + if (decoder->wg_format.u.video.format == WG_VIDEO_FORMAT_NV12 && + (align = decoder->wg_format.u.video.height & 15)) + { + aperture.Area.cx = decoder->wg_format.u.video.width; + aperture.Area.cy = decoder->wg_format.u.video.height; + IMFMediaType_SetBlob(media_type, &MF_MT_MINIMUM_DISPLAY_APERTURE, + (BYTE *)&aperture, sizeof(aperture)); + + aperture.Area.cy += 16 - align; + IMFMediaType_SetUINT64(media_type, &MF_MT_FRAME_SIZE, + (UINT64)aperture.Area.cx << 32 | aperture.Area.cy); + IMFMediaType_SetUINT32(media_type, &MF_MT_SAMPLE_SIZE, + aperture.Area.cx * aperture.Area.cy * 3 / 2); + } + + IMFMediaType_Release(decoder->output_type); + decoder->output_type = media_type; + + samples[0].dwStatus |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; + *status |= MFT_OUTPUT_DATA_BUFFER_FORMAT_CHANGE; + } + + IMFMediaBuffer_Unlock(media_buffer); + +done: + if (FAILED(hr)) + IMFMediaBuffer_SetCurrentLength(media_buffer, 0); + IMFMediaBuffer_Release(media_buffer); + return hr; +} + +static const IMFTransformVtbl h264_decoder_vtbl = +{ + h264_decoder_QueryInterface, + h264_decoder_AddRef, + h264_decoder_Release, + h264_decoder_GetStreamLimits, + h264_decoder_GetStreamCount, + h264_decoder_GetStreamIDs, + h264_decoder_GetInputStreamInfo, + h264_decoder_GetOutputStreamInfo, + h264_decoder_GetAttributes, + h264_decoder_GetInputStreamAttributes, + h264_decoder_GetOutputStreamAttributes, + h264_decoder_DeleteInputStream, + h264_decoder_AddInputStreams, + h264_decoder_GetInputAvailableType, + h264_decoder_GetOutputAvailableType, + h264_decoder_SetInputType, + h264_decoder_SetOutputType, + h264_decoder_GetInputCurrentType, + h264_decoder_GetOutputCurrentType, + h264_decoder_GetInputStatus, + h264_decoder_GetOutputStatus, + h264_decoder_SetOutputBounds, + h264_decoder_ProcessEvent, + h264_decoder_ProcessMessage, + h264_decoder_ProcessInput, + h264_decoder_ProcessOutput, +}; + +HRESULT h264_decoder_create(REFIID riid, void **ret) +{ + struct h264_decoder *decoder; + + TRACE("riid %s, ret %p.\n", debugstr_guid(riid), ret); + + if (!(decoder = calloc(1, sizeof(*decoder)))) + return E_OUTOFMEMORY; + + decoder->IMFTransform_iface.lpVtbl = &h264_decoder_vtbl; + decoder->refcount = 1; + + *ret = &decoder->IMFTransform_iface; + TRACE("Created decoder %p\n", *ret); + return S_OK; +} diff --git a/dlls/mfplat/main.c b/dlls/mfplat/main.c index cee052defeb..83fc8549e24 100644 --- wine/dlls/mfplat/main.c +++ wine/dlls/mfplat/main.c @@ -36,6 +36,7 @@ #include "d3d11.h" #include "uuids.h" +#include "wine/debug.h" #include "wine/list.h" #include "mfplat_private.h" @@ -52,9 +53,6 @@ #include "initguid.h" #include "mfd3d12.h" -#include "bcrypt.h" -#include "pathcch.h" - WINE_DEFAULT_DEBUG_CHANNEL(mfplat); struct local_handler @@ -124,20 +122,17 @@ struct system_time_source MFCLOCK_STATE state; IMFClock *clock; LONGLONG start_offset; - LONGLONG system_time; - LONGLONG clock_time; float rate; int i_rate; CRITICAL_SECTION cs; }; -static void system_time_source_update_clock_time(struct system_time_source *source, LONGLONG system_time) +static void system_time_source_apply_rate(const struct system_time_source *source, LONGLONG *value) { - LONGLONG diff = system_time - source->system_time; - if (source->i_rate) diff *= source->i_rate; - else if (source->rate != 1.0f) diff *= source->rate; - source->clock_time += diff; - source->system_time = system_time; + if (source->i_rate) + *value *= source->i_rate; + else + *value *= source->rate; } static struct system_time_source *impl_from_IMFPresentationTimeSource(IMFPresentationTimeSource *iface) @@ -183,7 +178,7 @@ static ULONG WINAPI transform_activate_AddRef(IMFActivate *iface) struct transform_activate *activate = impl_from_IMFActivate(iface); ULONG refcount = InterlockedIncrement(&activate->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -193,7 +188,7 @@ static ULONG WINAPI transform_activate_Release(IMFActivate *iface) struct transform_activate *activate = impl_from_IMFActivate(iface); ULONG refcount = InterlockedDecrement(&activate->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -707,10 +702,9 @@ static HRESULT register_transform(const CLSID *clsid, const WCHAR *name, UINT32 HRESULT hr = S_OK; HKEY hclsid = 0; WCHAR buffer[64]; + DWORD size, ret; WCHAR str[250]; UINT8 *blob; - UINT32 size; - DWORD ret; guid_to_string(buffer, clsid); swprintf(str, ARRAY_SIZE(str), L"%s\\%s", transform_keyW, buffer); @@ -1196,7 +1190,7 @@ static HRESULT mft_enum(GUID category, UINT32 flags, const MFT_REGISTER_TYPE_INF if (FAILED(hr = MFGetPluginControl(&plugin_control))) { - WARN("Failed to get plugin control instance, hr %#lx.\n", hr); + WARN("Failed to get plugin control instance, hr %#x.\n", hr); return hr; } @@ -1519,7 +1513,7 @@ static HRESULT mft_get_attributes(HKEY hkey, IMFAttributes **ret) if (!RegQueryValueExW(hkey, L"Attributes", NULL, NULL, blob, &size)) { if (FAILED(hr = MFInitAttributesFromBlob(attributes, blob, size))) - WARN("Failed to initialize attributes, hr %#lx.\n", hr); + WARN("Failed to initialize attributes, hr %#x.\n", hr); } free(blob); @@ -1583,6 +1577,18 @@ HRESULT WINAPI MFTGetInfo(CLSID clsid, WCHAR **name, MFT_REGISTER_TYPE_INFO **in return hr; } +static BOOL CALLBACK register_winegstreamer_proc(INIT_ONCE *once, void *param, void **ctx) +{ + HMODULE mod = LoadLibraryW(L"winegstreamer.dll"); + if (mod) + { + HRESULT (WINAPI *proc)(void) = (void *)GetProcAddress(mod, "DllRegisterServer"); + proc(); + FreeLibrary(mod); + } + return TRUE; +} + /*********************************************************************** * MFStartup (mfplat.@) */ @@ -1590,8 +1596,11 @@ HRESULT WINAPI MFStartup(ULONG version, DWORD flags) { #define MF_VERSION_XP MAKELONG( MF_API_VERSION, 1 ) #define MF_VERSION_WIN7 MAKELONG( MF_API_VERSION, 2 ) + static INIT_ONCE once = INIT_ONCE_STATIC_INIT; + + TRACE("%#x, %#x.\n", version, flags); - TRACE("%#lx, %#lx.\n", version, flags); + InitOnceExecuteOnce(&once, register_winegstreamer_proc, NULL, NULL); if (version != MF_VERSION_XP && version != MF_VERSION_WIN7) return MF_E_BAD_STARTUP_VERSION; @@ -1618,7 +1627,7 @@ HRESULT WINAPI MFShutdown(void) */ HRESULT WINAPI MFCopyImage(BYTE *dest, LONG deststride, const BYTE *src, LONG srcstride, DWORD width, DWORD lines) { - TRACE("%p, %ld, %p, %ld, %lu, %lu.\n", dest, deststride, src, srcstride, width, lines); + TRACE("%p, %d, %p, %d, %u, %u.\n", dest, deststride, src, srcstride, width, lines); while (lines--) { @@ -2312,7 +2321,7 @@ static const char *debugstr_eventid(DWORD event) }; struct event_id *ret = bsearch(&event, event_ids, ARRAY_SIZE(event_ids), sizeof(*event_ids), debug_event_id); - return ret ? wine_dbg_sprintf("%s", ret->name) : wine_dbg_sprintf("%lu", event); + return ret ? wine_dbg_sprintf("%s", ret->name) : wine_dbg_sprintf("%u", event); } static inline struct attributes *impl_from_IMFAttributes(IMFAttributes *iface) @@ -2342,7 +2351,7 @@ static ULONG WINAPI mfattributes_AddRef(IMFAttributes *iface) struct attributes *attributes = impl_from_IMFAttributes(iface); ULONG refcount = InterlockedIncrement(&attributes->ref); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); return refcount; } @@ -2352,7 +2361,7 @@ static ULONG WINAPI mfattributes_Release(IMFAttributes *iface) struct attributes *attributes = impl_from_IMFAttributes(iface); ULONG refcount = InterlockedDecrement(&attributes->ref); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); if (!refcount) { @@ -2670,7 +2679,8 @@ HRESULT attributes_GetAllocatedString(struct attributes *attributes, REFGUID key if (SUCCEEDED(hr)) { *value = attrval.pwszVal; - *length = lstrlenW(*value); + if (length) + *length = lstrlenW(*value); } return hr; @@ -3730,7 +3740,7 @@ static ULONG WINAPI async_stream_op_AddRef(IUnknown *iface) struct async_stream_op *op = impl_async_stream_op_from_IUnknown(iface); ULONG refcount = InterlockedIncrement(&op->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); return refcount; } @@ -3740,7 +3750,7 @@ static ULONG WINAPI async_stream_op_Release(IUnknown *iface) struct async_stream_op *op = impl_async_stream_op_from_IUnknown(iface); ULONG refcount = InterlockedDecrement(&op->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); if (!refcount) { @@ -3902,7 +3912,7 @@ static ULONG WINAPI bytestream_AddRef(IMFByteStream *iface) struct bytestream *stream = impl_from_IMFByteStream(iface); ULONG refcount = InterlockedIncrement(&stream->attributes.ref); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); return refcount; } @@ -3913,7 +3923,7 @@ static ULONG WINAPI bytestream_Release(IMFByteStream *iface) ULONG refcount = InterlockedDecrement(&stream->attributes.ref); struct async_stream_op *cur, *cur2; - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); if (!refcount) { @@ -3952,7 +3962,7 @@ static HRESULT WINAPI bytestream_stream_GetCapabilities(IMFByteStream *iface, DW return S_OK; } -static HRESULT WINAPI bytestream_GetCapabilities(IMFByteStream *iface, DWORD *capabilities) +static HRESULT WINAPI bytestream_file_GetCapabilities(IMFByteStream *iface, DWORD *capabilities) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -3963,14 +3973,14 @@ static HRESULT WINAPI bytestream_GetCapabilities(IMFByteStream *iface, DWORD *ca return S_OK; } -static HRESULT WINAPI bytestream_SetLength(IMFByteStream *iface, QWORD length) +static HRESULT WINAPI bytestream_file_SetLength(IMFByteStream *iface, QWORD length) { FIXME("%p, %s\n", iface, wine_dbgstr_longlong(length)); return E_NOTIMPL; } -static HRESULT WINAPI bytestream_file_GetCurrentPosition(IMFByteStream *iface, QWORD *position) +static HRESULT WINAPI bytestream_GetCurrentPosition(IMFByteStream *iface, QWORD *position) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -4030,7 +4040,7 @@ static HRESULT WINAPI bytestream_file_Read(IMFByteStream *iface, BYTE *buffer, U HRESULT hr = S_OK; BOOL ret; - TRACE("%p, %p, %lu, %p.\n", iface, buffer, size, read_len); + TRACE("%p, %p, %u, %p.\n", iface, buffer, size, read_len); EnterCriticalSection(&stream->cs); @@ -4054,7 +4064,7 @@ static HRESULT WINAPI bytestream_BeginRead(IMFByteStream *iface, BYTE *data, ULO { struct bytestream *stream = impl_from_IMFByteStream(iface); - TRACE("%p, %p, %lu, %p, %p.\n", iface, data, size, callback, state); + TRACE("%p, %p, %u, %p, %p.\n", iface, data, size, callback, state); return bytestream_create_io_request(stream, ASYNC_STREAM_OP_READ, data, size, callback, state); } @@ -4068,9 +4078,9 @@ static HRESULT WINAPI bytestream_EndRead(IMFByteStream *iface, IMFAsyncResult *r return bytestream_complete_io_request(stream, ASYNC_STREAM_OP_READ, result, byte_read); } -static HRESULT WINAPI bytestream_Write(IMFByteStream *iface, const BYTE *data, ULONG count, ULONG *written) +static HRESULT WINAPI bytestream_file_Write(IMFByteStream *iface, const BYTE *data, ULONG count, ULONG *written) { - FIXME("%p, %p, %lu, %p\n", iface, data, count, written); + FIXME("%p, %p, %u, %p\n", iface, data, count, written); return E_NOTIMPL; } @@ -4080,7 +4090,7 @@ static HRESULT WINAPI bytestream_BeginWrite(IMFByteStream *iface, const BYTE *da { struct bytestream *stream = impl_from_IMFByteStream(iface); - TRACE("%p, %p, %lu, %p, %p.\n", iface, data, size, callback, state); + TRACE("%p, %p, %u, %p, %p.\n", iface, data, size, callback, state); return bytestream_create_io_request(stream, ASYNC_STREAM_OP_WRITE, data, size, callback, state); } @@ -4094,22 +4104,44 @@ static HRESULT WINAPI bytestream_EndWrite(IMFByteStream *iface, IMFAsyncResult * return bytestream_complete_io_request(stream, ASYNC_STREAM_OP_WRITE, result, written); } -static HRESULT WINAPI bytestream_Seek(IMFByteStream *iface, MFBYTESTREAM_SEEK_ORIGIN seek, LONGLONG offset, - DWORD flags, QWORD *current) +static HRESULT WINAPI bytestream_Seek(IMFByteStream *iface, MFBYTESTREAM_SEEK_ORIGIN origin, LONGLONG offset, + DWORD flags, QWORD *current) { - FIXME("%p, %u, %s, 0x%08lx, %p\n", iface, seek, wine_dbgstr_longlong(offset), flags, current); + struct bytestream *stream = impl_from_IMFByteStream(iface); + HRESULT hr = S_OK; - return E_NOTIMPL; + TRACE("%p, %u, %s, 0x%08x, %p\n", iface, origin, wine_dbgstr_longlong(offset), flags, current); + + EnterCriticalSection(&stream->cs); + + switch (origin) + { + case msoBegin: + stream->position = offset; + break; + case msoCurrent: + stream->position += offset; + break; + default: + WARN("Unknown origin mode %d.\n", origin); + hr = E_INVALIDARG; + } + + *current = stream->position; + + LeaveCriticalSection(&stream->cs); + + return hr; } -static HRESULT WINAPI bytestream_Flush(IMFByteStream *iface) +static HRESULT WINAPI bytestream_file_Flush(IMFByteStream *iface) { FIXME("%p\n", iface); return E_NOTIMPL; } -static HRESULT WINAPI bytestream_Close(IMFByteStream *iface) +static HRESULT WINAPI bytestream_file_Close(IMFByteStream *iface) { FIXME("%p\n", iface); @@ -4134,21 +4166,21 @@ static const IMFByteStreamVtbl bytestream_file_vtbl = bytestream_QueryInterface, bytestream_AddRef, bytestream_Release, - bytestream_GetCapabilities, + bytestream_file_GetCapabilities, bytestream_file_GetLength, - bytestream_SetLength, - bytestream_file_GetCurrentPosition, + bytestream_file_SetLength, + bytestream_GetCurrentPosition, bytestream_SetCurrentPosition, bytestream_file_IsEndOfStream, bytestream_file_Read, bytestream_BeginRead, bytestream_EndRead, - bytestream_Write, + bytestream_file_Write, bytestream_BeginWrite, bytestream_EndWrite, bytestream_Seek, - bytestream_Flush, - bytestream_Close + bytestream_file_Flush, + bytestream_file_Close }; static HRESULT WINAPI bytestream_stream_GetLength(IMFByteStream *iface, QWORD *length) @@ -4185,17 +4217,6 @@ static HRESULT WINAPI bytestream_stream_SetLength(IMFByteStream *iface, QWORD le return hr; } -static HRESULT WINAPI bytestream_stream_GetCurrentPosition(IMFByteStream *iface, QWORD *position) -{ - struct bytestream *stream = impl_from_IMFByteStream(iface); - - TRACE("%p, %p.\n", iface, position); - - *position = stream->position; - - return S_OK; -} - static HRESULT WINAPI bytestream_stream_IsEndOfStream(IMFByteStream *iface, BOOL *ret) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -4220,7 +4241,7 @@ static HRESULT WINAPI bytestream_stream_Read(IMFByteStream *iface, BYTE *buffer, LARGE_INTEGER position; HRESULT hr; - TRACE("%p, %p, %lu, %p.\n", iface, buffer, size, read_len); + TRACE("%p, %p, %u, %p.\n", iface, buffer, size, read_len); EnterCriticalSection(&stream->cs); @@ -4242,7 +4263,7 @@ static HRESULT WINAPI bytestream_stream_Write(IMFByteStream *iface, const BYTE * LARGE_INTEGER position; HRESULT hr; - TRACE("%p, %p, %lu, %p.\n", iface, buffer, size, written); + TRACE("%p, %p, %u, %p.\n", iface, buffer, size, written); EnterCriticalSection(&stream->cs); @@ -4258,36 +4279,6 @@ static HRESULT WINAPI bytestream_stream_Write(IMFByteStream *iface, const BYTE * return hr; } -static HRESULT WINAPI bytestream_stream_Seek(IMFByteStream *iface, MFBYTESTREAM_SEEK_ORIGIN origin, LONGLONG offset, - DWORD flags, QWORD *current) -{ - struct bytestream *stream = impl_from_IMFByteStream(iface); - HRESULT hr = S_OK; - - TRACE("%p, %u, %s, %#lx, %p.\n", iface, origin, wine_dbgstr_longlong(offset), flags, current); - - EnterCriticalSection(&stream->cs); - - switch (origin) - { - case msoBegin: - stream->position = offset; - break; - case msoCurrent: - stream->position += offset; - break; - default: - WARN("Unknown origin mode %d.\n", origin); - hr = E_INVALIDARG; - } - - *current = stream->position; - - LeaveCriticalSection(&stream->cs); - - return hr; -} - static HRESULT WINAPI bytestream_stream_Flush(IMFByteStream *iface) { struct bytestream *stream = impl_from_IMFByteStream(iface); @@ -4312,7 +4303,7 @@ static const IMFByteStreamVtbl bytestream_stream_vtbl = bytestream_stream_GetCapabilities, bytestream_stream_GetLength, bytestream_stream_SetLength, - bytestream_stream_GetCurrentPosition, + bytestream_GetCurrentPosition, bytestream_SetCurrentPosition, bytestream_stream_IsEndOfStream, bytestream_stream_Read, @@ -4321,7 +4312,7 @@ static const IMFByteStreamVtbl bytestream_stream_vtbl = bytestream_stream_Write, bytestream_BeginWrite, bytestream_EndWrite, - bytestream_stream_Seek, + bytestream_Seek, bytestream_stream_Flush, bytestream_stream_Close, }; @@ -4386,11 +4377,10 @@ static const IMFAttributesVtbl bytestream_attributes_vtbl = mfattributes_CopyAllItems }; -static HRESULT WINAPI bytestream_stream_read_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) +static HRESULT WINAPI bytestream_read_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) { struct bytestream *stream = impl_from_read_callback_IRtwqAsyncCallback(iface); struct async_stream_op *op; - LARGE_INTEGER position; IUnknown *object; HRESULT hr; @@ -4401,13 +4391,8 @@ static HRESULT WINAPI bytestream_stream_read_callback_Invoke(IRtwqAsyncCallback EnterCriticalSection(&stream->cs); - position.QuadPart = op->position; - if (SUCCEEDED(hr = IStream_Seek(stream->stream, position, STREAM_SEEK_SET, NULL))) - { - if (SUCCEEDED(hr = IStream_Read(stream->stream, op->u.dest, op->requested_length, &op->actual_length))) - stream->position += op->actual_length; - } - + hr = IMFByteStream_Read(&stream->IMFByteStream_iface, op->u.dest, op->requested_length, &op->actual_length); + if(FAILED(hr)) TRACE("Read failed: %#lx\n", hr); IMFAsyncResult_SetStatus(op->caller, hr); list_add_tail(&stream->pending, &op->entry); @@ -4418,11 +4403,10 @@ static HRESULT WINAPI bytestream_stream_read_callback_Invoke(IRtwqAsyncCallback return S_OK; } -static HRESULT WINAPI bytestream_stream_write_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) +static HRESULT WINAPI bytestream_write_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) { struct bytestream *stream = impl_from_read_callback_IRtwqAsyncCallback(iface); struct async_stream_op *op; - LARGE_INTEGER position; IUnknown *object; HRESULT hr; @@ -4433,13 +4417,8 @@ static HRESULT WINAPI bytestream_stream_write_callback_Invoke(IRtwqAsyncCallback EnterCriticalSection(&stream->cs); - position.QuadPart = op->position; - if (SUCCEEDED(hr = IStream_Seek(stream->stream, position, STREAM_SEEK_SET, NULL))) - { - if (SUCCEEDED(hr = IStream_Write(stream->stream, op->u.src, op->requested_length, &op->actual_length))) - stream->position += op->actual_length; - } - + hr = IMFByteStream_Write(&stream->IMFByteStream_iface, op->u.src, op->requested_length, &op->actual_length); + if(FAILED(hr)) TRACE("Write failed: %#lx\n", hr); IMFAsyncResult_SetStatus(op->caller, hr); list_add_tail(&stream->pending, &op->entry); @@ -4450,22 +4429,22 @@ static HRESULT WINAPI bytestream_stream_write_callback_Invoke(IRtwqAsyncCallback return S_OK; } -static const IRtwqAsyncCallbackVtbl bytestream_stream_read_callback_vtbl = +static const IRtwqAsyncCallbackVtbl bytestream_read_callback_vtbl = { bytestream_callback_QueryInterface, bytestream_read_callback_AddRef, bytestream_read_callback_Release, bytestream_callback_GetParameters, - bytestream_stream_read_callback_Invoke, + bytestream_read_callback_Invoke, }; -static const IRtwqAsyncCallbackVtbl bytestream_stream_write_callback_vtbl = +static const IRtwqAsyncCallbackVtbl bytestream_write_callback_vtbl = { bytestream_callback_QueryInterface, bytestream_write_callback_AddRef, bytestream_write_callback_Release, bytestream_callback_GetParameters, - bytestream_stream_write_callback_Invoke, + bytestream_write_callback_Invoke, }; /*********************************************************************** @@ -4491,8 +4470,8 @@ HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **byt object->IMFByteStream_iface.lpVtbl = &bytestream_stream_vtbl; object->attributes.IMFAttributes_iface.lpVtbl = &bytestream_attributes_vtbl; - object->read_callback.lpVtbl = &bytestream_stream_read_callback_vtbl; - object->write_callback.lpVtbl = &bytestream_stream_write_callback_vtbl; + object->read_callback.lpVtbl = &bytestream_read_callback_vtbl; + object->write_callback.lpVtbl = &bytestream_write_callback_vtbl; InitializeCriticalSection(&object->cs); list_init(&object->pending); @@ -4516,38 +4495,6 @@ HRESULT WINAPI MFCreateMFByteStreamOnStream(IStream *stream, IMFByteStream **byt return S_OK; } -static HRESULT WINAPI bytestream_file_read_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) -{ - FIXME("%p, %p.\n", iface, result); - - return E_NOTIMPL; -} - -static HRESULT WINAPI bytestream_file_write_callback_Invoke(IRtwqAsyncCallback *iface, IRtwqAsyncResult *result) -{ - FIXME("%p, %p.\n", iface, result); - - return E_NOTIMPL; -} - -static const IRtwqAsyncCallbackVtbl bytestream_file_read_callback_vtbl = -{ - bytestream_callback_QueryInterface, - bytestream_read_callback_AddRef, - bytestream_read_callback_Release, - bytestream_callback_GetParameters, - bytestream_file_read_callback_Invoke, -}; - -static const IRtwqAsyncCallbackVtbl bytestream_file_write_callback_vtbl = -{ - bytestream_callback_QueryInterface, - bytestream_write_callback_AddRef, - bytestream_write_callback_Release, - bytestream_callback_GetParameters, - bytestream_file_write_callback_Invoke, -}; - static HRESULT WINAPI bytestream_file_getservice_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) { struct bytestream *stream = impl_bytestream_from_IMFGetService(iface); @@ -4582,8 +4529,11 @@ static const IMFGetServiceVtbl bytestream_file_getservice_vtbl = bytestream_file_getservice_GetService, }; -static HRESULT create_file_bytestream(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE openmode, MF_FILE_FLAGS flags, - const WCHAR *path, BOOL is_tempfile, IMFByteStream **bytestream) +/*********************************************************************** + * MFCreateFile (mfplat.@) + */ +HRESULT WINAPI MFCreateFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE openmode, MF_FILE_FLAGS flags, + LPCWSTR url, IMFByteStream **bytestream) { DWORD capabilities = MFBYTESTREAM_IS_SEEKABLE | MFBYTESTREAM_DOES_NOT_USE_NETWORK; DWORD filecreation_disposition = 0, fileaccessmode = 0, fileattributes = 0; @@ -4593,6 +4543,8 @@ static HRESULT create_file_bytestream(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPE HANDLE file; HRESULT hr; + TRACE("%d, %d, %#x, %s, %p.\n", accessmode, openmode, flags, debugstr_w(url), bytestream); + switch (accessmode) { case MF_ACCESSMODE_READ: @@ -4631,12 +4583,12 @@ static HRESULT create_file_bytestream(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPE if (flags & MF_FILEFLAGS_NOBUFFERING) fileattributes |= FILE_FLAG_NO_BUFFERING; - if (is_tempfile) - fileattributes |= FILE_FLAG_DELETE_ON_CLOSE; /* Open HANDLE to file */ - file = CreateFileW(path, fileaccessmode, filesharemode, NULL, filecreation_disposition, fileattributes, 0); - if (file == INVALID_HANDLE_VALUE) + file = CreateFileW(url, fileaccessmode, filesharemode, NULL, + filecreation_disposition, fileattributes, 0); + + if(file == INVALID_HANDLE_VALUE) return HRESULT_FROM_WIN32(GetLastError()); if (!(object = calloc(1, sizeof(*object)))) @@ -4654,68 +4606,26 @@ static HRESULT create_file_bytestream(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPE object->IMFByteStream_iface.lpVtbl = &bytestream_file_vtbl; object->attributes.IMFAttributes_iface.lpVtbl = &bytestream_attributes_vtbl; object->IMFGetService_iface.lpVtbl = &bytestream_file_getservice_vtbl; - object->read_callback.lpVtbl = &bytestream_file_read_callback_vtbl; - object->write_callback.lpVtbl = &bytestream_file_write_callback_vtbl; + object->read_callback.lpVtbl = &bytestream_read_callback_vtbl; + object->write_callback.lpVtbl = &bytestream_write_callback_vtbl; InitializeCriticalSection(&object->cs); list_init(&object->pending); object->capabilities = capabilities; object->hfile = file; - if (!is_tempfile && GetFileTime(file, NULL, NULL, &writetime)) + if (GetFileTime(file, NULL, NULL, &writetime)) { IMFAttributes_SetBlob(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_LAST_MODIFIED_TIME, (const UINT8 *)&writetime, sizeof(writetime)); } - IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_ORIGIN_NAME, path); + IMFAttributes_SetString(&object->attributes.IMFAttributes_iface, &MF_BYTESTREAM_ORIGIN_NAME, url); *bytestream = &object->IMFByteStream_iface; return S_OK; } -/*********************************************************************** - * MFCreateFile (mfplat.@) - */ -HRESULT WINAPI MFCreateFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE openmode, MF_FILE_FLAGS flags, - const WCHAR *path, IMFByteStream **bytestream) -{ - TRACE("%d, %d, %#x, %s, %p.\n", accessmode, openmode, flags, debugstr_w(path), bytestream); - - return create_file_bytestream(accessmode, openmode, flags, path, FALSE, bytestream); -} - -/*********************************************************************** - * MFCreateTempFile (mfplat.@) - */ -HRESULT WINAPI MFCreateTempFile(MF_FILE_ACCESSMODE accessmode, MF_FILE_OPENMODE openmode, MF_FILE_FLAGS flags, - IMFByteStream **bytestream) -{ - WCHAR name[24], tmppath[MAX_PATH], *path; - ULONG64 rnd; - size_t len; - HRESULT hr; - - TRACE("%d, %d, %#x, %p.\n", accessmode, openmode, flags, bytestream); - - BCryptGenRandom(NULL, (UCHAR *)&rnd, sizeof(rnd), BCRYPT_USE_SYSTEM_PREFERRED_RNG); - swprintf(name, ARRAY_SIZE(name), L"MFP%llX.TMP", rnd); - GetTempPathW(ARRAY_SIZE(tmppath), tmppath); - - len = wcslen(tmppath) + wcslen(name) + 2; - if (!(path = malloc(len * sizeof(*path)))) - return E_OUTOFMEMORY; - - wcscpy(path, tmppath); - PathCchAppend(path, len, name); - - hr = create_file_bytestream(accessmode, openmode, flags, path, TRUE, bytestream); - - free(path); - - return hr; -} - struct bytestream_wrapper { IMFByteStreamCacheControl IMFByteStreamCacheControl_iface; @@ -4834,7 +4744,7 @@ static ULONG WINAPI bytestream_wrapper_AddRef(IMFByteStream *iface) struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); ULONG refcount = InterlockedIncrement(&wrapper->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); return refcount; } @@ -4844,7 +4754,7 @@ static ULONG WINAPI bytestream_wrapper_Release(IMFByteStream *iface) struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); ULONG refcount = InterlockedDecrement(&wrapper->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); if (!refcount) { @@ -4942,7 +4852,7 @@ static HRESULT WINAPI bytestream_wrapper_Read(IMFByteStream *iface, BYTE *data, { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); - TRACE("%p, %p, %lu, %p.\n", iface, data, count, byte_read); + TRACE("%p, %p, %u, %p.\n", iface, data, count, byte_read); return wrapper->is_closed ? MF_E_INVALIDREQUEST : IMFByteStream_Read(wrapper->stream, data, count, byte_read); @@ -4953,7 +4863,7 @@ static HRESULT WINAPI bytestream_wrapper_BeginRead(IMFByteStream *iface, BYTE *d { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); - TRACE("%p, %p, %lu, %p, %p.\n", iface, data, size, callback, state); + TRACE("%p, %p, %u, %p, %p.\n", iface, data, size, callback, state); return wrapper->is_closed ? MF_E_INVALIDREQUEST : IMFByteStream_BeginRead(wrapper->stream, data, size, callback, state); @@ -4973,7 +4883,7 @@ static HRESULT WINAPI bytestream_wrapper_Write(IMFByteStream *iface, const BYTE { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); - TRACE("%p, %p, %lu, %p.\n", iface, data, count, written); + TRACE("%p, %p, %u, %p.\n", iface, data, count, written); return wrapper->is_closed ? MF_E_INVALIDREQUEST : IMFByteStream_Write(wrapper->stream, data, count, written); @@ -4984,7 +4894,7 @@ static HRESULT WINAPI bytestream_wrapper_BeginWrite(IMFByteStream *iface, const { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); - TRACE("%p, %p, %lu, %p, %p.\n", iface, data, size, callback, state); + TRACE("%p, %p, %u, %p, %p.\n", iface, data, size, callback, state); return wrapper->is_closed ? MF_E_INVALIDREQUEST : IMFByteStream_BeginWrite(wrapper->stream, data, size, callback, state); @@ -5005,7 +4915,7 @@ static HRESULT WINAPI bytestream_wrapper_Seek(IMFByteStream *iface, MFBYTESTREAM { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFByteStream(iface); - TRACE("%p, %u, %s, %#lx, %p.\n", iface, seek, wine_dbgstr_longlong(offset), flags, current); + TRACE("%p, %u, %s, %#x, %p.\n", iface, seek, wine_dbgstr_longlong(offset), flags, current); return wrapper->is_closed ? MF_E_INVALIDREQUEST : IMFByteStream_Seek(wrapper->stream, seek, offset, flags, current); @@ -5226,7 +5136,7 @@ static HRESULT WINAPI bytestream_wrapper_events_GetEvent(IMFMediaEventGenerator { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFMediaEventGenerator(iface); - TRACE("%p, %#lx, %p.\n", iface, flags, event); + TRACE("%p, %#x, %p.\n", iface, flags, event); return IMFMediaEventGenerator_GetEvent(wrapper->event_generator, flags, event); } @@ -5254,7 +5164,7 @@ static HRESULT WINAPI bytestream_wrapper_events_QueueEvent(IMFMediaEventGenerato { struct bytestream_wrapper *wrapper = impl_wrapper_from_IMFMediaEventGenerator(iface); - TRACE("%p, %ld, %s, %#lx, %s.\n", iface, type, debugstr_guid(ext_type), hr, debugstr_propvar(value)); + TRACE("%p, %d, %s, %#x, %s.\n", iface, type, debugstr_guid(ext_type), hr, debugstr_propvar(value)); return IMFMediaEventGenerator_QueueEvent(wrapper->event_generator, type, ext_type, hr, value); } @@ -5357,7 +5267,7 @@ static HRESULT WINAPI bytestream_wrapper_propstore_GetAt(IPropertyStore *iface, { struct bytestream_wrapper *wrapper = impl_wrapper_from_IPropertyStore(iface); - TRACE("%p, %lu, %p.\n", iface, prop, key); + TRACE("%p, %u, %p.\n", iface, prop, key); return IPropertyStore_GetAt(wrapper->propstore, prop, key); } @@ -5803,39 +5713,39 @@ static ULONG WINAPI MFPluginControl_Release(IMFPluginControl *iface) static HRESULT WINAPI MFPluginControl_GetPreferredClsid(IMFPluginControl *iface, DWORD plugin_type, const WCHAR *selector, CLSID *clsid) { - FIXME("(%ld %s %p)\n", plugin_type, debugstr_w(selector), clsid); + FIXME("(%d %s %p)\n", plugin_type, debugstr_w(selector), clsid); return E_NOTIMPL; } static HRESULT WINAPI MFPluginControl_GetPreferredClsidByIndex(IMFPluginControl *iface, DWORD plugin_type, DWORD index, WCHAR **selector, CLSID *clsid) { - FIXME("(%ld %ld %p %p)\n", plugin_type, index, selector, clsid); + FIXME("(%d %d %p %p)\n", plugin_type, index, selector, clsid); return E_NOTIMPL; } static HRESULT WINAPI MFPluginControl_SetPreferredClsid(IMFPluginControl *iface, DWORD plugin_type, const WCHAR *selector, const CLSID *clsid) { - FIXME("(%ld %s %s)\n", plugin_type, debugstr_w(selector), debugstr_guid(clsid)); + FIXME("(%d %s %s)\n", plugin_type, debugstr_w(selector), debugstr_guid(clsid)); return E_NOTIMPL; } static HRESULT WINAPI MFPluginControl_IsDisabled(IMFPluginControl *iface, DWORD plugin_type, REFCLSID clsid) { - FIXME("(%ld %s)\n", plugin_type, debugstr_guid(clsid)); + FIXME("(%d %s)\n", plugin_type, debugstr_guid(clsid)); return E_NOTIMPL; } static HRESULT WINAPI MFPluginControl_GetDisabledByIndex(IMFPluginControl *iface, DWORD plugin_type, DWORD index, CLSID *clsid) { - FIXME("(%ld %ld %p)\n", plugin_type, index, clsid); + FIXME("(%d %d %p)\n", plugin_type, index, clsid); return E_NOTIMPL; } static HRESULT WINAPI MFPluginControl_SetDisabled(IMFPluginControl *iface, DWORD plugin_type, REFCLSID clsid, BOOL disabled) { - FIXME("(%ld %s %x)\n", plugin_type, debugstr_guid(clsid), disabled); + FIXME("(%d %s %x)\n", plugin_type, debugstr_guid(clsid), disabled); return E_NOTIMPL; } @@ -6137,9 +6047,8 @@ static const IRtwqAsyncCallbackVtbl source_resolver_callback_url_vtbl = static HRESULT resolver_create_registered_handler(HKEY hkey, REFIID riid, void **handler) { - DWORD name_length, type; + unsigned int j = 0, name_length, type; HRESULT hr = E_FAIL; - unsigned int j = 0; WCHAR clsidW[39]; CLSID clsid; @@ -6162,15 +6071,40 @@ static HRESULT resolver_create_registered_handler(HKEY hkey, REFIID riid, void * return hr; } -static HRESULT resolver_create_bytestream_handler(IMFByteStream *stream, DWORD flags, const WCHAR *mime, - const WCHAR *extension, IMFByteStreamHandler **handler) +static HRESULT resolver_get_bytestream_handler(IMFByteStream *stream, const WCHAR *url, DWORD flags, + IMFByteStreamHandler **handler) { static const HKEY hkey_roots[2] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE }; + WCHAR *mimeW = NULL, *urlW = NULL; + IMFAttributes *attributes; + const WCHAR *url_ext; HRESULT hr = E_FAIL; unsigned int i, j; + UINT32 length; *handler = NULL; + /* MIME type */ + if (SUCCEEDED(IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes))) + { + IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, &mimeW, &length); + if (!url) + { + IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, &urlW, &length); + url = urlW; + } + IMFAttributes_Release(attributes); + } + + /* Extension */ + url_ext = url ? wcsrchr(url, '.') : NULL; + + if (!url_ext && !mimeW) + { + CoTaskMemFree(urlW); + return MF_E_UNSUPPORTED_BYTESTREAM_TYPE; + } + if (!(flags & MF_RESOLUTION_DISABLE_LOCAL_PLUGINS)) { struct local_handler *local_handler; @@ -6179,8 +6113,8 @@ static HRESULT resolver_create_bytestream_handler(IMFByteStream *stream, DWORD f LIST_FOR_EACH_ENTRY(local_handler, &local_bytestream_handlers, struct local_handler, entry) { - if ((mime && !lstrcmpiW(mime, local_handler->u.bytestream.mime)) - || (extension && !lstrcmpiW(extension, local_handler->u.bytestream.extension))) + if ((mimeW && !lstrcmpiW(mimeW, local_handler->u.bytestream.mime)) + || (url_ext && !lstrcmpiW(url_ext, local_handler->u.bytestream.extension))) { if (SUCCEEDED(hr = IMFActivate_ActivateObject(local_handler->activate, &IID_IMFByteStreamHandler, (void **)handler))) @@ -6191,12 +6125,16 @@ static HRESULT resolver_create_bytestream_handler(IMFByteStream *stream, DWORD f LeaveCriticalSection(&local_handlers_section); if (*handler) + { + CoTaskMemFree(mimeW); + CoTaskMemFree(urlW); return hr; + } } for (i = 0, hr = E_FAIL; i < ARRAY_SIZE(hkey_roots); ++i) { - const WCHAR *namesW[2] = { mime, extension }; + const WCHAR *namesW[2] = { mimeW, url_ext }; HKEY hkey, hkey_handler; if (RegOpenKeyW(hkey_roots[i], L"Software\\Microsoft\\Windows Media Foundation\\ByteStreamHandlers", &hkey)) @@ -6223,161 +6161,14 @@ static HRESULT resolver_create_bytestream_handler(IMFByteStream *stream, DWORD f break; } - return hr; -} - -static HRESULT resolver_get_bytestream_url_hint(IMFByteStream *stream, WCHAR const **url) -{ - static const unsigned char asfmagic[] = {0x30,0x26,0xb2,0x75,0x8e,0x66,0xcf,0x11,0xa6,0xd9,0x00,0xaa,0x00,0x62,0xce,0x6c}; - static const unsigned char wavmagic[] = { 'R', 'I', 'F', 'F',0x00,0x00,0x00,0x00, 'W', 'A', 'V', 'E', 'f', 'm', 't', ' '}; - static const unsigned char wavmask[] = {0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; - static const unsigned char isommagic[] = {0x00,0x00,0x00,0x00, 'f', 't', 'y', 'p', 'i', 's', 'o', 'm',0x00,0x00,0x00,0x00}; - static const unsigned char mp4_magic[] = {0x00,0x00,0x00,0x00, 'f', 't', 'y', 'p', 'M', 'S', 'N', 'V',0x00,0x00,0x00,0x00}; - static const unsigned char mp42magic[] = {0x00,0x00,0x00,0x00, 'f', 't', 'y', 'p', 'm', 'p', '4', '2',0x00,0x00,0x00,0x00}; - static const unsigned char mp4vmagic[] = {0x00,0x00,0x00,0x00, 'f', 't', 'y', 'p', 'M', '4', 'V', ' ',0x00,0x00,0x00,0x00}; - static const unsigned char mp4mask[] = {0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00}; - static const struct stream_content_url_hint - { - const unsigned char *magic; - const WCHAR *url; - const unsigned char *mask; - } - url_hints[] = - { - { asfmagic, L".asf" }, - { wavmagic, L".wav", wavmask }, - { isommagic, L".mp4", mp4mask }, - { mp42magic, L".mp4", mp4mask }, - { mp4_magic, L".mp4", mp4mask }, - { mp4vmagic, L".m4v", mp4mask }, - }; - unsigned char buffer[4 * sizeof(unsigned int)], pattern[4 * sizeof(unsigned int)]; - IMFAttributes *attributes; - DWORD length = 0, caps = 0; - unsigned int i, j; - QWORD position; - HRESULT hr; - - *url = NULL; - - if (SUCCEEDED(IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes))) - { - UINT32 string_length = 0; - IMFAttributes_GetStringLength(attributes, &MF_BYTESTREAM_CONTENT_TYPE, &string_length); - IMFAttributes_Release(attributes); - - if (string_length) - return S_OK; - } - - if (FAILED(hr = IMFByteStream_GetCapabilities(stream, &caps))) - return hr; - - if (!(caps & MFBYTESTREAM_IS_SEEKABLE)) - return MF_E_UNSUPPORTED_BYTESTREAM_TYPE; - - if (FAILED(hr = IMFByteStream_GetCurrentPosition(stream, &position))) - return hr; - - hr = IMFByteStream_Read(stream, buffer, sizeof(buffer), &length); - IMFByteStream_SetCurrentPosition(stream, position); if (FAILED(hr)) - return hr; - - if (length < sizeof(buffer)) - return S_OK; - - for (i = 0; i < ARRAY_SIZE(url_hints); ++i) - { - memcpy(pattern, buffer, sizeof(buffer)); - if (url_hints[i].mask) - { - unsigned int *mask = (unsigned int *)url_hints[i].mask; - unsigned int *data = (unsigned int *)pattern; - - for (j = 0; j < sizeof(buffer) / sizeof(unsigned int); ++j) - data[j] &= mask[j]; - - } - if (!memcmp(pattern, url_hints[i].magic, sizeof(pattern))) - { - *url = url_hints[i].url; - break; - } - } - - if (*url) - TRACE("Content type guessed as %s from %s.\n", debugstr_w(*url), debugstr_an((char *)buffer, length)); - else - WARN("Unrecognized content type %s.\n", debugstr_an((char *)buffer, length)); - - return S_OK; -} - -static HRESULT resolver_create_gstreamer_handler(IMFByteStreamHandler **handler) -{ - static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; - return CoCreateInstance(&CLSID_GStreamerByteStreamHandler, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); -} - -static HRESULT resolver_get_bytestream_handler(IMFByteStream *stream, const WCHAR *url, DWORD flags, - IMFByteStreamHandler **handler) -{ - WCHAR *mimeW = NULL, *urlW = NULL; - IMFAttributes *attributes; - const WCHAR *url_ext; - HRESULT hr = E_FAIL; - UINT32 length; - - *handler = NULL; - - /* MIME type */ - if (SUCCEEDED(IMFByteStream_QueryInterface(stream, &IID_IMFAttributes, (void **)&attributes))) { - IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_CONTENT_TYPE, &mimeW, &length); - if (!url) - { - IMFAttributes_GetAllocatedString(attributes, &MF_BYTESTREAM_ORIGIN_NAME, &urlW, &length); - url = urlW; - } - IMFAttributes_Release(attributes); - } - - /* Extension */ - url_ext = url ? wcsrchr(url, '.') : NULL; - - /* If content type was provided by the caller, it's tried first. Otherwise an attempt to deduce - content type from the content itself is made. - - TODO: wine specific fallback to predefined handler could be replaced by normally registering - this handler for all possible types. - */ - - if (url_ext || mimeW) - { - hr = resolver_create_bytestream_handler(stream, flags, mimeW, url_ext, handler); - - if (FAILED(hr)) - hr = resolver_create_gstreamer_handler(handler); + static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; + hr = CoCreateInstance(&CLSID_GStreamerByteStreamHandler, NULL, CLSCTX_INPROC_SERVER, &IID_IMFByteStreamHandler, (void **)handler); } CoTaskMemFree(mimeW); CoTaskMemFree(urlW); - - if (SUCCEEDED(hr)) - return hr; - - if (!(flags & MF_RESOLUTION_CONTENT_DOES_NOT_HAVE_TO_MATCH_EXTENSION_OR_MIME_TYPE)) - return MF_E_UNSUPPORTED_BYTESTREAM_TYPE; - - if (FAILED(hr = resolver_get_bytestream_url_hint(stream, &url_ext))) - return hr; - - hr = resolver_create_bytestream_handler(stream, flags, NULL, url_ext, handler); - - if (FAILED(hr)) - hr = resolver_create_gstreamer_handler(handler); - return hr; } @@ -6387,7 +6178,7 @@ static HRESULT resolver_create_scheme_handler(const WCHAR *scheme, DWORD flags, HRESULT hr = MF_E_UNSUPPORTED_SCHEME; unsigned int i; - TRACE("%s, %#lx, %p.\n", debugstr_w(scheme), flags, handler); + TRACE("%s, %#x, %p.\n", debugstr_w(scheme), flags, handler); *handler = NULL; @@ -6469,11 +6260,10 @@ static HRESULT resolver_get_scheme_handler(const WCHAR *url, DWORD flags, IMFSch if (ptr == url || *ptr != ':') { url = fileschemeW; - len = ARRAY_SIZE(fileschemeW) - 1; + ptr = fileschemeW + ARRAY_SIZE(fileschemeW) - 1; } - else - len = ptr - url + 1; + len = ptr - url; scheme = malloc((len + 1) * sizeof(WCHAR)); if (!scheme) return E_OUTOFMEMORY; @@ -6558,7 +6348,7 @@ static ULONG WINAPI source_resolver_AddRef(IMFSourceResolver *iface) struct source_resolver *resolver = impl_from_IMFSourceResolver(iface); ULONG refcount = InterlockedIncrement(&resolver->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); return refcount; } @@ -6569,7 +6359,7 @@ static ULONG WINAPI source_resolver_Release(IMFSourceResolver *iface) ULONG refcount = InterlockedDecrement(&resolver->refcount); struct resolver_queued_result *result, *result2; - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); if (!refcount) { @@ -6596,7 +6386,7 @@ static HRESULT WINAPI source_resolver_CreateObjectFromURL(IMFSourceResolver *ifa RTWQASYNCRESULT *data; HRESULT hr; - TRACE("%p, %s, %#lx, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, obj_type, object); + TRACE("%p, %s, %#x, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, obj_type, object); if (!url || !obj_type || !object) return E_POINTER; @@ -6637,7 +6427,7 @@ static HRESULT WINAPI source_resolver_CreateObjectFromByteStream(IMFSourceResolv RTWQASYNCRESULT *data; HRESULT hr; - TRACE("%p, %p, %s, %#lx, %p, %p, %p.\n", iface, stream, debugstr_w(url), flags, props, obj_type, object); + TRACE("%p, %p, %s, %#x, %p, %p, %p.\n", iface, stream, debugstr_w(url), flags, props, obj_type, object); if (!stream || !obj_type || !object) return E_POINTER; @@ -6678,7 +6468,7 @@ static HRESULT WINAPI source_resolver_BeginCreateObjectFromURL(IMFSourceResolver IRtwqAsyncResult *result; HRESULT hr; - TRACE("%p, %s, %#lx, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); + TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); if (FAILED(hr = resolver_get_scheme_handler(url, flags, &handler))) return hr; @@ -6722,7 +6512,7 @@ static HRESULT WINAPI source_resolver_BeginCreateObjectFromByteStream(IMFSourceR IRtwqAsyncResult *result; HRESULT hr; - TRACE("%p, %p, %s, %#lx, %p, %p, %p, %p.\n", iface, stream, debugstr_w(url), flags, props, cancel_cookie, + TRACE("%p, %p, %s, %#x, %p, %p, %p, %p.\n", iface, stream, debugstr_w(url), flags, props, cancel_cookie, callback, state); if (FAILED(hr = resolver_get_bytestream_handler(stream, url, flags, &handler))) @@ -6868,7 +6658,7 @@ static ULONG WINAPI mfmediaevent_AddRef(IMFMediaEvent *iface) struct media_event *event = impl_from_IMFMediaEvent(iface); ULONG refcount = InterlockedIncrement(&event->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -6878,7 +6668,7 @@ static ULONG WINAPI mfmediaevent_Release(IMFMediaEvent *iface) struct media_event *event = impl_from_IMFMediaEvent(iface); ULONG refcount = InterlockedDecrement(&event->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -7258,7 +7048,7 @@ HRESULT WINAPI MFCreateMediaEvent(MediaEventType type, REFGUID extended_type, HR struct media_event *object; HRESULT hr; - TRACE("%s, %s, %#lx, %s, %p.\n", debugstr_eventid(type), debugstr_guid(extended_type), status, + TRACE("%s, %s, %#x, %s, %p.\n", debugstr_eventid(type), debugstr_guid(extended_type), status, debugstr_propvar(value), event); object = malloc(sizeof(*object)); @@ -7367,7 +7157,7 @@ static ULONG WINAPI eventqueue_AddRef(IMFMediaEventQueue *iface) struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); ULONG refcount = InterlockedIncrement(&queue->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -7377,7 +7167,7 @@ static ULONG WINAPI eventqueue_Release(IMFMediaEventQueue *iface) struct event_queue *queue = impl_from_IMFMediaEventQueue(iface); ULONG refcount = InterlockedDecrement(&queue->refcount); - TRACE("%p, refcount %lu.\n", queue, refcount); + TRACE("%p, refcount %u.\n", queue, refcount); if (!refcount) { @@ -7542,7 +7332,7 @@ static HRESULT WINAPI eventqueue_QueueEventParamVar(IMFMediaEventQueue *iface, M IMFMediaEvent *event; HRESULT hr; - TRACE("%p, %s, %s, %#lx, %s\n", iface, debugstr_eventid(event_type), debugstr_guid(extended_type), status, + TRACE("%p, %s, %s, %#x, %s\n", iface, debugstr_eventid(event_type), debugstr_guid(extended_type), status, debugstr_propvar(value)); if (FAILED(hr = MFCreateMediaEvent(event_type, extended_type, status, value, &event))) @@ -7561,7 +7351,7 @@ static HRESULT WINAPI eventqueue_QueueEventParamUnk(IMFMediaEventQueue *iface, M PROPVARIANT value; HRESULT hr; - TRACE("%p, %s, %s, %#lx, %p.\n", iface, debugstr_eventid(event_type), debugstr_guid(extended_type), status, unk); + TRACE("%p, %s, %s, %#x, %p.\n", iface, debugstr_eventid(event_type), debugstr_guid(extended_type), status, unk); value.vt = VT_UNKNOWN; value.punkVal = unk; @@ -7684,7 +7474,7 @@ static ULONG WINAPI collection_AddRef(IMFCollection *iface) struct collection *collection = impl_from_IMFCollection(iface); ULONG refcount = InterlockedIncrement(&collection->refcount); - TRACE("%p, %ld.\n", collection, refcount); + TRACE("%p, %d.\n", collection, refcount); return refcount; } @@ -7694,7 +7484,7 @@ static ULONG WINAPI collection_Release(IMFCollection *iface) struct collection *collection = impl_from_IMFCollection(iface); ULONG refcount = InterlockedDecrement(&collection->refcount); - TRACE("%p, %ld.\n", collection, refcount); + TRACE("%p, %d.\n", collection, refcount); if (!refcount) { @@ -7724,7 +7514,7 @@ static HRESULT WINAPI collection_GetElement(IMFCollection *iface, DWORD idx, IUn { struct collection *collection = impl_from_IMFCollection(iface); - TRACE("%p, %lu, %p.\n", iface, idx, element); + TRACE("%p, %u, %p.\n", iface, idx, element); if (!element) return E_POINTER; @@ -7761,7 +7551,7 @@ static HRESULT WINAPI collection_RemoveElement(IMFCollection *iface, DWORD idx, struct collection *collection = impl_from_IMFCollection(iface); size_t count; - TRACE("%p, %lu, %p.\n", iface, idx, element); + TRACE("%p, %u, %p.\n", iface, idx, element); if (!element) return E_POINTER; @@ -7784,7 +7574,7 @@ static HRESULT WINAPI collection_InsertElementAt(IMFCollection *iface, DWORD idx struct collection *collection = impl_from_IMFCollection(iface); size_t i; - TRACE("%p, %lu, %p.\n", iface, idx, element); + TRACE("%p, %u, %p.\n", iface, idx, element); if (!mf_array_reserve((void **)&collection->elements, &collection->capacity, idx + 1, sizeof(*collection->elements))) @@ -7862,7 +7652,7 @@ HRESULT WINAPI MFCreateCollection(IMFCollection **collection) */ void *WINAPI MFHeapAlloc(SIZE_T size, ULONG flags, char *file, int line, EAllocationType type) { - TRACE("%Iu, %#lx, %s, %d, %#x.\n", size, flags, debugstr_a(file), line, type); + TRACE("%lu, %#x, %s, %d, %#x.\n", size, flags, debugstr_a(file), line, type); return HeapAlloc(GetProcessHeap(), flags, size); } @@ -7907,7 +7697,7 @@ static ULONG WINAPI system_clock_AddRef(IMFClock *iface) struct system_clock *clock = impl_from_IMFClock(iface); ULONG refcount = InterlockedIncrement(&clock->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -7917,7 +7707,7 @@ static ULONG WINAPI system_clock_Release(IMFClock *iface) struct system_clock *clock = impl_from_IMFClock(iface); ULONG refcount = InterlockedDecrement(&clock->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) free(clock); @@ -7938,7 +7728,7 @@ static HRESULT WINAPI system_clock_GetClockCharacteristics(IMFClock *iface, DWOR static HRESULT WINAPI system_clock_GetCorrelatedTime(IMFClock *iface, DWORD reserved, LONGLONG *clock_time, MFTIME *system_time) { - TRACE("%p, %#lx, %p, %p.\n", iface, reserved, clock_time, system_time); + TRACE("%p, %#x, %p, %p.\n", iface, reserved, clock_time, system_time); *clock_time = *system_time = MFGetSystemTime(); @@ -7956,7 +7746,7 @@ static HRESULT WINAPI system_clock_GetContinuityKey(IMFClock *iface, DWORD *key) static HRESULT WINAPI system_clock_GetState(IMFClock *iface, DWORD reserved, MFCLOCK_STATE *state) { - TRACE("%p, %#lx, %p.\n", iface, reserved, state); + TRACE("%p, %#x, %p.\n", iface, reserved, state); *state = MFCLOCK_STATE_RUNNING; @@ -8036,7 +7826,7 @@ static ULONG WINAPI system_time_source_AddRef(IMFPresentationTimeSource *iface) struct system_time_source *source = impl_from_IMFPresentationTimeSource(iface); ULONG refcount = InterlockedIncrement(&source->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -8046,7 +7836,7 @@ static ULONG WINAPI system_time_source_Release(IMFPresentationTimeSource *iface) struct system_time_source *source = impl_from_IMFPresentationTimeSource(iface); ULONG refcount = InterlockedDecrement(&source->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -8074,14 +7864,18 @@ static HRESULT WINAPI system_time_source_GetCorrelatedTime(IMFPresentationTimeSo struct system_time_source *source = impl_from_IMFPresentationTimeSource(iface); HRESULT hr; - TRACE("%p, %#lx, %p, %p.\n", iface, reserved, clock_time, system_time); + TRACE("%p, %#x, %p, %p.\n", iface, reserved, clock_time, system_time); EnterCriticalSection(&source->cs); if (SUCCEEDED(hr = IMFClock_GetCorrelatedTime(source->clock, 0, clock_time, system_time))) { if (source->state == MFCLOCK_STATE_RUNNING) - system_time_source_update_clock_time(source, *system_time); - *clock_time = source->start_offset + source->clock_time; + { + system_time_source_apply_rate(source, clock_time); + *clock_time += source->start_offset; + } + else + *clock_time = source->start_offset; } LeaveCriticalSection(&source->cs); @@ -8102,7 +7896,7 @@ static HRESULT WINAPI system_time_source_GetState(IMFPresentationTimeSource *ifa { struct system_time_source *source = impl_from_IMFPresentationTimeSource(iface); - TRACE("%p, %#lx, %p.\n", iface, reserved, state); + TRACE("%p, %#x, %p.\n", iface, reserved, state); EnterCriticalSection(&source->cs); *state = source->state; @@ -8220,19 +8014,25 @@ static HRESULT WINAPI system_time_source_sink_OnClockStart(IMFClockStateSink *if state = source->state; if (SUCCEEDED(hr = system_time_source_change_state(source, CLOCK_CMD_START))) { + system_time_source_apply_rate(source, &system_time); if (start_offset == PRESENTATION_CURRENT_POSITION) { - if (state != MFCLOCK_STATE_RUNNING) + switch (state) { - source->start_offset -= system_time; - source->system_time = 0; + case MFCLOCK_STATE_RUNNING: + break; + case MFCLOCK_STATE_PAUSED: + source->start_offset -= system_time; + break; + default: + source->start_offset = -system_time; + break; + ; } } else { - source->start_offset = start_offset; - source->system_time = system_time; - source->clock_time = 0; + source->start_offset = -system_time + start_offset; } } LeaveCriticalSection(&source->cs); @@ -8249,9 +8049,7 @@ static HRESULT WINAPI system_time_source_sink_OnClockStop(IMFClockStateSink *ifa EnterCriticalSection(&source->cs); if (SUCCEEDED(hr = system_time_source_change_state(source, CLOCK_CMD_STOP))) - { - source->start_offset = source->system_time = source->clock_time = 0; - } + source->start_offset = 0; LeaveCriticalSection(&source->cs); return hr; @@ -8267,7 +8065,8 @@ static HRESULT WINAPI system_time_source_sink_OnClockPause(IMFClockStateSink *if EnterCriticalSection(&source->cs); if (SUCCEEDED(hr = system_time_source_change_state(source, CLOCK_CMD_PAUSE))) { - system_time_source_update_clock_time(source, system_time); + system_time_source_apply_rate(source, &system_time); + source->start_offset += system_time; } LeaveCriticalSection(&source->cs); @@ -8284,7 +8083,8 @@ static HRESULT WINAPI system_time_source_sink_OnClockRestart(IMFClockStateSink * EnterCriticalSection(&source->cs); if (SUCCEEDED(hr = system_time_source_change_state(source, CLOCK_CMD_RESTART))) { - source->system_time = system_time; + system_time_source_apply_rate(source, &system_time); + source->start_offset -= system_time; } LeaveCriticalSection(&source->cs); @@ -8399,7 +8199,7 @@ static ULONG WINAPI async_create_file_callback_AddRef(IRtwqAsyncCallback *iface) struct async_create_file *async = impl_from_create_file_IRtwqAsyncCallback(iface); ULONG refcount = InterlockedIncrement(&async->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -8409,7 +8209,7 @@ static ULONG WINAPI async_create_file_callback_Release(IRtwqAsyncCallback *iface struct async_create_file *async = impl_from_create_file_IRtwqAsyncCallback(iface); ULONG refcount = InterlockedDecrement(&async->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -8696,7 +8496,7 @@ static ULONG WINAPI property_store_AddRef(IPropertyStore *iface) struct property_store *store = impl_from_IPropertyStore(iface); ULONG refcount = InterlockedIncrement(&store->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); return refcount; } @@ -8706,7 +8506,7 @@ static ULONG WINAPI property_store_Release(IPropertyStore *iface) struct property_store *store = impl_from_IPropertyStore(iface); ULONG refcount = InterlockedDecrement(&store->refcount); - TRACE("%p, refcount %ld.\n", iface, refcount); + TRACE("%p, refcount %d.\n", iface, refcount); if (!refcount) { @@ -8737,7 +8537,7 @@ static HRESULT WINAPI property_store_GetAt(IPropertyStore *iface, DWORD index, P { struct property_store *store = impl_from_IPropertyStore(iface); - TRACE("%p, %lu, %p.\n", iface, index, key); + TRACE("%p, %u, %p.\n", iface, index, key); EnterCriticalSection(&store->cs); @@ -8929,7 +8729,7 @@ static ULONG WINAPI dxgi_device_manager_AddRef(IMFDXGIDeviceManager *iface) struct dxgi_device_manager *manager = impl_from_IMFDXGIDeviceManager(iface); ULONG refcount = InterlockedIncrement(&manager->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -8939,7 +8739,7 @@ static ULONG WINAPI dxgi_device_manager_Release(IMFDXGIDeviceManager *iface) struct dxgi_device_manager *manager = impl_from_IMFDXGIDeviceManager(iface); ULONG refcount = InterlockedDecrement(&manager->refcount); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -9232,9 +9032,21 @@ static const IMFDXGIDeviceManagerVtbl dxgi_device_manager_vtbl = HRESULT WINAPI MFCreateDXGIDeviceManager(UINT *token, IMFDXGIDeviceManager **manager) { struct dxgi_device_manager *object; + const char *do_not_create = getenv("WINE_DO_NOT_CREATE_DXGI_DEVICE_MANAGER"); TRACE("%p, %p.\n", token, manager); + /* Returning a DXGI device manager triggers a bug and breaks The + * Long Dark and Trailmakers. This should be removed once CW bug + * #19126 is solved. Returning a DXGI device manager also breaks + * Age of Empires Definitive Edition - this gameid should be removed + * once CW bug #19741 is solved. */ + if (do_not_create && do_not_create[0] != '\0') + { + FIXME("stubbing out\n"); + return E_NOTIMPL; + } + if (!token || !manager) return E_POINTER; @@ -9360,7 +9172,7 @@ static ULONGLONG lldiv128(ULARGE_INTEGER c1, ULARGE_INTEGER c0, LONGLONG denom) { ULARGE_INTEGER q1, q0, rhat; ULARGE_INTEGER v, cmp1, cmp2; - DWORD s = 0; + unsigned int s = 0; v.QuadPart = llabs(denom); diff --git a/dlls/mfplat/media_source.c b/dlls/mfplat/media_source.c new file mode 100644 index 00000000000..82a6da1bcbf --- /dev/null +++ wine/dlls/mfplat/media_source.c @@ -0,0 +1,2006 @@ +/* GStreamer Media Source + * + * Copyright 2020 Derek Lesho + * Copyright 2020 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "mfapi.h" +#include "mferror.h" + +#include "wine/list.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +struct media_stream +{ + IMFMediaStream IMFMediaStream_iface; + LONG ref; + struct media_source *parent_source; + IMFMediaEventQueue *event_queue; + IMFStreamDescriptor *descriptor; + + struct wg_parser_stream *wg_stream; + + IUnknown **token_queue; + LONG token_queue_count; + LONG token_queue_cap; + + enum + { + STREAM_INACTIVE, + STREAM_SHUTDOWN, + STREAM_RUNNING, + } state; + DWORD stream_id; + BOOL eos; +}; + +enum source_async_op +{ + SOURCE_ASYNC_START, + SOURCE_ASYNC_PAUSE, + SOURCE_ASYNC_STOP, + SOURCE_ASYNC_REQUEST_SAMPLE, +}; + +struct source_async_command +{ + IUnknown IUnknown_iface; + LONG refcount; + enum source_async_op op; + union + { + struct + { + IMFPresentationDescriptor *descriptor; + GUID format; + PROPVARIANT position; + } start; + struct + { + struct media_stream *stream; + IUnknown *token; + } request_sample; + } u; +}; + +struct media_source +{ + IMFMediaSource IMFMediaSource_iface; + IMFGetService IMFGetService_iface; + IMFRateSupport IMFRateSupport_iface; + IMFRateControl IMFRateControl_iface; + IMFAsyncCallback async_commands_callback; + LONG ref; + DWORD async_commands_queue; + IMFMediaEventQueue *event_queue; + IMFByteStream *byte_stream; + + struct wg_parser *wg_parser; + + struct media_stream **streams; + ULONG stream_count; + IMFPresentationDescriptor *pres_desc; + enum + { + SOURCE_OPENING, + SOURCE_STOPPED, + SOURCE_PAUSED, + SOURCE_RUNNING, + SOURCE_SHUTDOWN, + } state; + + HANDLE read_thread; + bool read_thread_shutdown; +}; + +static inline struct media_stream *impl_from_IMFMediaStream(IMFMediaStream *iface) +{ + return CONTAINING_RECORD(iface, struct media_stream, IMFMediaStream_iface); +} + +static inline struct media_source *impl_from_IMFMediaSource(IMFMediaSource *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFMediaSource_iface); +} + +static inline struct media_source *impl_from_IMFGetService(IMFGetService *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFGetService_iface); +} + +static inline struct media_source *impl_from_IMFRateSupport(IMFRateSupport *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFRateSupport_iface); +} + +static inline struct media_source *impl_from_IMFRateControl(IMFRateControl *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, IMFRateControl_iface); +} + +static inline struct media_source *impl_from_async_commands_callback_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct media_source, async_commands_callback); +} + +static inline struct source_async_command *impl_from_async_command_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct source_async_command, IUnknown_iface); +} + +static HRESULT WINAPI source_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI source_async_command_AddRef(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + return InterlockedIncrement(&command->refcount); +} + +static ULONG WINAPI source_async_command_Release(IUnknown *iface) +{ + struct source_async_command *command = impl_from_async_command_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&command->refcount); + + if (!refcount) + { + if (command->op == SOURCE_ASYNC_START) + PropVariantClear(&command->u.start.position); + else if (command->op == SOURCE_ASYNC_REQUEST_SAMPLE) + { + if (command->u.request_sample.token) + IUnknown_Release(command->u.request_sample.token); + } + free(command); + } + + return refcount; +} + +static const IUnknownVtbl source_async_command_vtbl = +{ + source_async_command_QueryInterface, + source_async_command_AddRef, + source_async_command_Release, +}; + +static HRESULT source_create_async_op(enum source_async_op op, struct source_async_command **ret) +{ + struct source_async_command *command; + + if (!(command = calloc(1, sizeof(*command)))) + return E_OUTOFMEMORY; + + command->IUnknown_iface.lpVtbl = &source_async_command_vtbl; + command->op = op; + + *ret = command; + + return S_OK; +} + +static HRESULT WINAPI callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static HRESULT WINAPI callback_GetParameters(IMFAsyncCallback *iface, + DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static ULONG WINAPI source_async_commands_callback_AddRef(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI source_async_commands_callback_Release(IMFAsyncCallback *iface) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static IMFStreamDescriptor *stream_descriptor_from_id(IMFPresentationDescriptor *pres_desc, DWORD id, BOOL *selected) +{ + ULONG sd_count; + IMFStreamDescriptor *ret; + unsigned int i; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorCount(pres_desc, &sd_count))) + return NULL; + + for (i = 0; i < sd_count; i++) + { + DWORD stream_id; + + if (FAILED(IMFPresentationDescriptor_GetStreamDescriptorByIndex(pres_desc, i, selected, &ret))) + return NULL; + + if (SUCCEEDED(IMFStreamDescriptor_GetStreamIdentifier(ret, &stream_id)) && stream_id == id) + return ret; + + IMFStreamDescriptor_Release(ret); + } + return NULL; +} + +static BOOL enqueue_token(struct media_stream *stream, IUnknown *token) +{ + if (stream->token_queue_count == stream->token_queue_cap) + { + IUnknown **buf; + stream->token_queue_cap = stream->token_queue_cap * 2 + 1; + buf = realloc(stream->token_queue, stream->token_queue_cap * sizeof(*buf)); + if (buf) + stream->token_queue = buf; + else + { + stream->token_queue_cap = stream->token_queue_count; + return FALSE; + } + } + stream->token_queue[stream->token_queue_count++] = token; + return TRUE; +} + +static void flush_token_queue(struct media_stream *stream, BOOL send) +{ + LONG i; + + for (i = 0; i < stream->token_queue_count; i++) + { + if (send) + { + HRESULT hr; + struct source_async_command *command; + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + { + command->u.request_sample.stream = stream; + command->u.request_sample.token = stream->token_queue[i]; + + hr = MFPutWorkItem(stream->parent_source->async_commands_queue, + &stream->parent_source->async_commands_callback, &command->IUnknown_iface); + } + if (FAILED(hr)) + WARN("Could not enqueue sample request, hr %#x\n", hr); + } + else if (stream->token_queue[i]) + IUnknown_Release(stream->token_queue[i]); + } + free(stream->token_queue); + stream->token_queue = NULL; + stream->token_queue_count = 0; + stream->token_queue_cap = 0; +} + +static void start_pipeline(struct media_source *source, struct source_async_command *command) +{ + PROPVARIANT *position = &command->u.start.position; + BOOL seek_message = source->state != SOURCE_STOPPED && position->vt != VT_EMPTY; + unsigned int i; + + /* seek to beginning on stop->play */ + if (source->state == SOURCE_STOPPED && position->vt == VT_EMPTY) + { + position->vt = VT_I8; + position->hVal.QuadPart = 0; + } + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream; + IMFStreamDescriptor *sd; + IMFMediaTypeHandler *mth; + IMFMediaType *current_mt; + DWORD stream_id; + BOOL was_active; + BOOL selected; + + stream = source->streams[i]; + + IMFStreamDescriptor_GetStreamIdentifier(stream->descriptor, &stream_id); + + sd = stream_descriptor_from_id(command->u.start.descriptor, stream_id, &selected); + IMFStreamDescriptor_Release(sd); + + was_active = stream->state != STREAM_INACTIVE; + + stream->state = selected ? STREAM_RUNNING : STREAM_INACTIVE; + + if (selected) + { + struct wg_format format; + + IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &mth); + IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt); + + mf_media_type_to_wg_format(current_mt, &format); + wg_parser_stream_enable(stream->wg_stream, &format, NULL, 0); + + IMFMediaType_Release(current_mt); + IMFMediaTypeHandler_Release(mth); + } + + if (position->vt != VT_EMPTY) + stream->eos = FALSE; + + if (selected) + { + TRACE("Stream %u (%p) selected\n", i, stream); + IMFMediaEventQueue_QueueEventParamUnk(source->event_queue, + was_active ? MEUpdatedStream : MENewStream, &GUID_NULL, + S_OK, (IUnknown*) &stream->IMFMediaStream_iface); + + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, + seek_message ? MEStreamSeeked : MEStreamStarted, &GUID_NULL, S_OK, position); + } + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, + seek_message ? MESourceSeeked : MESourceStarted, + &GUID_NULL, S_OK, position); + + source->state = SOURCE_RUNNING; + + if (position->vt == VT_I8) + wg_parser_stream_seek(source->streams[0]->wg_stream, 1.0, position->hVal.QuadPart, 0, + AM_SEEKING_AbsolutePositioning, AM_SEEKING_NoPositioning); + + for (i = 0; i < source->stream_count; i++) + flush_token_queue(source->streams[i], position->vt == VT_EMPTY); +} + +static void pause_pipeline(struct media_source *source) +{ + unsigned int i; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->state != STREAM_INACTIVE) + { + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamPaused, &GUID_NULL, S_OK, NULL); + } + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourcePaused, &GUID_NULL, S_OK, NULL); + + source->state = SOURCE_PAUSED; +} + +static void stop_pipeline(struct media_source *source) +{ + unsigned int i; + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + if (stream->state != STREAM_INACTIVE) + { + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEStreamStopped, &GUID_NULL, S_OK, NULL); + wg_parser_stream_disable(stream->wg_stream); + } + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MESourceStopped, &GUID_NULL, S_OK, NULL); + + source->state = SOURCE_STOPPED; + + for (i = 0; i < source->stream_count; i++) + flush_token_queue(source->streams[i], FALSE); +} + +static void dispatch_end_of_presentation(struct media_source *source) +{ + PROPVARIANT empty = {.vt = VT_EMPTY}; + unsigned int i; + + /* A stream has ended, check whether all have */ + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + + if (stream->state != STREAM_INACTIVE && !stream->eos) + return; + } + + IMFMediaEventQueue_QueueEventParamVar(source->event_queue, MEEndOfPresentation, &GUID_NULL, S_OK, &empty); +} + +static void send_buffer(struct media_stream *stream, const struct wg_parser_buffer *wg_buffer, IUnknown *token) +{ + IMFMediaBuffer *buffer; + IMFSample *sample; + HRESULT hr; + BYTE *data; + + if (FAILED(hr = MFCreateSample(&sample))) + { + ERR("Failed to create sample, hr %#x.\n", hr); + return; + } + + if (FAILED(hr = MFCreateMemoryBuffer(wg_buffer->size, &buffer))) + { + ERR("Failed to create buffer, hr %#x.\n", hr); + IMFSample_Release(sample); + return; + } + + if (FAILED(hr = IMFSample_AddBuffer(sample, buffer))) + { + ERR("Failed to add buffer, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = IMFMediaBuffer_SetCurrentLength(buffer, wg_buffer->size))) + { + ERR("Failed to set size, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = IMFMediaBuffer_Lock(buffer, &data, NULL, NULL))) + { + ERR("Failed to lock buffer, hr %#x.\n", hr); + goto out; + } + + if (!wg_parser_stream_copy_buffer(stream->wg_stream, data, 0, wg_buffer->size)) + { + wg_parser_stream_release_buffer(stream->wg_stream); + IMFMediaBuffer_Unlock(buffer); + goto out; + } + wg_parser_stream_release_buffer(stream->wg_stream); + + if (FAILED(hr = IMFMediaBuffer_Unlock(buffer))) + { + ERR("Failed to unlock buffer, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = IMFSample_SetSampleTime(sample, wg_buffer->pts))) + { + ERR("Failed to set sample time, hr %#x.\n", hr); + goto out; + } + + if (FAILED(hr = IMFSample_SetSampleDuration(sample, wg_buffer->duration))) + { + ERR("Failed to set sample duration, hr %#x.\n", hr); + goto out; + } + + if (token) + IMFSample_SetUnknown(sample, &MFSampleExtension_Token, token); + + IMFMediaEventQueue_QueueEventParamUnk(stream->event_queue, MEMediaSample, + &GUID_NULL, S_OK, (IUnknown *)sample); + +out: + IMFMediaBuffer_Release(buffer); + IMFSample_Release(sample); +} + +static void wait_on_sample(struct media_stream *stream, IUnknown *token) +{ + PROPVARIANT empty_var = {.vt = VT_EMPTY}; + struct wg_parser_buffer buffer; + + TRACE("%p, %p\n", stream, token); + + if (wg_parser_stream_get_buffer(stream->wg_stream, &buffer)) + { + send_buffer(stream, &buffer, token); + } + else + { + stream->eos = TRUE; + IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, MEEndOfStream, &GUID_NULL, S_OK, &empty_var); + dispatch_end_of_presentation(stream->parent_source); + } +} + +static HRESULT WINAPI source_async_commands_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct media_source *source = impl_from_async_commands_callback_IMFAsyncCallback(iface); + struct source_async_command *command; + IUnknown *state; + HRESULT hr; + + if (source->state == SOURCE_SHUTDOWN) + return S_OK; + + if (FAILED(hr = IMFAsyncResult_GetState(result, &state))) + return hr; + + command = impl_from_async_command_IUnknown(state); + switch (command->op) + { + case SOURCE_ASYNC_START: + start_pipeline(source, command); + break; + case SOURCE_ASYNC_PAUSE: + pause_pipeline(source); + break; + case SOURCE_ASYNC_STOP: + stop_pipeline(source); + break; + case SOURCE_ASYNC_REQUEST_SAMPLE: + if (source->state == SOURCE_PAUSED) + enqueue_token(command->u.request_sample.stream, command->u.request_sample.token); + else + wait_on_sample(command->u.request_sample.stream, command->u.request_sample.token); + break; + } + + IUnknown_Release(state); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl source_async_commands_callback_vtbl = +{ + callback_QueryInterface, + source_async_commands_callback_AddRef, + source_async_commands_callback_Release, + callback_GetParameters, + source_async_commands_Invoke, +}; + +static DWORD CALLBACK read_thread(void *arg) +{ + struct media_source *source = arg; + IMFByteStream *byte_stream = source->byte_stream; + size_t buffer_size = 4096; + uint64_t file_size; + void *data; + + if (!(data = malloc(buffer_size))) + return 0; + + IMFByteStream_GetLength(byte_stream, &file_size); + + TRACE("Starting read thread for media source %p.\n", source); + + while (!source->read_thread_shutdown) + { + uint64_t offset; + ULONG ret_size; + uint32_t size; + HRESULT hr; + + if (!wg_parser_get_next_read_offset(source->wg_parser, &offset, &size)) + continue; + + if (offset >= file_size) + size = 0; + else if (offset + size >= file_size) + size = file_size - offset; + + /* Some IMFByteStreams (including the standard file-based stream) return + * an error when reading past the file size. */ + if (!size) + { + wg_parser_push_data(source->wg_parser, WG_READ_SUCCESS, data, 0); + continue; + } + + if (!array_reserve(&data, &buffer_size, size, 1)) + { + free(data); + return 0; + } + + ret_size = 0; + + if (SUCCEEDED(hr = IMFByteStream_SetCurrentPosition(byte_stream, offset))) + hr = IMFByteStream_Read(byte_stream, data, size, &ret_size); + if (FAILED(hr)) + ERR("Failed to read %u bytes at offset %I64u, hr %#x.\n", size, offset, hr); + else if (ret_size != size) + ERR("Unexpected short read: requested %u bytes, got %u.\n", size, ret_size); + wg_parser_push_data(source->wg_parser, SUCCEEDED(hr) ? WG_READ_SUCCESS : WG_READ_FAILURE, data, ret_size); + } + + free(data); + TRACE("Media source is shutting down; exiting.\n"); + return 0; +} + +static HRESULT WINAPI media_stream_QueryInterface(IMFMediaStream *iface, REFIID riid, void **out) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaStream) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &stream->IMFMediaStream_iface; + } + else + { + FIXME("(%s, %p)\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_stream_AddRef(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedIncrement(&stream->ref); + + TRACE("%p, refcount %u.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI media_stream_Release(IMFMediaStream *iface) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + ULONG ref = InterlockedDecrement(&stream->ref); + + TRACE("%p, refcount %u.\n", iface, ref); + + if (!ref) + { + if (stream->event_queue) + IMFMediaEventQueue_Release(stream->event_queue); + flush_token_queue(stream, FALSE); + free(stream); + } + + return ref; +} + +static HRESULT WINAPI media_stream_GetEvent(IMFMediaStream *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %#x, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(stream->event_queue, flags, event); +} + +static HRESULT WINAPI media_stream_BeginGetEvent(IMFMediaStream *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(stream->event_queue, callback, state); +} + +static HRESULT WINAPI media_stream_EndGetEvent(IMFMediaStream *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p, %p.\n", stream, result, event); + + return IMFMediaEventQueue_EndGetEvent(stream->event_queue, result, event); +} + +static HRESULT WINAPI media_stream_QueueEvent(IMFMediaStream *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(stream->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_stream_GetMediaSource(IMFMediaStream *iface, IMFMediaSource **source) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p.\n", iface, source); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + IMFMediaSource_AddRef(&stream->parent_source->IMFMediaSource_iface); + *source = &stream->parent_source->IMFMediaSource_iface; + + return S_OK; +} + +static HRESULT WINAPI media_stream_GetStreamDescriptor(IMFMediaStream* iface, IMFStreamDescriptor **descriptor) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + + TRACE("%p, %p.\n", iface, descriptor); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + IMFStreamDescriptor_AddRef(stream->descriptor); + *descriptor = stream->descriptor; + + return S_OK; +} + +static HRESULT WINAPI media_stream_RequestSample(IMFMediaStream *iface, IUnknown *token) +{ + struct media_stream *stream = impl_from_IMFMediaStream(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p, %p.\n", iface, token); + + if (stream->state == STREAM_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (stream->state == STREAM_INACTIVE) + { + WARN("Stream isn't active\n"); + return MF_E_MEDIA_SOURCE_WRONGSTATE; + } + + if (stream->eos) + { + return MF_E_END_OF_STREAM; + } + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_REQUEST_SAMPLE, &command))) + { + command->u.request_sample.stream = stream; + if (token) + IUnknown_AddRef(token); + command->u.request_sample.token = token; + + hr = MFPutWorkItem(stream->parent_source->async_commands_queue, + &stream->parent_source->async_commands_callback, &command->IUnknown_iface); + } + + return hr; +} + +static const IMFMediaStreamVtbl media_stream_vtbl = +{ + media_stream_QueryInterface, + media_stream_AddRef, + media_stream_Release, + media_stream_GetEvent, + media_stream_BeginGetEvent, + media_stream_EndGetEvent, + media_stream_QueueEvent, + media_stream_GetMediaSource, + media_stream_GetStreamDescriptor, + media_stream_RequestSample +}; + +static HRESULT new_media_stream(struct media_source *source, + struct wg_parser_stream *wg_stream, DWORD stream_id, struct media_stream **out_stream) +{ + struct media_stream *object = calloc(1, sizeof(*object)); + HRESULT hr; + + TRACE("source %p, wg_stream %p, stream_id %u.\n", source, wg_stream, stream_id); + + object->IMFMediaStream_iface.lpVtbl = &media_stream_vtbl; + object->ref = 1; + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + { + free(object); + return hr; + } + + IMFMediaSource_AddRef(&source->IMFMediaSource_iface); + object->parent_source = source; + object->stream_id = stream_id; + + object->state = STREAM_INACTIVE; + object->eos = FALSE; + object->wg_stream = wg_stream; + + TRACE("Created stream object %p.\n", object); + + *out_stream = object; + + return S_OK; +} + +static HRESULT media_stream_init_desc(struct media_stream *stream) +{ + IMFMediaTypeHandler *type_handler = NULL; + IMFMediaType *stream_types[8]; + struct wg_format format; + DWORD type_count = 0; + unsigned int i; + HRESULT hr; + + wg_parser_stream_get_preferred_format(stream->wg_stream, &format); + + if (format.major_type == WG_MAJOR_TYPE_VIDEO) + { + /* These are the most common native output types of decoders: + https://docs.microsoft.com/en-us/windows/win32/medfound/mft-decoder-expose-output-types-in-native-order */ + static const GUID *const video_types[] = + { + &MFVideoFormat_NV12, + &MFVideoFormat_YV12, + &MFVideoFormat_YUY2, + &MFVideoFormat_IYUV, + &MFVideoFormat_I420, + &MFVideoFormat_ARGB32, + &MFVideoFormat_RGB32, + }; + + IMFMediaType *base_type = mf_media_type_from_wg_format(&format); + GUID base_subtype; + + IMFMediaType_GetGUID(base_type, &MF_MT_SUBTYPE, &base_subtype); + + stream_types[0] = base_type; + type_count = 1; + + for (i = 0; i < ARRAY_SIZE(video_types); i++) + { + IMFMediaType *new_type; + + if (IsEqualGUID(&base_subtype, video_types[i])) + continue; + + if (FAILED(hr = MFCreateMediaType(&new_type))) + goto done; + stream_types[type_count++] = new_type; + + if (FAILED(hr = IMFMediaType_CopyAllItems(base_type, (IMFAttributes *) new_type))) + goto done; + if (FAILED(hr = IMFMediaType_SetGUID(new_type, &MF_MT_SUBTYPE, video_types[i]))) + goto done; + } + } + else if (format.major_type == WG_MAJOR_TYPE_AUDIO) + { + /* Expose at least one PCM and one floating point type for the + consumer to pick from. */ + static const enum wg_audio_format audio_types[] = + { + WG_AUDIO_FORMAT_S16LE, + WG_AUDIO_FORMAT_F32LE, + }; + + stream_types[0] = mf_media_type_from_wg_format(&format); + type_count = 1; + + for (i = 0; i < ARRAY_SIZE(audio_types); i++) + { + struct wg_format new_format; + if (format.u.audio.format == audio_types[i]) + continue; + new_format = format; + new_format.u.audio.format = audio_types[i]; + stream_types[type_count++] = mf_media_type_from_wg_format(&new_format); + } + } + else + { + if ((stream_types[0] = mf_media_type_from_wg_format(&format))) + type_count = 1; + } + + assert(type_count <= ARRAY_SIZE(stream_types)); + + if (!type_count) + { + ERR("Failed to establish an IMFMediaType from any of the possible stream caps!\n"); + return E_FAIL; + } + + if (FAILED(hr = MFCreateStreamDescriptor(stream->stream_id, type_count, stream_types, &stream->descriptor))) + goto done; + + if (FAILED(hr = IMFStreamDescriptor_GetMediaTypeHandler(stream->descriptor, &type_handler))) + { + IMFStreamDescriptor_Release(stream->descriptor); + goto done; + } + + if (FAILED(hr = IMFMediaTypeHandler_SetCurrentMediaType(type_handler, stream_types[0]))) + { + IMFStreamDescriptor_Release(stream->descriptor); + goto done; + } + +done: + if (type_handler) + IMFMediaTypeHandler_Release(type_handler); + for (i = 0; i < type_count; i++) + IMFMediaType_Release(stream_types[i]); + return hr; +} + +static HRESULT WINAPI media_source_get_service_QueryInterface(IMFGetService *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_get_service_AddRef(IMFGetService *iface) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_get_service_Release(IMFGetService *iface) +{ + struct media_source *source = impl_from_IMFGetService(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_get_service_GetService(IMFGetService *iface, REFGUID service, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFGetService(iface); + + TRACE("%p, %s, %s, %p.\n", iface, debugstr_guid(service), debugstr_guid(riid), obj); + + *obj = NULL; + + if (IsEqualGUID(service, &MF_RATE_CONTROL_SERVICE)) + { + if (IsEqualIID(riid, &IID_IMFRateSupport)) + { + *obj = &source->IMFRateSupport_iface; + } + else if (IsEqualIID(riid, &IID_IMFRateControl)) + { + *obj = &source->IMFRateControl_iface; + } + } + else + FIXME("Unsupported service %s.\n", debugstr_guid(service)); + + if (*obj) + IUnknown_AddRef((IUnknown *)*obj); + + return *obj ? S_OK : E_NOINTERFACE; +} + +static const IMFGetServiceVtbl media_source_get_service_vtbl = +{ + media_source_get_service_QueryInterface, + media_source_get_service_AddRef, + media_source_get_service_Release, + media_source_get_service_GetService, +}; + +static HRESULT WINAPI media_source_rate_support_QueryInterface(IMFRateSupport *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_rate_support_AddRef(IMFRateSupport *iface) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_rate_support_Release(IMFRateSupport *iface) +{ + struct media_source *source = impl_from_IMFRateSupport(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_rate_support_GetSlowestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) +{ + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + *rate = 0.0f; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_support_GetFastestRate(IMFRateSupport *iface, MFRATE_DIRECTION direction, BOOL thin, float *rate) +{ + TRACE("%p, %d, %d, %p.\n", iface, direction, thin, rate); + + *rate = direction == MFRATE_FORWARD ? 1e6f : -1e6f; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_support_IsRateSupported(IMFRateSupport *iface, BOOL thin, float rate, + float *nearest_rate) +{ + TRACE("%p, %d, %f, %p.\n", iface, thin, rate, nearest_rate); + + if (nearest_rate) + *nearest_rate = rate; + + return rate >= -1e6f && rate <= 1e6f ? S_OK : MF_E_UNSUPPORTED_RATE; +} + +static const IMFRateSupportVtbl media_source_rate_support_vtbl = +{ + media_source_rate_support_QueryInterface, + media_source_rate_support_AddRef, + media_source_rate_support_Release, + media_source_rate_support_GetSlowestRate, + media_source_rate_support_GetFastestRate, + media_source_rate_support_IsRateSupported, +}; + +static HRESULT WINAPI media_source_rate_control_QueryInterface(IMFRateControl *iface, REFIID riid, void **obj) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_QueryInterface(&source->IMFMediaSource_iface, riid, obj); +} + +static ULONG WINAPI media_source_rate_control_AddRef(IMFRateControl *iface) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_AddRef(&source->IMFMediaSource_iface); +} + +static ULONG WINAPI media_source_rate_control_Release(IMFRateControl *iface) +{ + struct media_source *source = impl_from_IMFRateControl(iface); + return IMFMediaSource_Release(&source->IMFMediaSource_iface); +} + +static HRESULT WINAPI media_source_rate_control_SetRate(IMFRateControl *iface, BOOL thin, float rate) +{ + FIXME("%p, %d, %f.\n", iface, thin, rate); + + if (rate < 0.0f) + return MF_E_REVERSE_UNSUPPORTED; + + if (thin) + return MF_E_THINNING_UNSUPPORTED; + + if (rate != 1.0f) + return MF_E_UNSUPPORTED_RATE; + + return S_OK; +} + +static HRESULT WINAPI media_source_rate_control_GetRate(IMFRateControl *iface, BOOL *thin, float *rate) +{ + TRACE("%p, %p, %p.\n", iface, thin, rate); + + if (thin) + *thin = FALSE; + + *rate = 1.0f; + + return S_OK; +} + +static const IMFRateControlVtbl media_source_rate_control_vtbl = +{ + media_source_rate_control_QueryInterface, + media_source_rate_control_AddRef, + media_source_rate_control_Release, + media_source_rate_control_SetRate, + media_source_rate_control_GetRate, +}; + +static HRESULT WINAPI media_source_QueryInterface(IMFMediaSource *iface, REFIID riid, void **out) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), out); + + if (IsEqualIID(riid, &IID_IMFMediaSource) || + IsEqualIID(riid, &IID_IMFMediaEventGenerator) || + IsEqualIID(riid, &IID_IUnknown)) + { + *out = &source->IMFMediaSource_iface; + } + else if (IsEqualIID(riid, &IID_IMFGetService)) + { + *out = &source->IMFGetService_iface; + } + else + { + FIXME("%s, %p.\n", debugstr_guid(riid), out); + *out = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*out); + return S_OK; +} + +static ULONG WINAPI media_source_AddRef(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedIncrement(&source->ref); + + TRACE("%p, refcount %u.\n", iface, ref); + + return ref; +} + +static ULONG WINAPI media_source_Release(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + ULONG ref = InterlockedDecrement(&source->ref); + + TRACE("%p, refcount %u.\n", iface, ref); + + if (!ref) + { + IMFMediaSource_Shutdown(&source->IMFMediaSource_iface); + IMFMediaEventQueue_Release(source->event_queue); + free(source); + } + + return ref; +} + +static HRESULT WINAPI media_source_GetEvent(IMFMediaSource *iface, DWORD flags, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %#x, %p.\n", iface, flags, event); + + return IMFMediaEventQueue_GetEvent(source->event_queue, flags, event); +} + +static HRESULT WINAPI media_source_BeginGetEvent(IMFMediaSource *iface, IMFAsyncCallback *callback, IUnknown *state) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p, %p.\n", iface, callback, state); + + return IMFMediaEventQueue_BeginGetEvent(source->event_queue, callback, state); +} + +static HRESULT WINAPI media_source_EndGetEvent(IMFMediaSource *iface, IMFAsyncResult *result, IMFMediaEvent **event) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p, %p.\n", iface, result, event); + + return IMFMediaEventQueue_EndGetEvent(source->event_queue, result, event); +} + +static HRESULT WINAPI media_source_QueueEvent(IMFMediaSource *iface, MediaEventType event_type, REFGUID ext_type, + HRESULT hr, const PROPVARIANT *value) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %d, %s, %#x, %p.\n", iface, event_type, debugstr_guid(ext_type), hr, value); + + return IMFMediaEventQueue_QueueEventParamVar(source->event_queue, event_type, ext_type, hr, value); +} + +static HRESULT WINAPI media_source_GetCharacteristics(IMFMediaSource *iface, DWORD *characteristics) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p.\n", iface, characteristics); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + *characteristics = MFMEDIASOURCE_CAN_SEEK | MFMEDIASOURCE_CAN_PAUSE; + + return S_OK; +} + +static HRESULT WINAPI media_source_CreatePresentationDescriptor(IMFMediaSource *iface, IMFPresentationDescriptor **descriptor) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + + TRACE("%p, %p.\n", iface, descriptor); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + return IMFPresentationDescriptor_Clone(source->pres_desc, descriptor); +} + +static HRESULT WINAPI media_source_Start(IMFMediaSource *iface, IMFPresentationDescriptor *descriptor, + const GUID *time_format, const PROPVARIANT *position) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, descriptor, time_format, position); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (!(IsEqualIID(time_format, &GUID_NULL))) + return MF_E_UNSUPPORTED_TIME_FORMAT; + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_START, &command))) + { + command->u.start.descriptor = descriptor; + command->u.start.format = *time_format; + PropVariantCopy(&command->u.start.position, position); + + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + } + + return hr; +} + +static HRESULT WINAPI media_source_Stop(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p.\n", iface); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_STOP, &command))) + hr = MFPutWorkItem(source->async_commands_queue, &source->async_commands_callback, &command->IUnknown_iface); + + return hr; +} + +static HRESULT WINAPI media_source_Pause(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + struct source_async_command *command; + HRESULT hr; + + TRACE("%p.\n", iface); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + if (source->state != SOURCE_RUNNING) + return MF_E_INVALID_STATE_TRANSITION; + + if (SUCCEEDED(hr = source_create_async_op(SOURCE_ASYNC_PAUSE, &command))) + hr = MFPutWorkItem(source->async_commands_queue, + &source->async_commands_callback, &command->IUnknown_iface); + + return S_OK; +} + +static HRESULT WINAPI media_source_Shutdown(IMFMediaSource *iface) +{ + struct media_source *source = impl_from_IMFMediaSource(iface); + unsigned int i; + + TRACE("%p.\n", iface); + + if (source->state == SOURCE_SHUTDOWN) + return MF_E_SHUTDOWN; + + source->state = SOURCE_SHUTDOWN; + + wg_parser_disconnect(source->wg_parser); + + source->read_thread_shutdown = true; + WaitForSingleObject(source->read_thread, INFINITE); + CloseHandle(source->read_thread); + + IMFPresentationDescriptor_Release(source->pres_desc); + IMFMediaEventQueue_Shutdown(source->event_queue); + IMFByteStream_Release(source->byte_stream); + + for (i = 0; i < source->stream_count; i++) + { + struct media_stream *stream = source->streams[i]; + + stream->state = STREAM_SHUTDOWN; + + IMFMediaEventQueue_Shutdown(stream->event_queue); + IMFStreamDescriptor_Release(stream->descriptor); + IMFMediaSource_Release(&stream->parent_source->IMFMediaSource_iface); + + IMFMediaStream_Release(&stream->IMFMediaStream_iface); + } + + wg_parser_destroy(source->wg_parser); + + free(source->streams); + + MFUnlockWorkQueue(source->async_commands_queue); + + return S_OK; +} + +static const IMFMediaSourceVtbl IMFMediaSource_vtbl = +{ + media_source_QueryInterface, + media_source_AddRef, + media_source_Release, + media_source_GetEvent, + media_source_BeginGetEvent, + media_source_EndGetEvent, + media_source_QueueEvent, + media_source_GetCharacteristics, + media_source_CreatePresentationDescriptor, + media_source_Start, + media_source_Stop, + media_source_Pause, + media_source_Shutdown, +}; + +static HRESULT media_source_constructor(IMFByteStream *bytestream, struct media_source **out_media_source) +{ + BOOL video_selected = FALSE, audio_selected = FALSE; + IMFStreamDescriptor **descriptors = NULL; + unsigned int stream_count = UINT_MAX; + struct media_source *object; + UINT64 total_pres_time = 0; + struct wg_parser *parser; + DWORD bytestream_caps; + uint64_t file_size; + unsigned int i; + HRESULT hr; + + if (FAILED(hr = IMFByteStream_GetCapabilities(bytestream, &bytestream_caps))) + return hr; + + if (!(bytestream_caps & MFBYTESTREAM_IS_SEEKABLE)) + { + FIXME("Non-seekable bytestreams not supported.\n"); + return MF_E_BYTESTREAM_NOT_SEEKABLE; + } + + if (FAILED(hr = IMFByteStream_GetLength(bytestream, &file_size))) + { + FIXME("Failed to get byte stream length, hr %#x.\n", hr); + return hr; + } + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFMediaSource_iface.lpVtbl = &IMFMediaSource_vtbl; + object->IMFGetService_iface.lpVtbl = &media_source_get_service_vtbl; + object->IMFRateSupport_iface.lpVtbl = &media_source_rate_support_vtbl; + object->IMFRateControl_iface.lpVtbl = &media_source_rate_control_vtbl; + object->async_commands_callback.lpVtbl = &source_async_commands_callback_vtbl; + object->ref = 1; + object->byte_stream = bytestream; + IMFByteStream_AddRef(bytestream); + + if (FAILED(hr = MFCreateEventQueue(&object->event_queue))) + goto fail; + + if (FAILED(hr = MFAllocateWorkQueue(&object->async_commands_queue))) + goto fail; + + /* In Media Foundation, sources may read from any media source stream + * without fear of blocking due to buffering limits on another. Trailmakers, + * a Unity3D Engine game, only reads one sample from the audio stream (and + * never deselects it). Remove buffering limits from decodebin in order to + * account for this. Note that this does leak memory, but the same memory + * leak occurs with native. */ + if (!(parser = wg_parser_create(WG_PARSER_DECODEBIN, true))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + object->wg_parser = parser; + + object->read_thread = CreateThread(NULL, 0, read_thread, object, 0, NULL); + + object->state = SOURCE_OPENING; + + if (FAILED(hr = wg_parser_connect(parser, file_size))) + goto fail; + + stream_count = wg_parser_get_stream_count(parser); + + if (!(object->streams = calloc(stream_count, sizeof(*object->streams)))) + { + hr = E_OUTOFMEMORY; + goto fail; + } + + for (i = 0; i < stream_count; ++i) + { + if (FAILED(hr = new_media_stream(object, wg_parser_get_stream(parser, i), i, &object->streams[i]))) + goto fail; + + if (FAILED(hr = media_stream_init_desc(object->streams[i]))) + { + ERR("Failed to finish initialization of media stream %p, hr %x.\n", object->streams[i], hr); + IMFMediaSource_Release(&object->streams[i]->parent_source->IMFMediaSource_iface); + IMFMediaEventQueue_Release(object->streams[i]->event_queue); + free(object->streams[i]); + goto fail; + } + + object->stream_count++; + } + + /* init presentation descriptor */ + + descriptors = malloc(object->stream_count * sizeof(IMFStreamDescriptor *)); + for (i = 0; i < object->stream_count; i++) + { + IMFStreamDescriptor **descriptor = &descriptors[object->stream_count - 1 - i]; + char language[128]; + DWORD language_len; + WCHAR *languageW; + + IMFMediaStream_GetStreamDescriptor(&object->streams[i]->IMFMediaStream_iface, descriptor); + + if (wg_parser_stream_get_language(object->streams[i]->wg_stream, language, sizeof(language))) + { + if ((language_len = MultiByteToWideChar(CP_UTF8, 0, language, -1, NULL, 0))) + { + languageW = malloc(language_len * sizeof(WCHAR)); + if (MultiByteToWideChar(CP_UTF8, 0, language, -1, languageW, language_len)) + { + IMFStreamDescriptor_SetString(*descriptor, &MF_SD_LANGUAGE, languageW); + } + free(languageW); + } + } + } + + if (FAILED(hr = MFCreatePresentationDescriptor(object->stream_count, descriptors, &object->pres_desc))) + goto fail; + + /* Select one of each major type. */ + for (i = 0; i < object->stream_count; i++) + { + IMFMediaTypeHandler *handler; + GUID major_type; + BOOL select_stream = FALSE; + + IMFStreamDescriptor_GetMediaTypeHandler(descriptors[i], &handler); + IMFMediaTypeHandler_GetMajorType(handler, &major_type); + if (IsEqualGUID(&major_type, &MFMediaType_Video) && !video_selected) + { + select_stream = TRUE; + video_selected = TRUE; + } + if (IsEqualGUID(&major_type, &MFMediaType_Audio) && !audio_selected) + { + select_stream = TRUE; + audio_selected = TRUE; + } + if (select_stream) + IMFPresentationDescriptor_SelectStream(object->pres_desc, i); + IMFMediaTypeHandler_Release(handler); + IMFStreamDescriptor_Release(descriptors[i]); + } + free(descriptors); + descriptors = NULL; + + for (i = 0; i < object->stream_count; i++) + total_pres_time = max(total_pres_time, + wg_parser_stream_get_duration(object->streams[i]->wg_stream)); + + if (object->stream_count) + IMFPresentationDescriptor_SetUINT64(object->pres_desc, &MF_PD_DURATION, total_pres_time); + + object->state = SOURCE_STOPPED; + + *out_media_source = object; + return S_OK; + + fail: + WARN("Failed to construct MFMediaSource, hr %#x.\n", hr); + + if (descriptors) + { + for (i = 0; i < object->stream_count; i++) + IMFStreamDescriptor_Release(descriptors[i]); + free(descriptors); + } + for (i = 0; i < object->stream_count; i++) + { + struct media_stream *stream = object->streams[i]; + + IMFMediaEventQueue_Release(stream->event_queue); + IMFStreamDescriptor_Release(stream->descriptor); + IMFMediaSource_Release(&stream->parent_source->IMFMediaSource_iface); + + free(stream); + } + free(object->streams); + if (stream_count != UINT_MAX) + wg_parser_disconnect(object->wg_parser); + if (object->read_thread) + { + object->read_thread_shutdown = true; + WaitForSingleObject(object->read_thread, INFINITE); + CloseHandle(object->read_thread); + } + if (object->wg_parser) + wg_parser_destroy(object->wg_parser); + if (object->async_commands_queue) + MFUnlockWorkQueue(object->async_commands_queue); + if (object->event_queue) + IMFMediaEventQueue_Release(object->event_queue); + IMFByteStream_Release(object->byte_stream); + free(object); + return hr; +} + +struct winegstreamer_stream_handler_result +{ + struct list entry; + IMFAsyncResult *result; + MF_OBJECT_TYPE obj_type; + IUnknown *object; +}; + +struct winegstreamer_stream_handler +{ + IMFByteStreamHandler IMFByteStreamHandler_iface; + IMFAsyncCallback IMFAsyncCallback_iface; + LONG refcount; + struct list results; + CRITICAL_SECTION cs; +}; + +static struct winegstreamer_stream_handler *impl_from_IMFByteStreamHandler(IMFByteStreamHandler *iface) +{ + return CONTAINING_RECORD(iface, struct winegstreamer_stream_handler, IMFByteStreamHandler_iface); +} + +static struct winegstreamer_stream_handler *impl_from_IMFAsyncCallback(IMFAsyncCallback *iface) +{ + return CONTAINING_RECORD(iface, struct winegstreamer_stream_handler, IMFAsyncCallback_iface); +} + +static HRESULT WINAPI winegstreamer_stream_handler_QueryInterface(IMFByteStreamHandler *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFByteStreamHandler) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFByteStreamHandler_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI winegstreamer_stream_handler_AddRef(IMFByteStreamHandler *iface) +{ + struct winegstreamer_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedIncrement(&handler->refcount); + + TRACE("%p, refcount %u.\n", handler, refcount); + + return refcount; +} + +static ULONG WINAPI winegstreamer_stream_handler_Release(IMFByteStreamHandler *iface) +{ + struct winegstreamer_stream_handler *handler = impl_from_IMFByteStreamHandler(iface); + ULONG refcount = InterlockedDecrement(&handler->refcount); + struct winegstreamer_stream_handler_result *result, *next; + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + LIST_FOR_EACH_ENTRY_SAFE(result, next, &handler->results, struct winegstreamer_stream_handler_result, entry) + { + list_remove(&result->entry); + IMFAsyncResult_Release(result->result); + if (result->object) + IUnknown_Release(result->object); + free(result); + } + DeleteCriticalSection(&handler->cs); + free(handler); + } + + return refcount; +} + +struct create_object_context +{ + IUnknown IUnknown_iface; + LONG refcount; + + IPropertyStore *props; + IMFByteStream *stream; + WCHAR *url; + DWORD flags; +}; + +static struct create_object_context *impl_from_IUnknown(IUnknown *iface) +{ + return CONTAINING_RECORD(iface, struct create_object_context, IUnknown_iface); +} + +static HRESULT WINAPI create_object_context_QueryInterface(IUnknown *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IUnknown_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI create_object_context_AddRef(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedIncrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI create_object_context_Release(IUnknown *iface) +{ + struct create_object_context *context = impl_from_IUnknown(iface); + ULONG refcount = InterlockedDecrement(&context->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (context->props) + IPropertyStore_Release(context->props); + if (context->stream) + IMFByteStream_Release(context->stream); + free(context->url); + free(context); + } + + return refcount; +} + +static const IUnknownVtbl create_object_context_vtbl = +{ + create_object_context_QueryInterface, + create_object_context_AddRef, + create_object_context_Release, +}; + +static HRESULT WINAPI winegstreamer_stream_handler_BeginCreateObject(IMFByteStreamHandler *iface, IMFByteStream *stream, const WCHAR *url, DWORD flags, + IPropertyStore *props, IUnknown **cancel_cookie, IMFAsyncCallback *callback, IUnknown *state) +{ + struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct create_object_context *context; + IMFAsyncResult *caller, *item; + HRESULT hr; + + TRACE("%p, %s, %#x, %p, %p, %p, %p.\n", iface, debugstr_w(url), flags, props, cancel_cookie, callback, state); + + if (cancel_cookie) + *cancel_cookie = NULL; + + if (FAILED(hr = MFCreateAsyncResult(NULL, callback, state, &caller))) + return hr; + + if (!(context = calloc(1, sizeof(*context)))) + { + IMFAsyncResult_Release(caller); + return E_OUTOFMEMORY; + } + + context->IUnknown_iface.lpVtbl = &create_object_context_vtbl; + context->refcount = 1; + context->props = props; + if (context->props) + IPropertyStore_AddRef(context->props); + context->flags = flags; + context->stream = stream; + if (context->stream) + IMFByteStream_AddRef(context->stream); + if (url) + context->url = wcsdup(url); + if (!context->stream) + { + IMFAsyncResult_Release(caller); + IUnknown_Release(&context->IUnknown_iface); + return E_OUTOFMEMORY; + } + + hr = MFCreateAsyncResult(&context->IUnknown_iface, &this->IMFAsyncCallback_iface, (IUnknown *)caller, &item); + IUnknown_Release(&context->IUnknown_iface); + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = MFPutWorkItemEx(MFASYNC_CALLBACK_QUEUE_IO, item))) + { + if (cancel_cookie) + { + *cancel_cookie = (IUnknown *)caller; + IUnknown_AddRef(*cancel_cookie); + } + } + + IMFAsyncResult_Release(item); + } + IMFAsyncResult_Release(caller); + + return hr; +} + +static HRESULT WINAPI winegstreamer_stream_handler_EndCreateObject(IMFByteStreamHandler *iface, IMFAsyncResult *result, + MF_OBJECT_TYPE *obj_type, IUnknown **object) +{ + struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct winegstreamer_stream_handler_result *found = NULL, *cur; + HRESULT hr; + + TRACE("%p, %p, %p, %p.\n", iface, result, obj_type, object); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct winegstreamer_stream_handler_result, entry) + { + if (result == cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + *obj_type = found->obj_type; + *object = found->object; + hr = IMFAsyncResult_GetStatus(found->result); + IMFAsyncResult_Release(found->result); + free(found); + } + else + { + *obj_type = MF_OBJECT_INVALID; + *object = NULL; + hr = MF_E_UNEXPECTED; + } + + return hr; +} + +static HRESULT WINAPI winegstreamer_stream_handler_CancelObjectCreation(IMFByteStreamHandler *iface, IUnknown *cancel_cookie) +{ + struct winegstreamer_stream_handler *this = impl_from_IMFByteStreamHandler(iface); + struct winegstreamer_stream_handler_result *found = NULL, *cur; + + TRACE("%p, %p.\n", iface, cancel_cookie); + + EnterCriticalSection(&this->cs); + + LIST_FOR_EACH_ENTRY(cur, &this->results, struct winegstreamer_stream_handler_result, entry) + { + if (cancel_cookie == (IUnknown *)cur->result) + { + list_remove(&cur->entry); + found = cur; + break; + } + } + + LeaveCriticalSection(&this->cs); + + if (found) + { + IMFAsyncResult_Release(found->result); + if (found->object) + IUnknown_Release(found->object); + free(found); + } + + return found ? S_OK : MF_E_UNEXPECTED; +} + +static HRESULT WINAPI winegstreamer_stream_handler_GetMaxNumberOfBytesRequiredForResolution(IMFByteStreamHandler *iface, QWORD *bytes) +{ + FIXME("stub (%p %p)\n", iface, bytes); + return E_NOTIMPL; +} + +static const IMFByteStreamHandlerVtbl winegstreamer_stream_handler_vtbl = +{ + winegstreamer_stream_handler_QueryInterface, + winegstreamer_stream_handler_AddRef, + winegstreamer_stream_handler_Release, + winegstreamer_stream_handler_BeginCreateObject, + winegstreamer_stream_handler_EndCreateObject, + winegstreamer_stream_handler_CancelObjectCreation, + winegstreamer_stream_handler_GetMaxNumberOfBytesRequiredForResolution, +}; + +static HRESULT WINAPI winegstreamer_stream_handler_callback_QueryInterface(IMFAsyncCallback *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IMFAsyncCallback) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFAsyncCallback_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI winegstreamer_stream_handler_callback_AddRef(IMFAsyncCallback *iface) +{ + struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_AddRef(&handler->IMFByteStreamHandler_iface); +} + +static ULONG WINAPI winegstreamer_stream_handler_callback_Release(IMFAsyncCallback *iface) +{ + struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + return IMFByteStreamHandler_Release(&handler->IMFByteStreamHandler_iface); +} + +static HRESULT WINAPI winegstreamer_stream_handler_callback_GetParameters(IMFAsyncCallback *iface, DWORD *flags, DWORD *queue) +{ + return E_NOTIMPL; +} + +static HRESULT winegstreamer_stream_handler_create_object(struct winegstreamer_stream_handler *This, WCHAR *url, IMFByteStream *stream, DWORD flags, + IPropertyStore *props, IUnknown **out_object, MF_OBJECT_TYPE *out_obj_type) +{ + TRACE("%p, %s, %p, %u, %p, %p, %p.\n", This, debugstr_w(url), stream, flags, props, out_object, out_obj_type); + + if (flags & MF_RESOLUTION_MEDIASOURCE) + { + HRESULT hr; + struct media_source *new_source; + + if (FAILED(hr = media_source_constructor(stream, &new_source))) + return hr; + + TRACE("->(%p)\n", new_source); + + *out_object = (IUnknown*)&new_source->IMFMediaSource_iface; + *out_obj_type = MF_OBJECT_MEDIASOURCE; + + return S_OK; + } + else + { + FIXME("flags = %08x\n", flags); + return E_NOTIMPL; + } +} + +static HRESULT WINAPI winegstreamer_stream_handler_callback_Invoke(IMFAsyncCallback *iface, IMFAsyncResult *result) +{ + struct winegstreamer_stream_handler *handler = impl_from_IMFAsyncCallback(iface); + struct winegstreamer_stream_handler_result *handler_result; + MF_OBJECT_TYPE obj_type = MF_OBJECT_INVALID; + IUnknown *object = NULL, *context_object; + struct create_object_context *context; + IMFAsyncResult *caller; + HRESULT hr; + + caller = (IMFAsyncResult *)IMFAsyncResult_GetStateNoAddRef(result); + + if (FAILED(hr = IMFAsyncResult_GetObject(result, &context_object))) + { + WARN("Expected context set for callee result.\n"); + return hr; + } + + context = impl_from_IUnknown(context_object); + + hr = winegstreamer_stream_handler_create_object(handler, context->url, context->stream, context->flags, context->props, &object, &obj_type); + + if ((handler_result = malloc(sizeof(*handler_result)))) + { + handler_result->result = caller; + IMFAsyncResult_AddRef(handler_result->result); + handler_result->obj_type = obj_type; + handler_result->object = object; + + EnterCriticalSection(&handler->cs); + list_add_tail(&handler->results, &handler_result->entry); + LeaveCriticalSection(&handler->cs); + } + else + { + if (object) + IUnknown_Release(object); + hr = E_OUTOFMEMORY; + } + + IUnknown_Release(&context->IUnknown_iface); + + IMFAsyncResult_SetStatus(caller, hr); + MFInvokeCallback(caller); + + return S_OK; +} + +static const IMFAsyncCallbackVtbl winegstreamer_stream_handler_callback_vtbl = +{ + winegstreamer_stream_handler_callback_QueryInterface, + winegstreamer_stream_handler_callback_AddRef, + winegstreamer_stream_handler_callback_Release, + winegstreamer_stream_handler_callback_GetParameters, + winegstreamer_stream_handler_callback_Invoke, +}; + +HRESULT winegstreamer_stream_handler_create(REFIID riid, void **obj) +{ + struct winegstreamer_stream_handler *this; + HRESULT hr; + + TRACE("%s, %p.\n", debugstr_guid(riid), obj); + + if (!(this = calloc(1, sizeof(*this)))) + return E_OUTOFMEMORY; + + list_init(&this->results); + InitializeCriticalSection(&this->cs); + + this->IMFByteStreamHandler_iface.lpVtbl = &winegstreamer_stream_handler_vtbl; + this->IMFAsyncCallback_iface.lpVtbl = &winegstreamer_stream_handler_callback_vtbl; + this->refcount = 1; + + hr = IMFByteStreamHandler_QueryInterface(&this->IMFByteStreamHandler_iface, riid, obj); + IMFByteStreamHandler_Release(&this->IMFByteStreamHandler_iface); + + return hr; +} diff --git a/dlls/mfplat/mediatype.c b/dlls/mfplat/mediatype.c index b36a44d1e1c..4f24ccbd237 100644 --- wine/dlls/mfplat/mediatype.c +++ wine/dlls/mfplat/mediatype.c @@ -25,6 +25,8 @@ #include "ks.h" #include "ksmedia.h" +#include "wine/debug.h" + WINE_DEFAULT_DEBUG_CHANNEL(mfplat); DEFINE_MEDIATYPE_GUID(MFVideoFormat_IMC1, MAKEFOURCC('I','M','C','1')); @@ -138,7 +140,7 @@ static ULONG WINAPI mediatype_AddRef(IMFMediaType *iface) struct media_type *media_type = impl_from_IMFMediaType(iface); ULONG refcount = InterlockedIncrement(&media_type->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -148,7 +150,7 @@ static ULONG WINAPI mediatype_Release(IMFMediaType *iface) struct media_type *media_type = impl_from_IMFMediaType(iface); ULONG refcount = InterlockedDecrement(&media_type->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -976,9 +978,8 @@ static const MFVIDEOFORMAT * WINAPI video_mediatype_GetVideoFormat(IMFVideoMedia TRACE("%p.\n", iface); CoTaskMemFree(media_type->video_format); - media_type->video_format = NULL; if (FAILED(hr = MFCreateMFVideoFormatFromMFMediaType(&media_type->IMFMediaType_iface, &media_type->video_format, &size))) - WARN("Failed to create format description, hr %#lx.\n", hr); + WARN("Failed to create format description, hr %#x.\n", hr); return media_type->video_format; } @@ -986,7 +987,7 @@ static const MFVIDEOFORMAT * WINAPI video_mediatype_GetVideoFormat(IMFVideoMedia static HRESULT WINAPI video_mediatype_GetVideoRepresentation(IMFVideoMediaType *iface, GUID representation, void **data, LONG stride) { - FIXME("%p, %s, %p, %ld.\n", iface, debugstr_guid(&representation), data, stride); + FIXME("%p, %s, %p, %d.\n", iface, debugstr_guid(&representation), data, stride); return E_NOTIMPL; } @@ -1377,11 +1378,10 @@ static const WAVEFORMATEX * WINAPI audio_mediatype_GetAudioFormat(IMFAudioMediaT TRACE("%p.\n", iface); CoTaskMemFree(media_type->audio_format); - media_type->audio_format = NULL; if (FAILED(hr = MFCreateWaveFormatExFromMFMediaType(&media_type->IMFMediaType_iface, &media_type->audio_format, &size, MFWaveFormatExConvertFlag_Normal))) { - WARN("Failed to create wave format description, hr %#lx.\n", hr); + WARN("Failed to create wave format description, hr %#x.\n", hr); } return media_type->audio_format; @@ -1498,7 +1498,7 @@ static ULONG WINAPI stream_descriptor_AddRef(IMFStreamDescriptor *iface) struct stream_desc *stream_desc = impl_from_IMFStreamDescriptor(iface); ULONG refcount = InterlockedIncrement(&stream_desc->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -1509,7 +1509,7 @@ static ULONG WINAPI stream_descriptor_Release(IMFStreamDescriptor *iface) ULONG refcount = InterlockedDecrement(&stream_desc->attributes.ref); unsigned int i; - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -1955,7 +1955,7 @@ static HRESULT WINAPI mediatype_handler_GetMediaTypeByIndex(IMFMediaTypeHandler { struct stream_desc *stream_desc = impl_from_IMFMediaTypeHandler(iface); - TRACE("%p, %lu, %p.\n", iface, index, type); + TRACE("%p, %u, %p.\n", iface, index, type); if (index >= stream_desc->media_types_count) return MF_E_NO_MORE_TYPES; @@ -2046,7 +2046,7 @@ HRESULT WINAPI MFCreateStreamDescriptor(DWORD identifier, DWORD count, unsigned int i; HRESULT hr; - TRACE("%ld, %ld, %p, %p.\n", identifier, count, types, descriptor); + TRACE("%d, %d, %p, %p.\n", identifier, count, types, descriptor); if (!count) return E_INVALIDARG; @@ -2104,7 +2104,7 @@ static ULONG WINAPI presentation_descriptor_AddRef(IMFPresentationDescriptor *if struct presentation_desc *presentation_desc = impl_from_IMFPresentationDescriptor(iface); ULONG refcount = InterlockedIncrement(&presentation_desc->attributes.ref); - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } @@ -2115,7 +2115,7 @@ static ULONG WINAPI presentation_descriptor_Release(IMFPresentationDescriptor *i ULONG refcount = InterlockedDecrement(&presentation_desc->attributes.ref); unsigned int i; - TRACE("%p, refcount %lu.\n", iface, refcount); + TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { @@ -2433,7 +2433,7 @@ static HRESULT WINAPI presentation_descriptor_GetStreamDescriptorByIndex(IMFPres { struct presentation_desc *presentation_desc = impl_from_IMFPresentationDescriptor(iface); - TRACE("%p, %lu, %p, %p.\n", iface, index, selected, descriptor); + TRACE("%p, %u, %p, %p.\n", iface, index, selected, descriptor); if (index >= presentation_desc->count) return E_INVALIDARG; @@ -2452,7 +2452,7 @@ static HRESULT WINAPI presentation_descriptor_SelectStream(IMFPresentationDescri { struct presentation_desc *presentation_desc = impl_from_IMFPresentationDescriptor(iface); - TRACE("%p, %lu.\n", iface, index); + TRACE("%p, %u.\n", iface, index); if (index >= presentation_desc->count) return E_INVALIDARG; @@ -2468,7 +2468,7 @@ static HRESULT WINAPI presentation_descriptor_DeselectStream(IMFPresentationDesc { struct presentation_desc *presentation_desc = impl_from_IMFPresentationDescriptor(iface); - TRACE("%p, %lu.\n", iface, index); + TRACE("%p, %u.\n", iface, index); if (index >= presentation_desc->count) return E_INVALIDARG; @@ -2580,7 +2580,7 @@ HRESULT WINAPI MFCreatePresentationDescriptor(DWORD count, IMFStreamDescriptor * unsigned int i; HRESULT hr; - TRACE("%lu, %p, %p.\n", count, descriptors, out); + TRACE("%u, %p, %p.\n", count, descriptors, out); if (!count) return E_INVALIDARG; @@ -2685,7 +2685,7 @@ HRESULT WINAPI MFGetStrideForBitmapInfoHeader(DWORD fourcc, DWORD width, LONG *s struct uncompressed_video_format *format; GUID subtype; - TRACE("%s, %lu, %p.\n", debugstr_fourcc(fourcc), width, stride); + TRACE("%s, %u, %p.\n", debugstr_fourcc(fourcc), width, stride); memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype)); subtype.Data1 = fourcc; @@ -2752,15 +2752,15 @@ HRESULT WINAPI MFGetPlaneSize(DWORD fourcc, DWORD width, DWORD height, DWORD *si unsigned int stride; GUID subtype; - TRACE("%s, %lu, %lu, %p.\n", debugstr_fourcc(fourcc), width, height, size); + TRACE("%s, %u, %u, %p.\n", debugstr_fourcc(fourcc), width, height, size); memcpy(&subtype, &MFVideoFormat_Base, sizeof(subtype)); subtype.Data1 = fourcc; - if ((format = mf_get_video_format(&subtype))) - stride = mf_get_stride_for_format(format, width); - else - stride = 0; + if (!(format = mf_get_video_format(&subtype))) + return MF_E_INVALIDMEDIATYPE; + + stride = mf_get_stride_for_format(format, width); switch (fourcc) { @@ -2926,10 +2926,8 @@ HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *mediatype, WAVE if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_NUM_CHANNELS, &value))) format->nChannels = value; - if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &value))) - format->nSamplesPerSec = value; - if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &value))) - format->nAvgBytesPerSec = value; + IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &format->nSamplesPerSec); + IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &format->nAvgBytesPerSec); if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &value))) format->nBlockAlign = value; if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_BITS_PER_SAMPLE, &value))) @@ -2941,8 +2939,7 @@ HRESULT WINAPI MFCreateWaveFormatExFromMFMediaType(IMFMediaType *mediatype, WAVE if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_VALID_BITS_PER_SAMPLE, &value))) format_ext->Samples.wSamplesPerBlock = value; - if (SUCCEEDED(IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_CHANNEL_MASK, &value))) - format_ext->dwChannelMask = value; + IMFMediaType_GetUINT32(mediatype, &MF_MT_AUDIO_CHANNEL_MASK, &format_ext->dwChannelMask); memcpy(&format_ext->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM, sizeof(format_ext->SubFormat)); } @@ -3088,8 +3085,8 @@ HRESULT WINAPI MFCreateAudioMediaType(const WAVEFORMATEX *format, IMFAudioMediaT return S_OK; } -static void media_type_get_ratio(IMFMediaType *media_type, const GUID *attr, DWORD *numerator, - DWORD *denominator) +static void media_type_get_ratio(IMFMediaType *media_type, const GUID *attr, UINT32 *numerator, + UINT32 *denominator) { UINT64 value; @@ -3105,7 +3102,7 @@ static void media_type_get_ratio(IMFMediaType *media_type, const GUID *attr, DWO */ HRESULT WINAPI MFCreateMFVideoFormatFromMFMediaType(IMFMediaType *media_type, MFVIDEOFORMAT **video_format, UINT32 *size) { - UINT32 flags, palette_size = 0, value; + UINT32 flags, palette_size = 0, avgrate; MFVIDEOFORMAT *format; INT32 stride; GUID guid; @@ -3165,12 +3162,11 @@ HRESULT WINAPI MFCreateMFVideoFormatFromMFMediaType(IMFMediaType *media_type, MF if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_DEFAULT_STRIDE, (UINT32 *)&stride)) && stride < 0) format->videoInfo.VideoFlags |= MFVideoFlag_BottomUpLinearRep; - if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AVG_BITRATE, &value))) - format->compressedInfo.AvgBitrate = value; - if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AVG_BIT_ERROR_RATE, &value))) - format->compressedInfo.AvgBitErrorRate = value; - if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_MAX_KEYFRAME_SPACING, &value))) - format->compressedInfo.MaxKeyFrameSpacing = value; + if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AVG_BITRATE, &avgrate))) + format->compressedInfo.AvgBitrate = avgrate; + if (SUCCEEDED(IMFMediaType_GetUINT32(media_type, &MF_MT_AVG_BIT_ERROR_RATE, &avgrate))) + format->compressedInfo.AvgBitErrorRate = avgrate; + IMFMediaType_GetUINT32(media_type, &MF_MT_MAX_KEYFRAME_SPACING, &format->compressedInfo.MaxKeyFrameSpacing); /* Palette. */ if (palette_size) @@ -3217,15 +3213,15 @@ HRESULT WINAPI MFConvertColorInfoToDXVA(DWORD *dxva_info, const MFVIDEOFORMAT *f struct frame_rate { - UINT64 key; - UINT64 value; + UINT64 rate; + UINT64 frame_time; }; static int __cdecl frame_rate_compare(const void *a, const void *b) { - const UINT64 *key = a; + const UINT64 *rate = a; const struct frame_rate *known_rate = b; - return *key == known_rate->key ? 0 : ( *key < known_rate->key ? 1 : -1 ); + return *rate == known_rate->rate ? 0 : ( *rate < known_rate->rate ? 1 : -1 ); } /*********************************************************************** @@ -3254,7 +3250,7 @@ HRESULT WINAPI MFFrameRateToAverageTimePerFrame(UINT32 numerator, UINT32 denomin if ((entry = bsearch(&rate, known_rates, ARRAY_SIZE(known_rates), sizeof(*known_rates), frame_rate_compare))) { - *avgframetime = entry->value; + *avgframetime = entry->frame_time; } else *avgframetime = numerator ? denominator * (UINT64)10000000 / numerator : 0; @@ -3262,64 +3258,6 @@ HRESULT WINAPI MFFrameRateToAverageTimePerFrame(UINT32 numerator, UINT32 denomin return S_OK; } -static unsigned int get_gcd(unsigned int a, unsigned int b) -{ - unsigned int m; - - while (b) - { - m = a % b; - a = b; - b = m; - } - - return a; -} - -/*********************************************************************** - * MFAverageTimePerFrameToFrameRate (mfplat.@) - */ -HRESULT WINAPI MFAverageTimePerFrameToFrameRate(UINT64 avgtime, UINT32 *numerator, UINT32 *denominator) -{ - static const struct frame_rate known_rates[] = - { -#define KNOWN_RATE(ft,n,d) { ft, ((UINT64)n << 32) | d } - KNOWN_RATE(417188, 24000, 1001), - KNOWN_RATE(416667, 24, 1), - KNOWN_RATE(400000, 25, 1), - KNOWN_RATE(333667, 30000, 1001), - KNOWN_RATE(333333, 30, 1), - KNOWN_RATE(200000, 50, 1), - KNOWN_RATE(166833, 60000, 1001), - KNOWN_RATE(166667, 60, 1), -#undef KNOWN_RATE - }; - const struct frame_rate *entry; - unsigned int gcd; - - TRACE("%s, %p, %p.\n", wine_dbgstr_longlong(avgtime), numerator, denominator); - - if ((entry = bsearch(&avgtime, known_rates, ARRAY_SIZE(known_rates), sizeof(*known_rates), - frame_rate_compare))) - { - *numerator = entry->value >> 32; - *denominator = entry->value; - } - else if (avgtime) - { - if (avgtime > 100000000) avgtime = 100000000; - gcd = get_gcd(10000000, avgtime); - *numerator = 10000000 / gcd; - *denominator = avgtime / gcd; - } - else - { - *numerator = *denominator = 0; - } - - return S_OK; -} - /*********************************************************************** * MFMapDXGIFormatToDX9Format (mfplat.@) */ @@ -3513,7 +3451,7 @@ HRESULT WINAPI MFInitVideoFormat_RGB(MFVIDEOFORMAT *format, DWORD width, DWORD h { unsigned int transfer_function; - TRACE("%p, %lu, %lu, %#lx.\n", format, width, height, d3dformat); + TRACE("%p, %u, %u, %#x.\n", format, width, height, d3dformat); if (!format) return E_INVALIDARG; diff --git a/dlls/mfplat/mfplat.c b/dlls/mfplat/mfplat.c new file mode 100644 index 00000000000..3ec45d7de4e --- /dev/null +++ wine/dlls/mfplat/mfplat.c @@ -0,0 +1,1036 @@ +/* + * Copyright 2019 Nikolay Sivov for CodeWeavers + * Copyright 2020 Zebediah Figura for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" + +#include "ks.h" +#include "ksmedia.h" +#include "wmcodecdsp.h" +#include "initguid.h" +#include "mfapi.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mfplat); + +DEFINE_MEDIATYPE_GUID(MFAudioFormat_XMAudio2, 0x0166); + +struct video_processor +{ + IMFTransform IMFTransform_iface; + LONG refcount; + IMFAttributes *attributes; + IMFAttributes *output_attributes; +}; + +static struct video_processor *impl_video_processor_from_IMFTransform(IMFTransform *iface) +{ + return CONTAINING_RECORD(iface, struct video_processor, IMFTransform_iface); +} + +static HRESULT WINAPI video_processor_QueryInterface(IMFTransform *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IMFTransform) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IMFTransform_AddRef(iface); + return S_OK; + } + + WARN("Unsupported %s.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI video_processor_AddRef(IMFTransform *iface) +{ + struct video_processor *transform = impl_video_processor_from_IMFTransform(iface); + ULONG refcount = InterlockedIncrement(&transform->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + return refcount; +} + +static ULONG WINAPI video_processor_Release(IMFTransform *iface) +{ + struct video_processor *transform = impl_video_processor_from_IMFTransform(iface); + ULONG refcount = InterlockedDecrement(&transform->refcount); + + TRACE("%p, refcount %u.\n", iface, refcount); + + if (!refcount) + { + if (transform->attributes) + IMFAttributes_Release(transform->attributes); + if (transform->output_attributes) + IMFAttributes_Release(transform->output_attributes); + free(transform); + } + + return refcount; +} + +static HRESULT WINAPI video_processor_GetStreamLimits(IMFTransform *iface, DWORD *input_minimum, DWORD *input_maximum, + DWORD *output_minimum, DWORD *output_maximum) +{ + TRACE("%p, %p, %p, %p, %p.\n", iface, input_minimum, input_maximum, output_minimum, output_maximum); + + *input_minimum = *input_maximum = *output_minimum = *output_maximum = 1; + + return S_OK; +} + +static HRESULT WINAPI video_processor_GetStreamCount(IMFTransform *iface, DWORD *inputs, DWORD *outputs) +{ + TRACE("%p, %p, %p.\n", iface, inputs, outputs); + + *inputs = *outputs = 1; + + return S_OK; +} + +static HRESULT WINAPI video_processor_GetStreamIDs(IMFTransform *iface, DWORD input_size, DWORD *inputs, + DWORD output_size, DWORD *outputs) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetInputStreamInfo(IMFTransform *iface, DWORD id, MFT_INPUT_STREAM_INFO *info) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetOutputStreamInfo(IMFTransform *iface, DWORD id, MFT_OUTPUT_STREAM_INFO *info) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetAttributes(IMFTransform *iface, IMFAttributes **attributes) +{ + struct video_processor *transform = impl_video_processor_from_IMFTransform(iface); + + TRACE("%p, %p.\n", iface, attributes); + + *attributes = transform->attributes; + IMFAttributes_AddRef(*attributes); + + return S_OK; +} + +static HRESULT WINAPI video_processor_GetInputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetOutputStreamAttributes(IMFTransform *iface, DWORD id, + IMFAttributes **attributes) +{ + struct video_processor *transform = impl_video_processor_from_IMFTransform(iface); + + TRACE("%p, %u, %p.\n", iface, id, attributes); + + *attributes = transform->output_attributes; + IMFAttributes_AddRef(*attributes); + + return S_OK; +} + +static HRESULT WINAPI video_processor_DeleteInputStream(IMFTransform *iface, DWORD id) +{ + TRACE("%p, %u.\n", iface, id); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_AddInputStreams(IMFTransform *iface, DWORD streams, DWORD *ids) +{ + TRACE("%p, %u, %p.\n", iface, streams, ids); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetInputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + FIXME("%p, %u, %u, %p.\n", iface, id, index, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetOutputAvailableType(IMFTransform *iface, DWORD id, DWORD index, + IMFMediaType **type) +{ + FIXME("%p, %u, %u, %p.\n", iface, id, index, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_SetInputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + FIXME("%p, %u, %p, %#x.\n", iface, id, type, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_SetOutputType(IMFTransform *iface, DWORD id, IMFMediaType *type, DWORD flags) +{ + FIXME("%p, %u, %p, %#x.\n", iface, id, type, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetInputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p.\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetOutputCurrentType(IMFTransform *iface, DWORD id, IMFMediaType **type) +{ + FIXME("%p, %u, %p.\n", iface, id, type); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetInputStatus(IMFTransform *iface, DWORD id, DWORD *flags) +{ + FIXME("%p, %u, %p.\n", iface, id, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_GetOutputStatus(IMFTransform *iface, DWORD *flags) +{ + FIXME("%p, %p.\n", iface, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_SetOutputBounds(IMFTransform *iface, LONGLONG lower, LONGLONG upper) +{ + FIXME("%p, %s, %s.\n", iface, wine_dbgstr_longlong(lower), wine_dbgstr_longlong(upper)); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_ProcessEvent(IMFTransform *iface, DWORD id, IMFMediaEvent *event) +{ + TRACE("%p, %u, %p.\n", iface, id, event); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) +{ + FIXME("%p, %u.\n", iface, message); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_ProcessInput(IMFTransform *iface, DWORD id, IMFSample *sample, DWORD flags) +{ + FIXME("%p, %u, %p, %#x.\n", iface, id, sample, flags); + + return E_NOTIMPL; +} + +static HRESULT WINAPI video_processor_ProcessOutput(IMFTransform *iface, DWORD flags, DWORD count, + MFT_OUTPUT_DATA_BUFFER *samples, DWORD *status) +{ + FIXME("%p, %#x, %u, %p, %p.\n", iface, flags, count, samples, status); + + return E_NOTIMPL; +} + +static const IMFTransformVtbl video_processor_vtbl = +{ + video_processor_QueryInterface, + video_processor_AddRef, + video_processor_Release, + video_processor_GetStreamLimits, + video_processor_GetStreamCount, + video_processor_GetStreamIDs, + video_processor_GetInputStreamInfo, + video_processor_GetOutputStreamInfo, + video_processor_GetAttributes, + video_processor_GetInputStreamAttributes, + video_processor_GetOutputStreamAttributes, + video_processor_DeleteInputStream, + video_processor_AddInputStreams, + video_processor_GetInputAvailableType, + video_processor_GetOutputAvailableType, + video_processor_SetInputType, + video_processor_SetOutputType, + video_processor_GetInputCurrentType, + video_processor_GetOutputCurrentType, + video_processor_GetInputStatus, + video_processor_GetOutputStatus, + video_processor_SetOutputBounds, + video_processor_ProcessEvent, + video_processor_ProcessMessage, + video_processor_ProcessInput, + video_processor_ProcessOutput, +}; + +struct class_factory +{ + IClassFactory IClassFactory_iface; + LONG refcount; + HRESULT (*create_instance)(REFIID riid, void **obj); +}; + +static struct class_factory *impl_from_IClassFactory(IClassFactory *iface) +{ + return CONTAINING_RECORD(iface, struct class_factory, IClassFactory_iface); +} + +static HRESULT WINAPI class_factory_QueryInterface(IClassFactory *iface, REFIID riid, void **obj) +{ + TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); + + if (IsEqualGUID(riid, &IID_IClassFactory) || + IsEqualGUID(riid, &IID_IUnknown)) + { + *obj = iface; + IClassFactory_AddRef(iface); + return S_OK; + } + + WARN("%s is not supported.\n", debugstr_guid(riid)); + *obj = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI class_factory_AddRef(IClassFactory *iface) +{ + struct class_factory *factory = impl_from_IClassFactory(iface); + return InterlockedIncrement(&factory->refcount); +} + +static ULONG WINAPI class_factory_Release(IClassFactory *iface) +{ + struct class_factory *factory = impl_from_IClassFactory(iface); + ULONG refcount = InterlockedDecrement(&factory->refcount); + + if (!refcount) + free(factory); + + return refcount; +} + +static HRESULT WINAPI class_factory_CreateInstance(IClassFactory *iface, IUnknown *outer, REFIID riid, void **obj) +{ + struct class_factory *factory = impl_from_IClassFactory(iface); + + TRACE("%p, %p, %s, %p.\n", iface, outer, debugstr_guid(riid), obj); + + if (outer) + { + *obj = NULL; + return CLASS_E_NOAGGREGATION; + } + + return factory->create_instance(riid, obj); +} + +static HRESULT WINAPI class_factory_LockServer(IClassFactory *iface, BOOL dolock) +{ + TRACE("%p, %d.\n", iface, dolock); + return S_OK; +} + +static const IClassFactoryVtbl class_factory_vtbl = +{ + class_factory_QueryInterface, + class_factory_AddRef, + class_factory_Release, + class_factory_CreateInstance, + class_factory_LockServer, +}; + +static HRESULT video_processor_create(REFIID riid, void **ret) +{ + struct video_processor *object; + HRESULT hr; + + if (!(object = calloc(1, sizeof(*object)))) + return E_OUTOFMEMORY; + + object->IMFTransform_iface.lpVtbl = &video_processor_vtbl; + object->refcount = 1; + + if (FAILED(hr = MFCreateAttributes(&object->attributes, 0))) + goto failed; + + if (FAILED(hr = MFCreateAttributes(&object->output_attributes, 0))) + goto failed; + + *ret = &object->IMFTransform_iface; + return S_OK; + +failed: + + IMFTransform_Release(&object->IMFTransform_iface); + return hr; +} + +static const GUID CLSID_GStreamerByteStreamHandler = {0x317df618, 0x5e5a, 0x468a, {0x9f, 0x15, 0xd8, 0x27, 0xa9, 0xa0, 0x81, 0x62}}; + +static const GUID CLSID_WINEAudioConverter = {0x6a170414,0xaad9,0x4693,{0xb8,0x06,0x3a,0x0c,0x47,0xc5,0x70,0xd6}}; + +static const struct class_object +{ + const GUID *clsid; + HRESULT (*create_instance)(REFIID riid, void **obj); +} +class_objects[] = +{ + { &CLSID_VideoProcessorMFT, &video_processor_create }, + { &CLSID_GStreamerByteStreamHandler, &winegstreamer_stream_handler_create }, + { &CLSID_WINEAudioConverter, &audio_converter_create }, + { &CLSID_CColorConvertDMO, &color_converter_create }, + { &CLSID_MSH264DecoderMFT, &h264_decoder_create }, + { &CLSID_MSAACDecMFT, &aac_decoder_create }, +}; + +HRESULT mfplat_get_class_object(REFCLSID rclsid, REFIID riid, void **obj) +{ + struct class_factory *factory; + unsigned int i; + HRESULT hr; + + for (i = 0; i < ARRAY_SIZE(class_objects); ++i) + { + if (IsEqualGUID(class_objects[i].clsid, rclsid)) + { + if (!(factory = malloc(sizeof(*factory)))) + return E_OUTOFMEMORY; + + factory->IClassFactory_iface.lpVtbl = &class_factory_vtbl; + factory->refcount = 1; + factory->create_instance = class_objects[i].create_instance; + + hr = IClassFactory_QueryInterface(&factory->IClassFactory_iface, riid, obj); + IClassFactory_Release(&factory->IClassFactory_iface); + return hr; + } + } + + return CLASS_E_CLASSNOTAVAILABLE; +} + +static WCHAR audio_converterW[] = L"Audio Converter"; +static const GUID *audio_converter_supported_types[] = +{ + &MFAudioFormat_PCM, + &MFAudioFormat_Float, +}; + +static WCHAR wma_decoderW[] = L"WMAudio Decoder MFT"; +static const GUID *wma_decoder_input_types[] = +{ + &MEDIASUBTYPE_MSAUDIO1, + &MFAudioFormat_WMAudioV8, + &MFAudioFormat_WMAudioV9, + &MFAudioFormat_WMAudio_Lossless, +}; +static const GUID *wma_decoder_output_types[] = +{ + &MFAudioFormat_PCM, + &MFAudioFormat_Float, +}; + +static WCHAR video_processorW[] = L"Video Processor MFT"; +static const GUID *video_processor_supported_types[] = +{ + &MFVideoFormat_IYUV, +}; + +static WCHAR color_converterW[] = L"Color Converter"; +static const GUID *color_converter_supported_types[] = +{ + &MFVideoFormat_RGB24, + &MFVideoFormat_RGB32, + &MFVideoFormat_RGB555, + &MFVideoFormat_RGB8, + &MFVideoFormat_AYUV, + &MFVideoFormat_I420, + &MFVideoFormat_IYUV, + &MFVideoFormat_NV11, + &MFVideoFormat_NV12, + &MFVideoFormat_UYVY, + &MFVideoFormat_v216, + &MFVideoFormat_v410, + &MFVideoFormat_YUY2, + &MFVideoFormat_YVYU, + &MFVideoFormat_YVYU, +}; + +static WCHAR h264_decoderW[] = L"H.264 Decoder"; +static const GUID *h264_decoder_input_types[] = +{ + &MFVideoFormat_H264, +}; +static const GUID *h264_decoder_output_types[] = +{ + &MFVideoFormat_NV12, + &MFVideoFormat_I420, + &MFVideoFormat_IYUV, + &MFVideoFormat_YUY2, + &MFVideoFormat_YV12, +}; + +static WCHAR aac_decoderW[] = L"AAC Decoder"; +static const GUID *aac_decoder_input_types[] = +{ + &MFAudioFormat_AAC, +}; +static const GUID *aac_decoder_output_types[] = +{ + &MFAudioFormat_Float, + &MFAudioFormat_PCM, +}; + +static const struct mft +{ + const GUID *clsid; + const GUID *category; + LPWSTR name; + const UINT32 flags; + const GUID *major_type; + const UINT32 input_types_count; + const GUID **input_types; + const UINT32 output_types_count; + const GUID **output_types; +} +mfts[] = +{ + { + &CLSID_WINEAudioConverter, + &MFT_CATEGORY_AUDIO_EFFECT, + audio_converterW, + MFT_ENUM_FLAG_SYNCMFT, + &MFMediaType_Audio, + ARRAY_SIZE(audio_converter_supported_types), + audio_converter_supported_types, + ARRAY_SIZE(audio_converter_supported_types), + audio_converter_supported_types, + }, + { + &CLSID_WMADecMediaObject, + &MFT_CATEGORY_AUDIO_DECODER, + wma_decoderW, + MFT_ENUM_FLAG_SYNCMFT, + &MFMediaType_Audio, + ARRAY_SIZE(wma_decoder_input_types), + wma_decoder_input_types, + ARRAY_SIZE(wma_decoder_output_types), + wma_decoder_output_types, + }, + { + &CLSID_VideoProcessorMFT, + &MFT_CATEGORY_VIDEO_PROCESSOR, + video_processorW, + 0, + &MFMediaType_Video, + ARRAY_SIZE(video_processor_supported_types), + video_processor_supported_types, + ARRAY_SIZE(video_processor_supported_types), + video_processor_supported_types, + }, + { + &CLSID_CColorConvertDMO, + &MFT_CATEGORY_VIDEO_EFFECT, + color_converterW, + MFT_ENUM_FLAG_SYNCMFT, + &MFMediaType_Video, + ARRAY_SIZE(color_converter_supported_types), + color_converter_supported_types, + ARRAY_SIZE(color_converter_supported_types), + color_converter_supported_types, + }, + { + &CLSID_MSH264DecoderMFT, + &MFT_CATEGORY_VIDEO_DECODER, + h264_decoderW, + MFT_ENUM_FLAG_SYNCMFT, + &MFMediaType_Video, + ARRAY_SIZE(h264_decoder_input_types), + h264_decoder_input_types, + ARRAY_SIZE(h264_decoder_output_types), + h264_decoder_output_types, + }, + { + &CLSID_MSAACDecMFT, + &MFT_CATEGORY_AUDIO_DECODER, + aac_decoderW, + MFT_ENUM_FLAG_SYNCMFT, + &MFMediaType_Audio, + ARRAY_SIZE(aac_decoder_input_types), + aac_decoder_input_types, + ARRAY_SIZE(aac_decoder_output_types), + aac_decoder_output_types, + }, +}; + +HRESULT mfplat_DllRegisterServer(void) +{ + unsigned int i, j; + HRESULT hr; + MFT_REGISTER_TYPE_INFO input_types[15], output_types[15]; + + for (i = 0; i < ARRAY_SIZE(mfts); i++) + { + const struct mft *cur = &mfts[i]; + + for (j = 0; j < cur->input_types_count; j++) + { + input_types[j].guidMajorType = *(cur->major_type); + input_types[j].guidSubtype = *(cur->input_types[j]); + } + for (j = 0; j < cur->output_types_count; j++) + { + output_types[j].guidMajorType = *(cur->major_type); + output_types[j].guidSubtype = *(cur->output_types[j]); + } + + hr = MFTRegister(*(cur->clsid), *(cur->category), cur->name, cur->flags, cur->input_types_count, + input_types, cur->output_types_count, output_types, NULL); + + if (FAILED(hr)) + { + FIXME("Failed to register MFT, hr %#x\n", hr); + return hr; + } + } + return S_OK; +} + +static const struct +{ + const GUID *subtype; + enum wg_video_format format; +} +video_formats[] = +{ + {&MFVideoFormat_ARGB32, WG_VIDEO_FORMAT_BGRA}, + {&MFVideoFormat_RGB32, WG_VIDEO_FORMAT_BGRx}, + {&MFVideoFormat_RGB24, WG_VIDEO_FORMAT_BGR}, + {&MFVideoFormat_RGB555, WG_VIDEO_FORMAT_RGB15}, + {&MFVideoFormat_RGB565, WG_VIDEO_FORMAT_RGB16}, + {&MFVideoFormat_AYUV, WG_VIDEO_FORMAT_AYUV}, + {&MFVideoFormat_I420, WG_VIDEO_FORMAT_I420}, + {&MFVideoFormat_IYUV, WG_VIDEO_FORMAT_I420}, + {&MFVideoFormat_NV12, WG_VIDEO_FORMAT_NV12}, + {&MFVideoFormat_UYVY, WG_VIDEO_FORMAT_UYVY}, + {&MFVideoFormat_YUY2, WG_VIDEO_FORMAT_YUY2}, + {&MFVideoFormat_YV12, WG_VIDEO_FORMAT_YV12}, + {&MFVideoFormat_YVYU, WG_VIDEO_FORMAT_YVYU}, +}; + +static const struct +{ + const GUID *subtype; + UINT32 depth; + enum wg_audio_format format; +} +audio_formats[] = +{ + {&MFAudioFormat_PCM, 8, WG_AUDIO_FORMAT_U8}, + {&MFAudioFormat_PCM, 16, WG_AUDIO_FORMAT_S16LE}, + {&MFAudioFormat_PCM, 24, WG_AUDIO_FORMAT_S24LE}, + {&MFAudioFormat_PCM, 32, WG_AUDIO_FORMAT_S32LE}, + {&MFAudioFormat_Float, 32, WG_AUDIO_FORMAT_F32LE}, + {&MFAudioFormat_Float, 64, WG_AUDIO_FORMAT_F64LE}, +}; + +static inline UINT64 make_uint64(UINT32 high, UINT32 low) +{ + return ((UINT64)high << 32) | low; +} + +static IMFMediaType *mf_media_type_from_wg_format_audio(const struct wg_format *format) +{ + IMFMediaType *type; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(audio_formats); ++i) + { + if (format->u.audio.format == audio_formats[i].format) + { + if (FAILED(MFCreateMediaType(&type))) + return NULL; + + IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio); + IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, audio_formats[i].subtype); + IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, audio_formats[i].depth); + IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, format->u.audio.rate); + IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, format->u.audio.channels); + if (format->u.audio.channel_mask) + IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, format->u.audio.channel_mask); + IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + IMFMediaType_SetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, format->u.audio.channels * audio_formats[i].depth / 8); + + return type; + } + } + + return NULL; +} + +static IMFMediaType *mf_media_type_from_wg_format_video(const struct wg_format *format) +{ + IMFMediaType *type; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(video_formats); ++i) + { + if (format->u.video.format == video_formats[i].format) + { + if (FAILED(MFCreateMediaType(&type))) + return NULL; + + IMFMediaType_SetGUID(type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video); + IMFMediaType_SetGUID(type, &MF_MT_SUBTYPE, video_formats[i].subtype); + IMFMediaType_SetUINT64(type, &MF_MT_FRAME_SIZE, + make_uint64(format->u.video.width, format->u.video.height)); + IMFMediaType_SetUINT64(type, &MF_MT_FRAME_RATE, + make_uint64(format->u.video.fps_n, format->u.video.fps_d)); + IMFMediaType_SetUINT32(type, &MF_MT_COMPRESSED, FALSE); + IMFMediaType_SetUINT32(type, &MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE); + IMFMediaType_SetUINT32(type, &MF_MT_VIDEO_ROTATION, MFVideoRotationFormat_0); + + return type; + } + } + + return NULL; +} + +IMFMediaType *mf_media_type_from_wg_format(const struct wg_format *format) +{ + switch (format->major_type) + { + case WG_MAJOR_TYPE_UNKNOWN: + return NULL; + + case WG_MAJOR_TYPE_AUDIO: + return mf_media_type_from_wg_format_audio(format); + + case WG_MAJOR_TYPE_VIDEO: + return mf_media_type_from_wg_format_video(format); + } + + assert(0); + return NULL; +} + +static void mf_media_type_to_wg_format_audio(IMFMediaType *type, struct wg_format *format) +{ + UINT32 rate, channels, channel_mask, depth; + unsigned int i; + GUID subtype; + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + { + FIXME("Subtype is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate))) + { + FIXME("Sample rate is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels))) + { + FIXME("Channel count is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth))) + { + FIXME("Depth is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_CHANNEL_MASK, &channel_mask))) + { + if (channels == 1) + channel_mask = KSAUDIO_SPEAKER_MONO; + else if (channels == 2) + channel_mask = KSAUDIO_SPEAKER_STEREO; + else if IsEqualGUID(&subtype, &MFAudioFormat_AAC) + channel_mask = 0; + else + { + FIXME("Channel mask is not set.\n"); + return; + } + } + + format->major_type = WG_MAJOR_TYPE_AUDIO; + format->u.audio.channels = channels; + format->u.audio.channel_mask = channel_mask; + format->u.audio.rate = rate; + + for (i = 0; i < ARRAY_SIZE(audio_formats); ++i) + { + if (IsEqualGUID(&subtype, audio_formats[i].subtype) && depth == audio_formats[i].depth) + { + format->u.audio.format = audio_formats[i].format; + return; + } + } + FIXME("Unrecognized audio subtype %s, depth %u.\n", debugstr_guid(&subtype), depth); +} + +static void mf_media_type_to_wg_format_video(IMFMediaType *type, struct wg_format *format) +{ + UINT64 frame_rate, frame_size; + unsigned int i; + GUID subtype; + + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + { + FIXME("Subtype is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + { + FIXME("Frame size is not set.\n"); + return; + } + + format->major_type = WG_MAJOR_TYPE_VIDEO; + format->u.video.width = (UINT32)(frame_size >> 32); + format->u.video.height = (UINT32)frame_size; + format->u.video.fps_n = 1; + format->u.video.fps_d = 1; + + if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate) + { + format->u.video.fps_n = (UINT32)(frame_rate >> 32); + format->u.video.fps_d = (UINT32)frame_rate; + } + + for (i = 0; i < ARRAY_SIZE(video_formats); ++i) + { + if (IsEqualGUID(&subtype, video_formats[i].subtype)) + { + format->u.video.format = video_formats[i].format; + break; + } + } + if (i == ARRAY_SIZE(video_formats)) + FIXME("Unrecognized video subtype %s.\n", debugstr_guid(&subtype)); +} + +void mf_media_type_to_wg_format(IMFMediaType *type, struct wg_format *format) +{ + GUID major_type; + + memset(format, 0, sizeof(*format)); + + if (FAILED(IMFMediaType_GetMajorType(type, &major_type))) + { + FIXME("Major type is not set.\n"); + return; + } + + if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + mf_media_type_to_wg_format_audio(type, format); + else if (IsEqualGUID(&major_type, &MFMediaType_Video)) + mf_media_type_to_wg_format_video(type, format); + else + FIXME("Unrecognized major type %s.\n", debugstr_guid(&major_type)); +} + +static void mf_media_type_to_wg_encoded_format_xwma(IMFMediaType *type, struct wg_encoded_format *format, + enum wg_encoded_type encoded_type, UINT32 version) +{ + UINT32 rate, depth, channels, block_align, bytes_per_second, codec_data_len; + BYTE codec_data[64]; + + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, &rate))) + { + FIXME("Sample rate is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_NUM_CHANNELS, &channels))) + { + FIXME("Channel count is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, &bytes_per_second))) + { + FIXME("Bitrate is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, &block_align))) + { + FIXME("Block alignment is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AUDIO_BITS_PER_SAMPLE, &depth))) + { + FIXME("Depth is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, codec_data, sizeof(codec_data), &codec_data_len))) + { + FIXME("Codec data is not set.\n"); + return; + } + + format->encoded_type = encoded_type; + format->u.xwma.version = version; + format->u.xwma.bitrate = bytes_per_second * 8; + format->u.xwma.rate = rate; + format->u.xwma.depth = depth; + format->u.xwma.channels = channels; + format->u.xwma.block_align = block_align; + format->u.xwma.codec_data_len = codec_data_len; + memcpy(format->u.xwma.codec_data, codec_data, codec_data_len); +} + +static void mf_media_type_to_wg_encoded_format_aac(IMFMediaType *type, struct wg_encoded_format *format) +{ + UINT32 codec_data_len, payload_type, profile_level_indication; + BYTE codec_data[64]; + + /* Audio specific config is stored at after HEAACWAVEINFO in MF_MT_USER_DATA + * https://docs.microsoft.com/en-us/windows/win32/api/mmreg/ns-mmreg-heaacwaveformat + */ + struct + { + WORD payload_type; + WORD profile_level_indication; + WORD type; + WORD reserved1; + DWORD reserved2; + } *aac_info = (void *)codec_data; + + if (FAILED(IMFMediaType_GetBlob(type, &MF_MT_USER_DATA, codec_data, sizeof(codec_data), &codec_data_len))) + { + FIXME("Codec data is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_PAYLOAD_TYPE, &payload_type))) + { + FIXME("AAC payload type is not set.\n"); + payload_type = aac_info->payload_type; + } + if (FAILED(IMFMediaType_GetUINT32(type, &MF_MT_AAC_AUDIO_PROFILE_LEVEL_INDICATION, &profile_level_indication))) + { + FIXME("AAC provile level indication is not set.\n"); + profile_level_indication = aac_info->profile_level_indication; + } + + format->encoded_type = WG_ENCODED_TYPE_AAC; + format->u.aac.payload_type = payload_type; + format->u.aac.profile_level_indication = profile_level_indication; + format->u.aac.codec_data_len = 0; + + if (codec_data_len > sizeof(*aac_info)) + { + format->u.aac.codec_data_len = codec_data_len - sizeof(*aac_info); + memcpy(format->u.aac.codec_data, codec_data + sizeof(*aac_info), codec_data_len - sizeof(*aac_info)); + } +} + +static void mf_media_type_to_wg_encoded_format_h264(IMFMediaType *type, struct wg_encoded_format *format) +{ + UINT64 frame_rate, frame_size; + UINT32 profile, level; + + format->encoded_type = WG_ENCODED_TYPE_H264; + format->u.h264.width = 0; + format->u.h264.height = 0; + format->u.h264.fps_n = 1; + format->u.h264.fps_d = 1; + + if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_SIZE, &frame_size))) + { + format->u.h264.width = (UINT32)(frame_size >> 32); + format->u.h264.height = (UINT32)frame_size; + } + + if (SUCCEEDED(IMFMediaType_GetUINT64(type, &MF_MT_FRAME_RATE, &frame_rate)) && (UINT32)frame_rate) + { + format->u.h264.fps_n = (UINT32)(frame_rate >> 32); + format->u.h264.fps_d = (UINT32)frame_rate; + } + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_PROFILE, &profile))) + format->u.h264.profile = profile; + + if (SUCCEEDED(IMFMediaType_GetUINT32(type, &MF_MT_MPEG2_LEVEL, &level))) + format->u.h264.level = level; +} + +void mf_media_type_to_wg_encoded_format(IMFMediaType *type, struct wg_encoded_format *format) +{ + GUID major_type, subtype; + + memset(format, 0, sizeof(*format)); + + if (FAILED(IMFMediaType_GetMajorType(type, &major_type))) + { + FIXME("Major type is not set.\n"); + return; + } + if (FAILED(IMFMediaType_GetGUID(type, &MF_MT_SUBTYPE, &subtype))) + { + FIXME("Subtype is not set.\n"); + return; + } + + if (IsEqualGUID(&major_type, &MFMediaType_Audio)) + { + if (IsEqualGUID(&subtype, &MEDIASUBTYPE_MSAUDIO1)) + mf_media_type_to_wg_encoded_format_xwma(type, format, WG_ENCODED_TYPE_WMA, 1); + else if (IsEqualGUID(&subtype, &MFAudioFormat_WMAudioV8)) + mf_media_type_to_wg_encoded_format_xwma(type, format, WG_ENCODED_TYPE_WMA, 2); + else if (IsEqualGUID(&subtype, &MFAudioFormat_WMAudioV9)) + mf_media_type_to_wg_encoded_format_xwma(type, format, WG_ENCODED_TYPE_WMA, 3); + else if (IsEqualGUID(&subtype, &MFAudioFormat_WMAudio_Lossless)) + mf_media_type_to_wg_encoded_format_xwma(type, format, WG_ENCODED_TYPE_WMA, 4); + else if (IsEqualGUID(&subtype, &MFAudioFormat_XMAudio2)) + mf_media_type_to_wg_encoded_format_xwma(type, format, WG_ENCODED_TYPE_XMA, 2); + else if (IsEqualGUID(&subtype, &MFAudioFormat_AAC)) + mf_media_type_to_wg_encoded_format_aac(type, format); + else + FIXME("Unimplemented audio subtype %s.\n", debugstr_guid(&subtype)); + } + else if (IsEqualGUID(&major_type, &MFMediaType_Video)) + { + if (IsEqualGUID(&subtype, &MFVideoFormat_H264)) + mf_media_type_to_wg_encoded_format_h264(type, format); + else + FIXME("Unimplemented audio subtype %s.\n", debugstr_guid(&subtype)); + } + else + { + FIXME("Unimplemented major type %s.\n", debugstr_guid(&major_type)); + } +} diff --git a/dlls/mfplat/mfplat.spec b/dlls/mfplat/mfplat.spec index 31c80f596c2..44d98a957e2 100644 --- wine/dlls/mfplat/mfplat.spec +++ wine/dlls/mfplat/mfplat.spec @@ -20,7 +20,7 @@ @ stdcall MFAllocateWorkQueue(ptr) @ stdcall MFAllocateWorkQueueEx(long ptr) rtworkq.RtwqAllocateWorkQueue @ stub MFAppendCollection -@ stdcall MFAverageTimePerFrameToFrameRate(int64 ptr ptr) +@ stub MFAverageTimePerFrameToFrameRate @ stdcall MFBeginCreateFile(long long long wstr ptr ptr ptr) @ stub MFBeginGetHostByName @ stdcall MFBeginRegisterWorkQueueWithMMCSS(long wstr long ptr ptr) @@ -71,7 +71,7 @@ @ stdcall MFCreateStreamDescriptor(long long ptr ptr) @ stdcall MFCreateSystemTimeSource(ptr) @ stub MFCreateSystemUnderlyingClock -@ stdcall MFCreateTempFile(long long long ptr) +@ stub MFCreateTempFile @ stdcall MFCreateTrackedSample(ptr) @ stdcall MFCreateTransformActivate(ptr) @ stub MFCreateURLFromPath @@ -162,7 +162,7 @@ @ stdcall MFTEnumEx(int128 long ptr ptr ptr ptr) @ stdcall MFTGetInfo(int128 ptr ptr ptr ptr ptr ptr) @ stdcall MFTRegister(int128 int128 wstr long long ptr long ptr ptr) -@ stdcall MFTRegisterLocal(ptr ptr wstr long long ptr long ptr) +@ stdcall MFTRegisterLocal(ptr ptr wstr long long ptr long ptr) @ stdcall MFTRegisterLocalByCLSID(ptr ptr wstr long long ptr long ptr) @ stdcall MFTUnregister(int128) @ stdcall MFTUnregisterLocal(ptr) diff --git a/dlls/mfplat/mfplat_private.h b/dlls/mfplat/mfplat_private.h index 8418c8eb2ef..b646d9e7cbb 100644 --- wine/dlls/mfplat/mfplat_private.h +++ wine/dlls/mfplat/mfplat_private.h @@ -128,13 +128,11 @@ static inline const char *debugstr_propvar(const PROPVARIANT *v) case VT_NULL: return wine_dbg_sprintf("%p {VT_NULL}", v); case VT_UI4: - return wine_dbg_sprintf("%p {VT_UI4: %ld}", v, v->ulVal); + return wine_dbg_sprintf("%p {VT_UI4: %d}", v, v->ulVal); case VT_UI8: return wine_dbg_sprintf("%p {VT_UI8: %s}", v, wine_dbgstr_longlong(v->uhVal.QuadPart)); case VT_I8: return wine_dbg_sprintf("%p {VT_I8: %s}", v, wine_dbgstr_longlong(v->hVal.QuadPart)); - case VT_R4: - return wine_dbg_sprintf("%p {VT_R4: %.8e}", v, v->fltVal); case VT_R8: return wine_dbg_sprintf("%p {VT_R8: %lf}", v, v->dblVal); case VT_CLSID: @@ -180,7 +178,7 @@ static inline const char *debugstr_fourcc(DWORD format) return wine_dbg_sprintf("%s", wine_dbgstr_an(formats[i].name, -1)); } - return wine_dbg_sprintf("%#lx", format); + return wine_dbg_sprintf("%#x", format); } return wine_dbgstr_an((char *)&format, 4); diff --git a/dlls/mfplat/quartz_parser.c b/dlls/mfplat/quartz_parser.c new file mode 100644 index 00000000000..e9a3b00016d --- /dev/null +++ wine/dlls/mfplat/quartz_parser.c @@ -0,0 +1,1945 @@ +/* + * DirectShow parser filters + * + * Copyright 2010 Maarten Lankhorst for CodeWeavers + * Copyright 2010 Aric Stewart for CodeWeavers + * Copyright 2019-2020 Zebediah Figura + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "gst_private.h" +#include "gst_guids.h" + +#include "amvideo.h" + +#include +#include "dvdmedia.h" +#include "mmreg.h" +#include "ks.h" +#include "initguid.h" +#include "wmcodecdsp.h" +#include "ksmedia.h" + +WINE_DEFAULT_DEBUG_CHANNEL(quartz); + +static const GUID MEDIASUBTYPE_CVID = {mmioFOURCC('c','v','i','d'), 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; +static const GUID MEDIASUBTYPE_MP3 = {WAVE_FORMAT_MPEGLAYER3, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}}; + +struct parser +{ + struct strmbase_filter filter; + IAMStreamSelect IAMStreamSelect_iface; + + struct strmbase_sink sink; + IAsyncReader *reader; + + struct parser_source **sources; + unsigned int source_count; + BOOL enum_sink_first; + + struct wg_parser *wg_parser; + + /* FIXME: It would be nice to avoid duplicating these with strmbase. + * However, synchronization is tricky; we need access to be protected by a + * separate lock. */ + bool streaming, sink_connected; + + HANDLE read_thread; + + BOOL (*init_gst)(struct parser *filter); + HRESULT (*source_query_accept)(struct parser_source *pin, const AM_MEDIA_TYPE *mt); + HRESULT (*source_get_media_type)(struct parser_source *pin, unsigned int index, AM_MEDIA_TYPE *mt); +}; + +struct parser_source +{ + struct strmbase_source pin; + IQualityControl IQualityControl_iface; + + struct wg_parser_stream *wg_stream; + + SourceSeeking seek; + + CRITICAL_SECTION flushing_cs; + CONDITION_VARIABLE eos_cv; + HANDLE thread; + + /* This variable is read and written by both the streaming thread and + * application threads. However, it is only written by the application + * thread when the streaming thread is not running, or when it is blocked + * by flushing_cs. */ + bool need_segment; + + bool eos; +}; + +static inline struct parser *impl_from_strmbase_filter(struct strmbase_filter *iface) +{ + return CONTAINING_RECORD(iface, struct parser, filter); +} + +static const IMediaSeekingVtbl GST_Seeking_Vtbl; +static const IQualityControlVtbl GSTOutPin_QualityControl_Vtbl; + +static struct parser_source *create_pin(struct parser *filter, + struct wg_parser_stream *stream, const WCHAR *name); +static HRESULT GST_RemoveOutputPins(struct parser *This); +static HRESULT WINAPI GST_ChangeCurrent(IMediaSeeking *iface); +static HRESULT WINAPI GST_ChangeStop(IMediaSeeking *iface); +static HRESULT WINAPI GST_ChangeRate(IMediaSeeking *iface); + +static bool amt_from_wg_format_audio(AM_MEDIA_TYPE *mt, const struct wg_format *format) +{ + mt->majortype = MEDIATYPE_Audio; + mt->formattype = FORMAT_WaveFormatEx; + + switch (format->u.audio.format) + { + case WG_AUDIO_FORMAT_UNKNOWN: + return false; + + case WG_AUDIO_FORMAT_MPEG1_LAYER1: + case WG_AUDIO_FORMAT_MPEG1_LAYER2: + { + MPEG1WAVEFORMAT *wave_format; + + if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format)))) + return false; + memset(wave_format, 0, sizeof(*wave_format)); + + mt->subtype = MEDIASUBTYPE_MPEG1AudioPayload; + mt->cbFormat = sizeof(*wave_format); + mt->pbFormat = (BYTE *)wave_format; + wave_format->wfx.wFormatTag = WAVE_FORMAT_MPEG; + wave_format->wfx.nChannels = format->u.audio.channels; + wave_format->wfx.nSamplesPerSec = format->u.audio.rate; + wave_format->wfx.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX); + wave_format->fwHeadLayer = (format->u.audio.format == WG_AUDIO_FORMAT_MPEG1_LAYER1 ? 1 : 2); + return true; + } + + case WG_AUDIO_FORMAT_MPEG1_LAYER3: + { + MPEGLAYER3WAVEFORMAT *wave_format; + + if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format)))) + return false; + memset(wave_format, 0, sizeof(*wave_format)); + + mt->subtype = MEDIASUBTYPE_MP3; + mt->cbFormat = sizeof(*wave_format); + mt->pbFormat = (BYTE *)wave_format; + wave_format->wfx.wFormatTag = WAVE_FORMAT_MPEGLAYER3; + wave_format->wfx.nChannels = format->u.audio.channels; + wave_format->wfx.nSamplesPerSec = format->u.audio.rate; + wave_format->wfx.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX); + /* FIXME: We can't get most of the MPEG data from the caps. We may have + * to manually parse the header. */ + wave_format->wID = MPEGLAYER3_ID_MPEG; + wave_format->fdwFlags = MPEGLAYER3_FLAG_PADDING_ON; + wave_format->nFramesPerBlock = 1; + wave_format->nCodecDelay = 1393; + return true; + } + + case WG_AUDIO_FORMAT_U8: + case WG_AUDIO_FORMAT_S16LE: + case WG_AUDIO_FORMAT_S24LE: + case WG_AUDIO_FORMAT_S32LE: + case WG_AUDIO_FORMAT_F32LE: + case WG_AUDIO_FORMAT_F64LE: + { + static const struct + { + bool is_float; + WORD depth; + } + format_table[] = + { + {0}, + {false, 8}, + {false, 16}, + {false, 24}, + {false, 32}, + {true, 32}, + {true, 64}, + }; + + bool is_float; + WORD depth; + + assert(format->u.audio.format < ARRAY_SIZE(format_table)); + is_float = format_table[format->u.audio.format].is_float; + depth = format_table[format->u.audio.format].depth; + + if (is_float || format->u.audio.channels > 2) + { + WAVEFORMATEXTENSIBLE *wave_format; + + if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format)))) + return false; + memset(wave_format, 0, sizeof(*wave_format)); + + mt->subtype = is_float ? MEDIASUBTYPE_IEEE_FLOAT : MEDIASUBTYPE_PCM; + mt->bFixedSizeSamples = TRUE; + mt->pbFormat = (BYTE *)wave_format; + mt->cbFormat = sizeof(*wave_format); + wave_format->Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wave_format->Format.nChannels = format->u.audio.channels; + wave_format->Format.nSamplesPerSec = format->u.audio.rate; + wave_format->Format.nAvgBytesPerSec = format->u.audio.rate * format->u.audio.channels * depth / 8; + wave_format->Format.nBlockAlign = format->u.audio.channels * depth / 8; + wave_format->Format.wBitsPerSample = depth; + wave_format->Format.cbSize = sizeof(*wave_format) - sizeof(WAVEFORMATEX); + wave_format->Samples.wValidBitsPerSample = depth; + wave_format->dwChannelMask = format->u.audio.channel_mask; + wave_format->SubFormat = is_float ? KSDATAFORMAT_SUBTYPE_IEEE_FLOAT : KSDATAFORMAT_SUBTYPE_PCM; + mt->lSampleSize = wave_format->Format.nBlockAlign; + } + else + { + WAVEFORMATEX *wave_format; + + if (!(wave_format = CoTaskMemAlloc(sizeof(*wave_format)))) + return false; + memset(wave_format, 0, sizeof(*wave_format)); + + mt->subtype = MEDIASUBTYPE_PCM; + mt->bFixedSizeSamples = TRUE; + mt->pbFormat = (BYTE *)wave_format; + mt->cbFormat = sizeof(*wave_format); + wave_format->wFormatTag = WAVE_FORMAT_PCM; + wave_format->nChannels = format->u.audio.channels; + wave_format->nSamplesPerSec = format->u.audio.rate; + wave_format->nAvgBytesPerSec = format->u.audio.rate * format->u.audio.channels * depth / 8; + wave_format->nBlockAlign = format->u.audio.channels * depth / 8; + wave_format->wBitsPerSample = depth; + wave_format->cbSize = 0; + mt->lSampleSize = wave_format->nBlockAlign; + } + return true; + } + } + + assert(0); + return false; +} + +#define ALIGN(n, alignment) (((n) + (alignment) - 1) & ~((alignment) - 1)) + +unsigned int wg_format_get_max_size(const struct wg_format *format) +{ + switch (format->major_type) + { + case WG_MAJOR_TYPE_VIDEO: + { + unsigned int width = format->u.video.width, height = format->u.video.height; + + switch (format->u.video.format) + { + case WG_VIDEO_FORMAT_BGRA: + case WG_VIDEO_FORMAT_BGRx: + case WG_VIDEO_FORMAT_AYUV: + return width * height * 4; + + case WG_VIDEO_FORMAT_BGR: + return ALIGN(width * 3, 4) * height; + + case WG_VIDEO_FORMAT_RGB15: + case WG_VIDEO_FORMAT_RGB16: + case WG_VIDEO_FORMAT_UYVY: + case WG_VIDEO_FORMAT_YUY2: + case WG_VIDEO_FORMAT_YVYU: + return ALIGN(width * 2, 4) * height; + + case WG_VIDEO_FORMAT_I420: + case WG_VIDEO_FORMAT_YV12: + return ALIGN(width, 4) * ALIGN(height, 2) /* Y plane */ + + 2 * ALIGN((width + 1) / 2, 4) * ((height + 1) / 2); /* U and V planes */ + + case WG_VIDEO_FORMAT_NV12: + return ALIGN(width, 4) * ALIGN(height, 2) /* Y plane */ + + ALIGN(width, 4) * ((height + 1) / 2); /* U/V plane */ + + case WG_VIDEO_FORMAT_CINEPAK: + /* Both ffmpeg's encoder and a Cinepak file seen in the wild report + * 24 bpp. ffmpeg sets biSizeImage as below; others may be smaller, + * but as long as every sample fits into our allocator, we're fine. */ + return width * height * 3; + + case WG_VIDEO_FORMAT_UNKNOWN: + FIXME("Cannot guess maximum sample size for unknown video format.\n"); + return 0; + } + break; + } + + case WG_MAJOR_TYPE_AUDIO: + { + unsigned int rate = format->u.audio.rate, channels = format->u.audio.channels; + + /* Actually we don't know how large of a sample GStreamer will give + * us. Hopefully 1 second is enough... */ + + switch (format->u.audio.format) + { + case WG_AUDIO_FORMAT_U8: + return rate * channels; + + case WG_AUDIO_FORMAT_S16LE: + return rate * channels * 2; + + case WG_AUDIO_FORMAT_S24LE: + return rate * channels * 3; + + case WG_AUDIO_FORMAT_S32LE: + case WG_AUDIO_FORMAT_F32LE: + return rate * channels * 4; + + case WG_AUDIO_FORMAT_F64LE: + return rate * channels * 8; + + case WG_AUDIO_FORMAT_MPEG1_LAYER1: + return 56000; + + case WG_AUDIO_FORMAT_MPEG1_LAYER2: + return 48000; + + case WG_AUDIO_FORMAT_MPEG1_LAYER3: + return 40000; + + case WG_AUDIO_FORMAT_UNKNOWN: + FIXME("Cannot guess maximum sample size for unknown audio format.\n"); + return 0; + } + break; + } + + case WG_MAJOR_TYPE_UNKNOWN: + FIXME("Cannot guess maximum sample size for unknown format.\n"); + return 0; + } + + assert(0); + return 0; +} + +static bool amt_from_wg_format_video(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm) +{ + static const struct + { + const GUID *subtype; + DWORD compression; + WORD depth; + } + format_table[] = + { + {0}, + {&MEDIASUBTYPE_ARGB32, BI_RGB, 32}, + {&MEDIASUBTYPE_RGB32, BI_RGB, 32}, + {&MEDIASUBTYPE_RGB24, BI_RGB, 24}, + {&MEDIASUBTYPE_RGB555, BI_RGB, 16}, + {&MEDIASUBTYPE_RGB565, BI_BITFIELDS, 16}, + {&MEDIASUBTYPE_AYUV, mmioFOURCC('A','Y','U','V'), 32}, + {&MEDIASUBTYPE_I420, mmioFOURCC('I','4','2','0'), 12}, + {&MEDIASUBTYPE_NV12, mmioFOURCC('N','V','1','2'), 12}, + {&MEDIASUBTYPE_UYVY, mmioFOURCC('U','Y','V','Y'), 16}, + {&MEDIASUBTYPE_YUY2, mmioFOURCC('Y','U','Y','2'), 16}, + {&MEDIASUBTYPE_YV12, mmioFOURCC('Y','V','1','2'), 12}, + {&MEDIASUBTYPE_YVYU, mmioFOURCC('Y','V','Y','U'), 16}, + {&MEDIASUBTYPE_CVID, mmioFOURCC('C','V','I','D'), 24}, + }; + + VIDEOINFO *video_format; + uint32_t frame_time; + + if (format->u.video.format == WG_VIDEO_FORMAT_UNKNOWN) + return false; + + if (!(video_format = CoTaskMemAlloc(sizeof(*video_format)))) + return false; + + assert(format->u.video.format < ARRAY_SIZE(format_table)); + + mt->majortype = MEDIATYPE_Video; + mt->subtype = *format_table[format->u.video.format].subtype; + if (wm) + mt->bFixedSizeSamples = TRUE; + else + mt->bTemporalCompression = TRUE; + mt->lSampleSize = 1; + mt->formattype = FORMAT_VideoInfo; + mt->cbFormat = sizeof(VIDEOINFOHEADER); + mt->pbFormat = (BYTE *)video_format; + + memset(video_format, 0, sizeof(*video_format)); + + if (wm) + { + SetRect(&video_format->rcSource, 0, 0, format->u.video.width, format->u.video.height); + video_format->rcTarget = video_format->rcSource; + } + if ((frame_time = MulDiv(10000000, format->u.video.fps_d, format->u.video.fps_n)) != -1) + video_format->AvgTimePerFrame = frame_time; + video_format->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + video_format->bmiHeader.biWidth = format->u.video.width; + video_format->bmiHeader.biHeight = format->u.video.height; + video_format->bmiHeader.biPlanes = 1; + video_format->bmiHeader.biBitCount = format_table[format->u.video.format].depth; + video_format->bmiHeader.biCompression = format_table[format->u.video.format].compression; + video_format->bmiHeader.biSizeImage = wg_format_get_max_size(format); + + if (format->u.video.format == WG_VIDEO_FORMAT_RGB16) + { + mt->cbFormat = offsetof(VIDEOINFO, dwBitMasks[3]); + video_format->dwBitMasks[iRED] = 0xf800; + video_format->dwBitMasks[iGREEN] = 0x07e0; + video_format->dwBitMasks[iBLUE] = 0x001f; + } + + return true; +} + +bool amt_from_wg_format(AM_MEDIA_TYPE *mt, const struct wg_format *format, bool wm) +{ + memset(mt, 0, sizeof(*mt)); + + switch (format->major_type) + { + case WG_MAJOR_TYPE_UNKNOWN: + return false; + + case WG_MAJOR_TYPE_AUDIO: + return amt_from_wg_format_audio(mt, format); + + case WG_MAJOR_TYPE_VIDEO: + return amt_from_wg_format_video(mt, format, wm); + } + + assert(0); + return false; +} + +static bool amt_to_wg_format_audio(const AM_MEDIA_TYPE *mt, struct wg_format *format) +{ + static const struct + { + const GUID *subtype; + WORD depth; + enum wg_audio_format format; + } + format_map[] = + { + {&MEDIASUBTYPE_PCM, 8, WG_AUDIO_FORMAT_U8}, + {&MEDIASUBTYPE_PCM, 16, WG_AUDIO_FORMAT_S16LE}, + {&MEDIASUBTYPE_PCM, 24, WG_AUDIO_FORMAT_S24LE}, + {&MEDIASUBTYPE_PCM, 32, WG_AUDIO_FORMAT_S32LE}, + {&MEDIASUBTYPE_IEEE_FLOAT, 32, WG_AUDIO_FORMAT_F32LE}, + {&MEDIASUBTYPE_IEEE_FLOAT, 64, WG_AUDIO_FORMAT_F64LE}, + }; + + const WAVEFORMATEX *audio_format = (const WAVEFORMATEX *)mt->pbFormat; + unsigned int i; + + if (!IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx)) + { + FIXME("Unknown format type %s.\n", debugstr_guid(&mt->formattype)); + return false; + } + if (mt->cbFormat < sizeof(WAVEFORMATEX) || !mt->pbFormat) + { + ERR("Unexpected format size %u.\n", mt->cbFormat); + return false; + } + + format->major_type = WG_MAJOR_TYPE_AUDIO; + format->u.audio.channels = audio_format->nChannels; + format->u.audio.rate = audio_format->nSamplesPerSec; + + if (audio_format->wFormatTag == WAVE_FORMAT_EXTENSIBLE) + { + const WAVEFORMATEXTENSIBLE *ext_format = (const WAVEFORMATEXTENSIBLE *)mt->pbFormat; + + format->u.audio.channel_mask = ext_format->dwChannelMask; + } + else + { + if (audio_format->nChannels == 1) + format->u.audio.channel_mask = KSAUDIO_SPEAKER_MONO; + else if (audio_format->nChannels == 2) + format->u.audio.channel_mask = KSAUDIO_SPEAKER_STEREO; + else + { + ERR("Unexpected channel count %u.\n", audio_format->nChannels); + return false; + } + } + + for (i = 0; i < ARRAY_SIZE(format_map); ++i) + { + if (IsEqualGUID(&mt->subtype, format_map[i].subtype) + && audio_format->wBitsPerSample == format_map[i].depth) + { + format->u.audio.format = format_map[i].format; + return true; + } + } + + FIXME("Unknown subtype %s, depth %u.\n", debugstr_guid(&mt->subtype), audio_format->wBitsPerSample); + return false; +} + +static bool amt_to_wg_format_audio_mpeg1(const AM_MEDIA_TYPE *mt, struct wg_format *format) +{ + const MPEG1WAVEFORMAT *audio_format = (const MPEG1WAVEFORMAT *)mt->pbFormat; + + if (!IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx)) + { + FIXME("Unknown format type %s.\n", debugstr_guid(&mt->formattype)); + return false; + } + if (mt->cbFormat < sizeof(*audio_format) || !mt->pbFormat) + { + ERR("Unexpected format size %u.\n", mt->cbFormat); + return false; + } + + format->major_type = WG_MAJOR_TYPE_AUDIO; + format->u.audio.channels = audio_format->wfx.nChannels; + format->u.audio.rate = audio_format->wfx.nSamplesPerSec; + if (audio_format->fwHeadLayer == 1) + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER1; + else if (audio_format->fwHeadLayer == 2) + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER2; + else if (audio_format->fwHeadLayer == 3) + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3; + else + return false; + return true; +} + +static bool amt_to_wg_format_audio_mpeg1_layer3(const AM_MEDIA_TYPE *mt, struct wg_format *format) +{ + const MPEGLAYER3WAVEFORMAT *audio_format = (const MPEGLAYER3WAVEFORMAT *)mt->pbFormat; + + if (!IsEqualGUID(&mt->formattype, &FORMAT_WaveFormatEx)) + { + FIXME("Unknown format type %s.\n", debugstr_guid(&mt->formattype)); + return false; + } + if (mt->cbFormat < sizeof(*audio_format) || !mt->pbFormat) + { + ERR("Unexpected format size %u.\n", mt->cbFormat); + return false; + } + + format->major_type = WG_MAJOR_TYPE_AUDIO; + format->u.audio.channels = audio_format->wfx.nChannels; + format->u.audio.rate = audio_format->wfx.nSamplesPerSec; + format->u.audio.format = WG_AUDIO_FORMAT_MPEG1_LAYER3; + return true; +} + +static bool amt_to_wg_format_video(const AM_MEDIA_TYPE *mt, struct wg_format *format) +{ + static const struct + { + const GUID *subtype; + enum wg_video_format format; + } + format_map[] = + { + {&MEDIASUBTYPE_ARGB32, WG_VIDEO_FORMAT_BGRA}, + {&MEDIASUBTYPE_RGB32, WG_VIDEO_FORMAT_BGRx}, + {&MEDIASUBTYPE_RGB24, WG_VIDEO_FORMAT_BGR}, + {&MEDIASUBTYPE_RGB555, WG_VIDEO_FORMAT_RGB15}, + {&MEDIASUBTYPE_RGB565, WG_VIDEO_FORMAT_RGB16}, + {&MEDIASUBTYPE_AYUV, WG_VIDEO_FORMAT_AYUV}, + {&MEDIASUBTYPE_I420, WG_VIDEO_FORMAT_I420}, + {&MEDIASUBTYPE_NV12, WG_VIDEO_FORMAT_NV12}, + {&MEDIASUBTYPE_UYVY, WG_VIDEO_FORMAT_UYVY}, + {&MEDIASUBTYPE_YUY2, WG_VIDEO_FORMAT_YUY2}, + {&MEDIASUBTYPE_YV12, WG_VIDEO_FORMAT_YV12}, + {&MEDIASUBTYPE_YVYU, WG_VIDEO_FORMAT_YVYU}, + {&MEDIASUBTYPE_CVID, WG_VIDEO_FORMAT_CINEPAK}, + }; + + const VIDEOINFOHEADER *video_format = (const VIDEOINFOHEADER *)mt->pbFormat; + unsigned int i; + + if (!IsEqualGUID(&mt->formattype, &FORMAT_VideoInfo)) + { + FIXME("Unknown format type %s.\n", debugstr_guid(&mt->formattype)); + return false; + } + if (mt->cbFormat < sizeof(VIDEOINFOHEADER) || !mt->pbFormat) + { + ERR("Unexpected format size %u.\n", mt->cbFormat); + return false; + } + + format->major_type = WG_MAJOR_TYPE_VIDEO; + format->u.video.width = video_format->bmiHeader.biWidth; + format->u.video.height = video_format->bmiHeader.biHeight; + format->u.video.fps_n = 10000000; + format->u.video.fps_d = video_format->AvgTimePerFrame; + + for (i = 0; i < ARRAY_SIZE(format_map); ++i) + { + if (IsEqualGUID(&mt->subtype, format_map[i].subtype)) + { + format->u.video.format = format_map[i].format; + return true; + } + } + + FIXME("Unknown subtype %s.\n", debugstr_guid(&mt->subtype)); + return false; +} + +bool amt_to_wg_format(const AM_MEDIA_TYPE *mt, struct wg_format *format) +{ + memset(format, 0, sizeof(*format)); + + if (IsEqualGUID(&mt->majortype, &MEDIATYPE_Video)) + return amt_to_wg_format_video(mt, format); + if (IsEqualGUID(&mt->majortype, &MEDIATYPE_Audio)) + { + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MPEG1AudioPayload)) + return amt_to_wg_format_audio_mpeg1(mt, format); + if (IsEqualGUID(&mt->subtype, &MEDIASUBTYPE_MP3)) + return amt_to_wg_format_audio_mpeg1_layer3(mt, format); + return amt_to_wg_format_audio(mt, format); + } + + FIXME("Unknown major type %s.\n", debugstr_guid(&mt->majortype)); + return false; +} + +/* + * scale_uint64() is based on gst_util_scale_int() from GStreamer, which is + * covered by the following license: + * + * GStreamer + * Copyright (C) 1999,2000 Erik Walthinsen + * 2000 Wim Taymans + * 2002 Thomas Vander Stichele + * 2004 Wim Taymans + * 2015 Jan Schmidt + * + * gstutils.c: Utility functions + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +static uint64_t scale_uint64(uint64_t value, uint32_t numerator, uint32_t denominator) +{ + ULARGE_INTEGER i, high, low; + + if (!value) + return 0; + + i.QuadPart = value; + low.QuadPart = (ULONGLONG)i.u.LowPart * numerator; + high.QuadPart = (ULONGLONG)i.u.HighPart * numerator + low.u.HighPart; + low.u.HighPart = 0; + + if (high.u.HighPart >= denominator) + return ULLONG_MAX; + + low.QuadPart += (high.QuadPart % denominator) << 32; + return ((high.QuadPart / denominator) << 32) + (low.QuadPart / denominator); +} + +/* Fill and send a single IMediaSample. */ +static HRESULT send_sample(struct parser_source *pin, IMediaSample *sample, + const struct wg_parser_buffer *buffer, uint32_t offset, uint32_t size, DWORD bytes_per_second) +{ + HRESULT hr; + BYTE *ptr = NULL; + + TRACE("offset %u, size %u, sample size %u\n", offset, size, IMediaSample_GetSize(sample)); + + hr = IMediaSample_SetActualDataLength(sample, size); + if(FAILED(hr)){ + WARN("SetActualDataLength failed: %08x\n", hr); + return hr; + } + + IMediaSample_GetPointer(sample, &ptr); + + if (!wg_parser_stream_copy_buffer(pin->wg_stream, ptr, offset, size)) + { + /* The GStreamer pin has been flushed. */ + return S_OK; + } + + if (buffer->has_pts) + { + REFERENCE_TIME start_pts = buffer->pts; + + if (offset) + start_pts += scale_uint64(offset, 10000000, bytes_per_second); + start_pts -= pin->seek.llCurrent; + start_pts *= pin->seek.dRate; + + if (buffer->has_duration) + { + REFERENCE_TIME end_pts = buffer->pts + buffer->duration; + + if (offset + size < buffer->size) + end_pts = buffer->pts + scale_uint64(offset + size, 10000000, bytes_per_second); + end_pts -= pin->seek.llCurrent; + end_pts *= pin->seek.dRate; + + IMediaSample_SetTime(sample, &start_pts, &end_pts); + IMediaSample_SetMediaTime(sample, &start_pts, &end_pts); + } + else + { + IMediaSample_SetTime(sample, &start_pts, NULL); + IMediaSample_SetMediaTime(sample, NULL, NULL); + } + } + else + { + IMediaSample_SetTime(sample, NULL, NULL); + IMediaSample_SetMediaTime(sample, NULL, NULL); + } + + IMediaSample_SetDiscontinuity(sample, !offset && buffer->discontinuity); + IMediaSample_SetPreroll(sample, buffer->preroll); + IMediaSample_SetSyncPoint(sample, !buffer->delta); + + if (!pin->pin.pin.peer) + hr = VFW_E_NOT_CONNECTED; + else + hr = IMemInputPin_Receive(pin->pin.pMemInputPin, sample); + + TRACE("sending sample returned: %08x\n", hr); + + return hr; +} + +/* Send a single GStreamer buffer (splitting it into multiple IMediaSamples if + * necessary). */ +static void send_buffer(struct parser_source *pin, const struct wg_parser_buffer *buffer) +{ + HRESULT hr; + IMediaSample *sample; + + if (pin->need_segment) + { + if (FAILED(hr = IPin_NewSegment(pin->pin.pin.peer, + pin->seek.llCurrent, pin->seek.llStop, pin->seek.dRate))) + WARN("Failed to deliver new segment, hr %#x.\n", hr); + pin->need_segment = false; + } + + if (IsEqualGUID(&pin->pin.pin.mt.formattype, &FORMAT_WaveFormatEx) + && (IsEqualGUID(&pin->pin.pin.mt.subtype, &MEDIASUBTYPE_PCM) + || IsEqualGUID(&pin->pin.pin.mt.subtype, &MEDIASUBTYPE_IEEE_FLOAT))) + { + WAVEFORMATEX *format = (WAVEFORMATEX *)pin->pin.pin.mt.pbFormat; + uint32_t offset = 0; + + while (offset < buffer->size) + { + uint32_t advance; + + hr = BaseOutputPinImpl_GetDeliveryBuffer(&pin->pin, &sample, NULL, NULL, 0); + + if (FAILED(hr)) + { + if (hr != VFW_E_NOT_CONNECTED) + ERR("Could not get a delivery buffer (%x), returning GST_FLOW_FLUSHING\n", hr); + break; + } + + advance = min(IMediaSample_GetSize(sample), buffer->size - offset); + + hr = send_sample(pin, sample, buffer, offset, advance, format->nAvgBytesPerSec); + + IMediaSample_Release(sample); + + if (FAILED(hr)) + break; + + offset += advance; + } + } + else + { + hr = BaseOutputPinImpl_GetDeliveryBuffer(&pin->pin, &sample, NULL, NULL, 0); + + if (FAILED(hr)) + { + if (hr != VFW_E_NOT_CONNECTED) + ERR("Could not get a delivery buffer (%x), returning GST_FLOW_FLUSHING\n", hr); + } + else + { + hr = send_sample(pin, sample, buffer, 0, buffer->size, 0); + + IMediaSample_Release(sample); + } + } + + wg_parser_stream_release_buffer(pin->wg_stream); +} + +static DWORD CALLBACK stream_thread(void *arg) +{ + struct parser_source *pin = arg; + struct parser *filter = impl_from_strmbase_filter(pin->pin.pin.filter); + + TRACE("Starting streaming thread for pin %p.\n", pin); + + while (filter->streaming) + { + struct wg_parser_buffer buffer; + + EnterCriticalSection(&pin->flushing_cs); + + if (pin->eos) + { + SleepConditionVariableCS(&pin->eos_cv, &pin->flushing_cs, INFINITE); + LeaveCriticalSection(&pin->flushing_cs); + continue; + } + + if (wg_parser_stream_get_buffer(pin->wg_stream, &buffer)) + { + send_buffer(pin, &buffer); + } + else + { + TRACE("Got EOS.\n"); + IPin_EndOfStream(pin->pin.pin.peer); + pin->eos = true; + } + + LeaveCriticalSection(&pin->flushing_cs); + } + + TRACE("Streaming stopped; exiting.\n"); + return 0; +} + +static DWORD CALLBACK read_thread(void *arg) +{ + struct parser *filter = arg; + LONGLONG file_size, unused; + size_t buffer_size = 4096; + void *data = NULL; + + if (!(data = malloc(buffer_size))) + return 0; + + IAsyncReader_Length(filter->reader, &file_size, &unused); + + TRACE("Starting read thread for filter %p.\n", filter); + + while (filter->sink_connected) + { + uint64_t offset; + uint32_t size; + HRESULT hr; + + if (!wg_parser_get_next_read_offset(filter->wg_parser, &offset, &size)) + continue; + + if (offset >= file_size) + size = 0; + else if (offset + size >= file_size) + size = file_size - offset; + + if (!array_reserve(&data, &buffer_size, size, 1)) + { + free(data); + return 0; + } + + hr = IAsyncReader_SyncRead(filter->reader, offset, size, data); + if (FAILED(hr)) + ERR("Failed to read %u bytes at offset %I64u, hr %#x.\n", size, offset, hr); + + wg_parser_push_data(filter->wg_parser, SUCCEEDED(hr) ? WG_READ_SUCCESS : WG_READ_FAILURE, data, size); + } + + free(data); + TRACE("Streaming stopped; exiting.\n"); + return 0; +} + +static inline struct parser_source *impl_from_IMediaSeeking(IMediaSeeking *iface) +{ + return CONTAINING_RECORD(iface, struct parser_source, seek.IMediaSeeking_iface); +} + +static struct strmbase_pin *parser_get_pin(struct strmbase_filter *base, unsigned int index) +{ + struct parser *filter = impl_from_strmbase_filter(base); + + if (filter->enum_sink_first) + { + if (!index) + return &filter->sink.pin; + else if (index <= filter->source_count) + return &filter->sources[index - 1]->pin.pin; + } + else + { + if (index < filter->source_count) + return &filter->sources[index]->pin.pin; + else if (index == filter->source_count) + return &filter->sink.pin; + } + return NULL; +} + +static void parser_destroy(struct strmbase_filter *iface) +{ + struct parser *filter = impl_from_strmbase_filter(iface); + HRESULT hr; + + /* Don't need to clean up output pins, disconnecting input pin will do that */ + if (filter->sink.pin.peer) + { + hr = IPin_Disconnect(filter->sink.pin.peer); + assert(hr == S_OK); + hr = IPin_Disconnect(&filter->sink.pin.IPin_iface); + assert(hr == S_OK); + } + + if (filter->reader) + IAsyncReader_Release(filter->reader); + filter->reader = NULL; + + wg_parser_destroy(filter->wg_parser); + + strmbase_sink_cleanup(&filter->sink); + strmbase_filter_cleanup(&filter->filter); + free(filter); +} + +static HRESULT parser_init_stream(struct strmbase_filter *iface) +{ + struct parser *filter = impl_from_strmbase_filter(iface); + DWORD stop_flags = AM_SEEKING_NoPositioning; + const SourceSeeking *seeking; + unsigned int i; + + if (!filter->sink_connected) + return S_OK; + + filter->streaming = true; + + /* DirectShow retains the old seek positions, but resets to them every time + * it transitions from stopped -> paused. */ + + seeking = &filter->sources[0]->seek; + if (seeking->llStop) + stop_flags = AM_SEEKING_AbsolutePositioning; + wg_parser_stream_seek(filter->sources[0]->wg_stream, seeking->dRate, + seeking->llCurrent, seeking->llStop, AM_SEEKING_AbsolutePositioning, stop_flags); + + for (i = 0; i < filter->source_count; ++i) + { + struct parser_source *pin = filter->sources[i]; + HRESULT hr; + + if (!pin->pin.pin.peer) + continue; + + if (FAILED(hr = IMemAllocator_Commit(pin->pin.pAllocator))) + ERR("Failed to commit allocator, hr %#x.\n", hr); + + pin->need_segment = true; + pin->eos = false; + + pin->thread = CreateThread(NULL, 0, stream_thread, pin, 0, NULL); + } + + return S_OK; +} + +static HRESULT parser_cleanup_stream(struct strmbase_filter *iface) +{ + struct parser *filter = impl_from_strmbase_filter(iface); + unsigned int i; + + if (!filter->sink_connected) + return S_OK; + + filter->streaming = false; + + for (i = 0; i < filter->source_count; ++i) + { + struct parser_source *pin = filter->sources[i]; + + if (!pin->pin.pin.peer) + continue; + + IMemAllocator_Decommit(pin->pin.pAllocator); + + WakeConditionVariable(&pin->eos_cv); + WaitForSingleObject(pin->thread, INFINITE); + CloseHandle(pin->thread); + pin->thread = NULL; + } + + return S_OK; +} + +static const struct strmbase_filter_ops filter_ops = +{ + .filter_get_pin = parser_get_pin, + .filter_destroy = parser_destroy, + .filter_init_stream = parser_init_stream, + .filter_cleanup_stream = parser_cleanup_stream, +}; + +static inline struct parser *impl_from_strmbase_sink(struct strmbase_sink *iface) +{ + return CONTAINING_RECORD(iface, struct parser, sink); +} + +static HRESULT sink_query_accept(struct strmbase_pin *iface, const AM_MEDIA_TYPE *mt) +{ + if (IsEqualGUID(&mt->majortype, &MEDIATYPE_Stream)) + return S_OK; + return S_FALSE; +} + +static HRESULT parser_sink_connect(struct strmbase_sink *iface, IPin *peer, const AM_MEDIA_TYPE *pmt) +{ + struct parser *filter = impl_from_strmbase_sink(iface); + LONGLONG file_size, unused; + HRESULT hr = S_OK; + unsigned int i; + + filter->reader = NULL; + if (FAILED(hr = IPin_QueryInterface(peer, &IID_IAsyncReader, (void **)&filter->reader))) + return hr; + + IAsyncReader_Length(filter->reader, &file_size, &unused); + + filter->sink_connected = true; + filter->read_thread = CreateThread(NULL, 0, read_thread, filter, 0, NULL); + + if (FAILED(hr = wg_parser_connect(filter->wg_parser, file_size))) + goto err; + + if (!filter->init_gst(filter)) + { + hr = E_FAIL; + goto err; + } + + for (i = 0; i < filter->source_count; ++i) + { + struct parser_source *pin = filter->sources[i]; + + pin->seek.llDuration = pin->seek.llStop = wg_parser_stream_get_duration(pin->wg_stream); + pin->seek.llCurrent = 0; + } + + return S_OK; +err: + GST_RemoveOutputPins(filter); + IAsyncReader_Release(filter->reader); + filter->reader = NULL; + return hr; +} + +static void parser_sink_disconnect(struct strmbase_sink *iface) +{ + struct parser *filter = impl_from_strmbase_sink(iface); + + GST_RemoveOutputPins(filter); + + IAsyncReader_Release(filter->reader); + filter->reader = NULL; +} + +static const struct strmbase_sink_ops sink_ops = +{ + .base.pin_query_accept = sink_query_accept, + .sink_connect = parser_sink_connect, + .sink_disconnect = parser_sink_disconnect, +}; + +static BOOL decodebin_parser_filter_init_gst(struct parser *filter) +{ + struct wg_parser *parser = filter->wg_parser; + const char *sgi = getenv("SteamGameId"); + const WCHAR *format; + unsigned int i, stream_count; + WCHAR source_name[20]; + + /* King of Fighters XIII requests the WMV decoder filter pins by name + * to connect them to a Sample Grabber filter. + */ + format = (sgi && !strcmp(sgi, "222940")) ? L"out%u" : L"Stream %02u"; + + stream_count = wg_parser_get_stream_count(parser); + for (i = 0; i < stream_count; ++i) + { + swprintf(source_name, ARRAY_SIZE(source_name), format, i); + if (!create_pin(filter, wg_parser_get_stream(parser, i), source_name)) + return FALSE; + } + + return TRUE; +} + +static HRESULT decodebin_parser_source_query_accept(struct parser_source *pin, const AM_MEDIA_TYPE *mt) +{ + struct wg_format format; + + /* At least make sure we can convert it to wg_format. */ + return amt_to_wg_format(mt, &format) ? S_OK : S_FALSE; +} + +static HRESULT decodebin_parser_source_get_media_type(struct parser_source *pin, + unsigned int index, AM_MEDIA_TYPE *mt) +{ + struct wg_format format; + + static const enum wg_video_format video_formats[] = + { + /* Try to prefer YUV formats over RGB ones. Most decoders output