Wine-Devel
By thread
wine-devel@list.winehq.org
By month
Messages by month
- ----- 2026 -----
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2025 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
November 2021
- 83 participants
- 2620 messages
[PATCH 4/6] mfreadwrite/tests: Add some video media type attributes tests.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/mfreadwrite/tests/mfplat.c | 195 ++++++++++++++++++++-
dlls/mfreadwrite/tests/resource.rc | 2 +
dlls/mfreadwrite/tests/test-40x36-h264.mp4 | Bin 0 -> 11151 bytes
3 files changed, 196 insertions(+), 1 deletion(-)
create mode 100644 dlls/mfreadwrite/tests/test-40x36-h264.mp4
diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c
index 5a7d021ef84..051c7206e96 100644
--- a/dlls/mfreadwrite/tests/mfplat.c
+++ b/dlls/mfreadwrite/tests/mfplat.c
@@ -28,6 +28,7 @@
#include "initguid.h"
#include "ole2.h"
+#include "uuids.h"
DEFINE_GUID(GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
@@ -1261,7 +1262,9 @@ struct media_type_item
{
const GUID *key;
const PROPVARIANT value;
+ BOOL optional;
BOOL todo_missing;
+ BOOL todo_value;
};
struct media_type_desc
@@ -1287,7 +1290,13 @@ static void check_media_type_items(IMFMediaType *media_type, const struct media_
hr = IMFMediaType_GetItemByIndex(media_type, i, &key, &value);
ok(hr == S_OK, "GetItemByIndex returned hr %#x\n", hr);
- for (j = 0; expect->items[j].key; j++) if (IsEqualGUID(expect->items[j].key, &key)) break;
+ for (j = 0; expect->items[j].key; j++)
+ {
+ if (!IsEqualGUID(expect->items[j].key, &key))
+ continue;
+ if (!expect->items[j].optional || !PropVariantCompareEx(&value, &expect->items[j].value, 0, 0))
+ break;
+ }
if (!expect->items[j].key)
{
todo_wine_if(expect->todo_spurious > spurious_count)
@@ -1301,6 +1310,7 @@ static void check_media_type_items(IMFMediaType *media_type, const struct media_
if (!strcmp(winetest_platform, "wine"))
ok(!expect->items[j].todo_missing, "attribute not missing %s\n", debugstr_guid(&key));
+ todo_wine_if(expect->items[j].todo_value)
ok(!PropVariantCompareEx(&value, &expect->items[j].value, 0, 0), "got %s, expected %s.\n",
debugstr_propvariant(&value), debugstr_propvariant(&expect->items[j].value));
PropVariantClear(&value);
@@ -1312,6 +1322,7 @@ static void check_media_type_items(IMFMediaType *media_type, const struct media_
for (j = 0; expect->items[j].key; j++)
{
if (found[j]) continue;
+ if (expect->items[j].optional) continue;
todo_wine_if(expect->items[j].todo_missing)
ok(0, "missing attribute %s\n", debugstr_guid(expect->items[j].key));
}
@@ -1400,6 +1411,118 @@ static void test_media_types(void)
.todo_spurious = 1,
};
+ static const BYTE mpeg_sequence_header[] =
+ {
+ 0x00,0x00,0x01,0x67,0x64,0x00,0x14,0xac,0xd9,0x4d,0xf9,0x67,0xc0,0x5a,0x83,0x03,
+ 0x03,0x52,0x80,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x1e,0x47,0x8a,0x14,0xcb,0x00,
+ 0x00,0x01,0x68,0xeb,0xec,0xb2,0x2c,
+ };
+ static const BYTE mpeg4_sample_description[] =
+ {
+ 0x00,0x00,0x00,0xd1,0x73,0x74,0x73,0x64,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+ 0x00,0x00,0x00,0xc1,0x61,0x76,0x63,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x28,0x00,0x24,0x00,0x48,0x00,0x00,0x00,0x48,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
+ 0x00,0x00,0x00,0x18,0xff,0xff,0x00,0x00,0x00,0x34,0x61,0x76,0x63,0x43,0x01,0x64,
+ 0x00,0x14,0xff,0xe1,0x00,0x1c,0x67,0x64,0x00,0x14,0xac,0xd9,0x4d,0xf9,0x67,0xc0,
+ 0x5a,0x83,0x03,0x03,0x52,0x80,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x1e,0x47,0x8a,
+ 0x14,0xcb,0x01,0x00,0x05,0x68,0xeb,0xec,0xb2,0x2c,0x00,0x00,0x00,0x14,0x62,0x74,
+ 0x72,0x74,0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x01,0x34,0x38,0x00,0x00,
+ 0x00,0x13,0x63,0x6f,0x6c,0x72,0x6e,0x63,0x6c,0x78,0x00,0x06,0x00,0x06,0x00,0x06,
+ 0x00,0x00,0x00,0x00,0x10,0x70,0x61,0x73,0x70,0x00,0x00,0x00,0x01,0x00,0x00,0x00,
+ 0x01,
+ };
+ static const struct media_type_desc video_40x36_h264_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Video}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFVideoFormat_H264}, .todo_value = TRUE},
+ {.key = &MF_MT_AM_FORMAT_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&FORMAT_MPEG2Video}, .todo_missing = TRUE},
+ {.key = &MF_MT_SAMPLE_SIZE, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_AVG_BITRATE, .value = {.vt = VT_UI4, .ulVal = 78904}, .todo_missing = TRUE},
+ {.key = &MF_MT_FRAME_RATE, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (30ull << 32) | 1ull}}},
+ {.key = &MF_MT_FRAME_SIZE, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (40ull << 32) | 36ull}}},
+ {.key = &MF_MT_INTERLACE_MODE, .value = {.vt = VT_UI4, .ulVal = MFVideoInterlace_MixedInterlaceOrProgressive}, .todo_missing = TRUE},
+ {
+ .key = &MF_MT_MPEG_SEQUENCE_HEADER,
+ .value =
+ {
+ .vt = VT_VECTOR|VT_UI1,
+ .caub =
+ {
+ .pElems = (BYTE *)mpeg_sequence_header,
+ .cElems = ARRAY_SIZE(mpeg_sequence_header)
+ }
+ },
+ .todo_missing = TRUE
+ },
+ {.key = &MF_MT_MPEG2_LEVEL, .value = {.vt = VT_UI4, .ulVal = 20}, .todo_missing = TRUE},
+ {.key = &MF_MT_MPEG2_PROFILE, .value = {.vt = VT_UI4, .ulVal = 100}, .todo_missing = TRUE},
+ {.key = &MF_MT_PIXEL_ASPECT_RATIO, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (1ull << 32) | 1ull}}, .todo_missing = TRUE},
+ {
+ .key = &MF_MT_MPEG4_SAMPLE_DESCRIPTION,
+ .value =
+ {
+ .vt = VT_VECTOR|VT_UI1,
+ .caub =
+ {
+ .pElems = (BYTE *)mpeg4_sample_description,
+ .cElems = ARRAY_SIZE(mpeg4_sample_description)
+ }
+ },
+ .todo_missing = TRUE
+ },
+ {.key = &MF_MT_MPEG4_CURRENT_SAMPLE_ENTRY, .value = {.vt = VT_UI4}, .todo_missing = TRUE},
+ {.key = &MF_MT_VIDEO_ROTATION, .value = {.vt = VT_UI4, .ulVal = 0}, .optional = TRUE /* <= win7 */},
+ {.key = &MF_PROGRESSIVE_CODING_CONTENT, .value = {.vt = VT_UI4, .ulVal = 1}, .optional = TRUE /* <= win7 */, .todo_missing = TRUE},
+ {.key = &MF_NALU_LENGTH_SET, .value = {.vt = VT_UI4, .ulVal = 1}, .optional = TRUE /* <= win7 */, .todo_missing = TRUE},
+ },
+ .todo_spurious = 2,
+ };
+ static const struct media_type_desc video_40x36_h264_nv12_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Video}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFVideoFormat_NV12}},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_COMPRESSED, .value = {.vt = VT_UI4, .ulVal = 0}},
+ {.key = &MF_MT_FIXED_SIZE_SAMPLES, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_SAMPLE_SIZE, .value = {.vt = VT_UI4, .ulVal = 40 * 36 * 3 / 2}, .todo_missing = TRUE},
+ {.key = &MF_MT_AVG_BIT_ERROR_RATE, .value = {.vt = VT_UI4, .ulVal = 0}, .todo_missing = TRUE},
+ {.key = &MF_MT_AVG_BITRATE, .value = {.vt = VT_UI4, .ulVal = 78904}, .todo_missing = TRUE},
+ {.key = &MF_MT_DEFAULT_STRIDE, .value = {.vt = VT_UI4, .ulVal = 40}, .todo_missing = TRUE},
+ {.key = &MF_MT_FRAME_RATE, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (30ull << 32) | 1ull}}},
+ {.key = &MF_MT_FRAME_SIZE, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (40ull << 32) | 36ull}}},
+ {.key = &MF_MT_INTERLACE_MODE, .value = {.vt = VT_UI4, .ulVal = MFVideoInterlace_MixedInterlaceOrProgressive}, .todo_missing = TRUE},
+ {.key = &MF_MT_PIXEL_ASPECT_RATIO, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (1ull << 32) | 1ull}}, .todo_missing = TRUE},
+ {.key = &MF_MT_VIDEO_ROTATION, .value = {.vt = VT_UI4, .ulVal = 0}, .optional = TRUE /* <= win7 */},
+ },
+ };
+ static const struct media_type_desc video_40x36_h264_i420_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Video}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFVideoFormat_I420}},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_COMPRESSED, .value = {.vt = VT_UI4, .ulVal = 0}},
+ {.key = &MF_MT_FIXED_SIZE_SAMPLES, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_SAMPLE_SIZE, .value = {.vt = VT_UI4, .ulVal = 40 * 36 * 3 / 2}, .todo_missing = TRUE},
+ {.key = &MF_MT_AVG_BIT_ERROR_RATE, .value = {.vt = VT_UI4, .ulVal = 0}, .todo_missing = TRUE},
+ {.key = &MF_MT_AVG_BITRATE, .value = {.vt = VT_UI4, .ulVal = 78904}, .todo_missing = TRUE},
+ {.key = &MF_MT_DEFAULT_STRIDE, .value = {.vt = VT_UI4, .ulVal = 40}, .todo_missing = TRUE},
+ {.key = &MF_MT_FRAME_RATE, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (30ull << 32) | 1ull}}},
+ {.key = &MF_MT_FRAME_SIZE, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (40ull << 32) | 36ull}}},
+ {.key = &MF_MT_INTERLACE_MODE, .value = {.vt = VT_UI4, .ulVal = MFVideoInterlace_MixedInterlaceOrProgressive}, .todo_missing = TRUE},
+ {.key = &MF_MT_PIXEL_ASPECT_RATIO, .value = {.vt = VT_UI8, .uhVal = {.QuadPart = (1ull << 32) | 1ull}}, .todo_missing = TRUE},
+ {.key = &MF_MT_VIDEO_ROTATION, .value = {.vt = VT_UI4, .ulVal = 0}, .optional = TRUE /* <= win7 */},
+ },
+ };
+
struct test_audio_format
{
GUID subtype;
@@ -1408,12 +1531,20 @@ static void test_media_types(void)
const struct media_type_desc *current;
};
+ struct test_video_format
+ {
+ GUID subtype;
+ const struct media_type_desc *current;
+ };
+
struct test_media_type
{
const char *resource;
const struct media_type_desc *native;
struct test_audio_format *audio_formats;
UINT audio_format_count;
+ struct test_video_format *video_formats;
+ UINT video_format_count;
};
struct test_audio_format test_audio_formats[] =
@@ -1424,6 +1555,12 @@ static void test_media_types(void)
{.subtype = MFAudioFormat_Float, .bits = 32, .rate = 48000, .current = &audio_48000_f32_desc},
};
+ struct test_video_format test_h264_formats[] =
+ {
+ {.subtype = MFVideoFormat_NV12, .current = &video_40x36_h264_nv12_desc},
+ {.subtype = MFVideoFormat_I420, .current = &video_40x36_h264_i420_desc},
+ };
+
struct test_media_type tests[] =
{
{
@@ -1438,9 +1575,16 @@ static void test_media_types(void)
.audio_formats = test_audio_formats,
.audio_format_count = ARRAY_SIZE(test_audio_formats),
},
+ {
+ .resource = "test-40x36-h264.mp4",
+ .native = &video_40x36_h264_desc,
+ .video_formats = test_h264_formats,
+ .video_format_count = ARRAY_SIZE(test_h264_formats),
+ },
};
struct test_audio_format audio_format;
+ struct test_video_format video_format;
IMFMediaType *media_type;
IMFSourceReader *reader;
IMFByteStream *stream;
@@ -1518,6 +1662,55 @@ static void test_media_types(void)
IMFByteStream_Release(stream);
}
+ for (j = 0; j < tests[i].video_format_count; ++j)
+ {
+ video_format = tests[i].video_formats[j];
+ stream = get_resource_stream(tests[i].resource);
+ if (FAILED(hr = MFCreateSourceReaderFromByteStream(stream, NULL, &reader)))
+ {
+ todo_wine
+ win_skip("MFCreateSourceReaderFromByteStream() failed\n");
+ IMFByteStream_Release(stream);
+ continue;
+ }
+ ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr);
+
+ hr = IMFSourceReader_GetNativeMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, &media_type);
+ ok(hr == S_OK, "Failed to get native mediatype, hr %#x.\n", hr);
+ winetest_push_context("native video");
+ check_media_type_items(media_type, tests[i].native);
+ IMFMediaType_Release(media_type);
+ winetest_pop_context();
+
+ hr = IMFSourceReader_GetNativeMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, 1, &media_type);
+ todo_wine
+ ok(hr == MF_E_NO_MORE_TYPES, "Expected only one native mediatype, hr %#x.\n", hr);
+ if (hr == S_OK) IMFMediaType_Release(media_type);
+
+ hr = IMFSourceReader_SetStreamSelection(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, TRUE);
+ ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+
+ hr = MFCreateMediaType(&media_type);
+ ok(hr == S_OK, "Failed to create media type, hr %#x.\n", hr);
+ hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Video);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+ hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &video_format.subtype);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+ hr = IMFSourceReader_SetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, media_type);
+ ok(hr == S_OK, "Failed setting current media type %u, hr %#x.\n", j, hr);
+ IMFMediaType_Release(media_type);
+
+ hr = IMFSourceReader_GetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_VIDEO_STREAM, &media_type);
+ ok(hr == S_OK, "Failed to get current mediatype, hr %#x.\n", hr);
+ winetest_push_context("current video %u", j);
+ check_media_type_items(media_type, video_format.current);
+ winetest_pop_context();
+ IMFMediaType_Release(media_type);
+
+ IMFSourceReader_Release(reader);
+ IMFByteStream_Release(stream);
+ }
+
winetest_pop_context();
}
}
diff --git a/dlls/mfreadwrite/tests/resource.rc b/dlls/mfreadwrite/tests/resource.rc
index 5b3269fef1d..5ae9636d671 100644
--- a/dlls/mfreadwrite/tests/resource.rc
+++ b/dlls/mfreadwrite/tests/resource.rc
@@ -24,3 +24,5 @@ test.wav RCDATA test.wav
/* @makedep: test-48000.wav */
test-48000.wav RCDATA test-48000.wav
+/* @makedep: test-40x36-h264.mp4 */
+test-40x36-h264.mp4 RCDATA test-40x36-h264.mp4
diff --git a/dlls/mfreadwrite/tests/test-40x36-h264.mp4 b/dlls/mfreadwrite/tests/test-40x36-h264.mp4
new file mode 100644
index 0000000000000000000000000000000000000000..3b3771a7c67322ed99721a026b9f17d8c7829856
GIT binary patch
literal 11151
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
literal 0
HcmV?d00001
--
2.33.1
Nov. 8, 2021
[PATCH 3/6] mfreadwrite/tests: Add some audio media type attributes tests.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
dlls/mfreadwrite/tests/Makefile.in | 2 +-
dlls/mfreadwrite/tests/mfplat.c | 293 ++++++++++++++++++++++++++
dlls/mfreadwrite/tests/resource.rc | 4 +
dlls/mfreadwrite/tests/test-48000.wav | Bin 0 -> 2104 bytes
4 files changed, 298 insertions(+), 1 deletion(-)
create mode 100644 dlls/mfreadwrite/tests/test-48000.wav
diff --git a/dlls/mfreadwrite/tests/Makefile.in b/dlls/mfreadwrite/tests/Makefile.in
index c7476c14c6b..f37a371ef2c 100644
--- a/dlls/mfreadwrite/tests/Makefile.in
+++ b/dlls/mfreadwrite/tests/Makefile.in
@@ -1,5 +1,5 @@
TESTDLL = mfreadwrite.dll
-IMPORTS = ole32 user32 d3d9 dxva2 mfplat mfreadwrite mfuuid
+IMPORTS = ole32 user32 d3d9 dxva2 mfplat mfreadwrite mfuuid propsys
C_SRCS = \
mfplat.c
diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c
index e3ff7f6e7aa..5a7d021ef84 100644
--- a/dlls/mfreadwrite/tests/mfplat.c
+++ b/dlls/mfreadwrite/tests/mfplat.c
@@ -39,6 +39,7 @@ DEFINE_GUID(GUID_NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#include "mfreadwrite.h"
#include "d3d9.h"
#include "dxva2api.h"
+#include "propvarutil.h"
#include "wine/heap.h"
#include "wine/test.h"
@@ -1230,6 +1231,297 @@ done:
DestroyWindow(window);
}
+static const char *debugstr_propvariant(const PROPVARIANT *var)
+{
+ if (!var) return "(null)";
+ switch (var->vt)
+ {
+ case VT_NULL: return "type null";
+ case VT_EMPTY: return "type empty";
+ case VT_BOOL: return wine_dbg_sprintf("type %#x, value %s", var->vt, var->boolVal ? "true" : "false");
+ case VT_I1: return wine_dbg_sprintf("type %#x, value %d", var->vt, var->cVal);
+ case VT_UI1: return wine_dbg_sprintf("type %#x, value %u", var->vt, var->bVal);
+ case VT_I2: return wine_dbg_sprintf("type %#x, value %d", var->vt, var->iVal);
+ case VT_UI2: return wine_dbg_sprintf("type %#x, value %u", var->vt, var->uiVal);
+ case VT_I4: return wine_dbg_sprintf("type %#x, value %d", var->vt, var->lVal);
+ case VT_UI4: return wine_dbg_sprintf("type %#x, value %u", var->vt, var->ulVal);
+ case VT_I8: return wine_dbg_sprintf("type %#x, value %I64d", var->vt, (INT64)var->hVal.QuadPart);
+ case VT_UI8: return wine_dbg_sprintf("type %#x, value %I64u", var->vt, (UINT64)var->uhVal.QuadPart);
+ case VT_R4: return wine_dbg_sprintf("type %#x, value %f", var->vt, var->fltVal);
+ case VT_R8: return wine_dbg_sprintf("type %#x, value %f", var->vt, var->dblVal);
+ case VT_BSTR:
+ case VT_LPWSTR: return debugstr_w(var->bstrVal);
+ case VT_LPSTR: return debugstr_a(var->pszVal);
+ case VT_CLSID: return debugstr_guid(var->puuid);
+ default: return wine_dbg_sprintf("type %#x", var->vt);
+ }
+}
+
+struct media_type_item
+{
+ const GUID *key;
+ const PROPVARIANT value;
+ BOOL todo_missing;
+};
+
+struct media_type_desc
+{
+ struct media_type_item items[1024];
+ UINT todo_spurious;
+};
+
+static void check_media_type_items(IMFMediaType *media_type, const struct media_type_desc *expect)
+{
+ UINT32 i, j, count, spurious_count = 0;
+ BOOL found[1024] = {0};
+ PROPVARIANT value;
+ HRESULT hr;
+ GUID key;
+
+ hr = IMFMediaType_GetCount(media_type, &count);
+ ok(hr == S_OK, "GetCount returned hr %#x\n", hr);
+
+ for (i = 0; i < count; i++)
+ {
+ PropVariantInit(&value);
+ hr = IMFMediaType_GetItemByIndex(media_type, i, &key, &value);
+ ok(hr == S_OK, "GetItemByIndex returned hr %#x\n", hr);
+
+ for (j = 0; expect->items[j].key; j++) if (IsEqualGUID(expect->items[j].key, &key)) break;
+ if (!expect->items[j].key)
+ {
+ todo_wine_if(expect->todo_spurious > spurious_count)
+ ok(0, "spurious attribute %s\n", debugstr_guid(&key));
+ spurious_count++;
+ continue;
+ }
+
+ ok(!found[j], "duplicate attribute %s\n", debugstr_guid(&key));
+ found[j] = TRUE;
+
+ if (!strcmp(winetest_platform, "wine"))
+ ok(!expect->items[j].todo_missing, "attribute not missing %s\n", debugstr_guid(&key));
+ ok(!PropVariantCompareEx(&value, &expect->items[j].value, 0, 0), "got %s, expected %s.\n",
+ debugstr_propvariant(&value), debugstr_propvariant(&expect->items[j].value));
+ PropVariantClear(&value);
+ }
+
+ if (!strcmp(winetest_platform, "wine"))
+ ok(expect->todo_spurious == spurious_count, "%u spurious attribute\n", spurious_count);
+
+ for (j = 0; expect->items[j].key; j++)
+ {
+ if (found[j]) continue;
+ todo_wine_if(expect->items[j].todo_missing)
+ ok(0, "missing attribute %s\n", debugstr_guid(expect->items[j].key));
+ }
+}
+
+static void test_media_types(void)
+{
+ static const struct media_type_desc audio_44100_s8_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 44100}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_BLOCK_ALIGNMENT, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_NUM_CHANNELS, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Audio}},
+ {.key = &MF_MT_AUDIO_SAMPLES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 44100}},
+ {.key = &MF_MT_AUDIO_PREFER_WAVEFORMATEX, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_AUDIO_BITS_PER_SAMPLE, .value = {.vt = VT_UI4, .ulVal = 8}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFAudioFormat_PCM}},
+ },
+ .todo_spurious = 1,
+ };
+ static const struct media_type_desc audio_48000_s8_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 48000}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_BLOCK_ALIGNMENT, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_NUM_CHANNELS, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Audio}},
+ {.key = &MF_MT_AUDIO_SAMPLES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 48000}},
+ {.key = &MF_MT_AUDIO_PREFER_WAVEFORMATEX, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_AUDIO_BITS_PER_SAMPLE, .value = {.vt = VT_UI4, .ulVal = 8}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFAudioFormat_PCM}},
+ },
+ .todo_spurious = 1,
+ };
+ static const struct media_type_desc audio_48000_s16_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 96000}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_BLOCK_ALIGNMENT, .value = {.vt = VT_UI4, .ulVal = 2}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_NUM_CHANNELS, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Audio}},
+ {.key = &MF_MT_AUDIO_SAMPLES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 48000}},
+ {.key = &MF_MT_AUDIO_PREFER_WAVEFORMATEX, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_AUDIO_BITS_PER_SAMPLE, .value = {.vt = VT_UI4, .ulVal = 16}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFAudioFormat_PCM}},
+ },
+ .todo_spurious = 1,
+ };
+ static const struct media_type_desc audio_44100_f32_desc=
+ {
+ .items =
+ {
+ {.key = &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 176400}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_BLOCK_ALIGNMENT, .value = {.vt = VT_UI4, .ulVal = 4}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_NUM_CHANNELS, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Audio}},
+ {.key = &MF_MT_AUDIO_SAMPLES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 44100}},
+ {.key = &MF_MT_AUDIO_PREFER_WAVEFORMATEX, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_AUDIO_BITS_PER_SAMPLE, .value = {.vt = VT_UI4, .ulVal = 32}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFAudioFormat_Float}},
+ },
+ .todo_spurious = 1,
+ };
+ static const struct media_type_desc audio_48000_f32_desc =
+ {
+ .items =
+ {
+ {.key = &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 192000}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_BLOCK_ALIGNMENT, .value = {.vt = VT_UI4, .ulVal = 4}, .todo_missing = TRUE},
+ {.key = &MF_MT_AUDIO_NUM_CHANNELS, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_MAJOR_TYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFMediaType_Audio}},
+ {.key = &MF_MT_AUDIO_SAMPLES_PER_SECOND, .value = {.vt = VT_UI4, .ulVal = 48000}},
+ {.key = &MF_MT_AUDIO_PREFER_WAVEFORMATEX, .value = {.vt = VT_UI4, .ulVal = 1}, .todo_missing = TRUE},
+ {.key = &MF_MT_ALL_SAMPLES_INDEPENDENT, .value = {.vt = VT_UI4, .ulVal = 1}},
+ {.key = &MF_MT_AUDIO_BITS_PER_SAMPLE, .value = {.vt = VT_UI4, .ulVal = 32}},
+ {.key = &MF_MT_SUBTYPE, .value = {.vt = VT_CLSID, .puuid = (GUID *)&MFAudioFormat_Float}},
+ },
+ .todo_spurious = 1,
+ };
+
+ struct test_audio_format
+ {
+ GUID subtype;
+ UINT32 bits;
+ UINT32 rate;
+ const struct media_type_desc *current;
+ };
+
+ struct test_media_type
+ {
+ const char *resource;
+ const struct media_type_desc *native;
+ struct test_audio_format *audio_formats;
+ UINT audio_format_count;
+ };
+
+ struct test_audio_format test_audio_formats[] =
+ {
+ {.subtype = MFAudioFormat_PCM, .bits = 8, .rate = 44100, .current = &audio_44100_s8_desc},
+ {.subtype = MFAudioFormat_Float, .bits = 32, .rate = 44100, .current = &audio_44100_f32_desc},
+ {.subtype = MFAudioFormat_PCM, .bits = 8, .rate = 48000, .current = &audio_48000_s8_desc},
+ {.subtype = MFAudioFormat_Float, .bits = 32, .rate = 48000, .current = &audio_48000_f32_desc},
+ };
+
+ struct test_media_type tests[] =
+ {
+ {
+ .resource = "test.wav",
+ .native = &audio_44100_s8_desc,
+ .audio_formats = test_audio_formats,
+ .audio_format_count = ARRAY_SIZE(test_audio_formats),
+ },
+ {
+ .resource = "test-48000.wav",
+ .native = &audio_48000_s16_desc,
+ .audio_formats = test_audio_formats,
+ .audio_format_count = ARRAY_SIZE(test_audio_formats),
+ },
+ };
+
+ struct test_audio_format audio_format;
+ IMFMediaType *media_type;
+ IMFSourceReader *reader;
+ IMFByteStream *stream;
+ HRESULT hr;
+ int i, j;
+
+ if (!pMFCreateMFByteStreamOnStream)
+ {
+ win_skip("MFCreateMFByteStreamOnStream() not found\n");
+ return;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(tests); ++i)
+ {
+ winetest_push_context(tests[i].resource);
+
+ for (j = 0; j < tests[i].audio_format_count; ++j)
+ {
+ audio_format = tests[i].audio_formats[j];
+ stream = get_resource_stream(tests[i].resource);
+ if (FAILED(hr = MFCreateSourceReaderFromByteStream(stream, NULL, &reader)))
+ {
+ todo_wine
+ win_skip("MFCreateSourceReaderFromByteStream() failed\n");
+ IMFByteStream_Release(stream);
+ continue;
+ }
+ ok(hr == S_OK, "Failed to create source reader, hr %#x.\n", hr);
+
+ hr = IMFSourceReader_GetNativeMediaType(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 0, &media_type);
+ ok(hr == S_OK, "Failed to get native mediatype, hr %#x.\n", hr);
+ winetest_push_context("native audio");
+ check_media_type_items(media_type, tests[i].native);
+ IMFMediaType_Release(media_type);
+ winetest_pop_context();
+
+ hr = IMFSourceReader_GetNativeMediaType(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, 1, &media_type);
+ todo_wine
+ ok(hr == MF_E_NO_MORE_TYPES, "Expected only one native mediatype, hr %#x.\n", hr);
+ if (hr == S_OK) IMFMediaType_Release(media_type);
+
+ hr = IMFSourceReader_SetStreamSelection(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, TRUE);
+ ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
+
+ hr = MFCreateMediaType(&media_type);
+ ok(hr == S_OK, "Failed to create media type, hr %#x.\n", hr);
+ hr = IMFMediaType_SetGUID(media_type, &MF_MT_MAJOR_TYPE, &MFMediaType_Audio);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+ hr = IMFMediaType_SetGUID(media_type, &MF_MT_SUBTYPE, &audio_format.subtype);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+ hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BITS_PER_SAMPLE, audio_format.bits);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+ hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_SAMPLES_PER_SECOND, audio_format.rate);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+
+ hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_BLOCK_ALIGNMENT, audio_format.bits / 8);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+ hr = IMFMediaType_SetUINT32(media_type, &MF_MT_AUDIO_AVG_BYTES_PER_SECOND, audio_format.rate * audio_format.bits / 8);
+ ok(hr == S_OK, "Failed to set attribute, hr %#x.\n", hr);
+
+ hr = IMFSourceReader_SetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, NULL, media_type);
+ todo_wine
+ ok(hr == S_OK || broken(hr == MF_E_TOPO_CODEC_NOT_FOUND) /* <= win7 */, "Failed setting current media type %u, hr %#x.\n", j, hr);
+ IMFMediaType_Release(media_type);
+ if (hr != S_OK) continue;
+
+ hr = IMFSourceReader_GetCurrentMediaType(reader, MF_SOURCE_READER_FIRST_AUDIO_STREAM, &media_type);
+ ok(hr == S_OK, "Failed to get current mediatype, hr %#x.\n", hr);
+ winetest_push_context("current audio %u", j);
+ check_media_type_items(media_type, audio_format.current);
+ winetest_pop_context();
+ IMFMediaType_Release(media_type);
+
+ IMFSourceReader_Release(reader);
+ IMFByteStream_Release(stream);
+ }
+
+ winetest_pop_context();
+ }
+}
+
START_TEST(mfplat)
{
HRESULT hr;
@@ -1242,6 +1534,7 @@ START_TEST(mfplat)
test_factory();
test_source_reader();
test_source_reader_from_media_source();
+ test_media_types();
test_reader_d3d9();
hr = MFShutdown();
diff --git a/dlls/mfreadwrite/tests/resource.rc b/dlls/mfreadwrite/tests/resource.rc
index f54212a8c8f..5b3269fef1d 100644
--- a/dlls/mfreadwrite/tests/resource.rc
+++ b/dlls/mfreadwrite/tests/resource.rc
@@ -20,3 +20,7 @@
/* @makedep: test.wav */
test.wav RCDATA test.wav
+
+/* @makedep: test-48000.wav */
+test-48000.wav RCDATA test-48000.wav
+
diff --git a/dlls/mfreadwrite/tests/test-48000.wav b/dlls/mfreadwrite/tests/test-48000.wav
new file mode 100644
index 0000000000000000000000000000000000000000..dedd660bb9a97a9cacedabaddc1b1bafd2f4a176
GIT binary patch
literal 2104
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
literal 0
HcmV?d00001
--
2.33.1
Nov. 8, 2021
[PATCH 2/6] include: Add some MPEG4 container attributes to mfidl.idl.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
include/mfidl.idl | 3 +++
1 file changed, 3 insertions(+)
diff --git a/include/mfidl.idl b/include/mfidl.idl
index 511f97e3ac6..4df1d0c85ba 100644
--- a/include/mfidl.idl
+++ b/include/mfidl.idl
@@ -1589,6 +1589,9 @@ cpp_quote("EXTERN_GUID(MR_AUDIO_POLICY_SERVICE, 0x911fd737, 0x6775, 0x4ab0, 0xa6
cpp_quote("EXTERN_GUID(MF_PROPERTY_HANDLER_SERVICE, 0xa3face02, 0x32b8, 0x41dd, 0x90, 0xe7, 0x5f, 0xef, 0x7c, 0x89, 0x91, 0xb5);")
cpp_quote("EXTERN_GUID(MF_WORKQUEUE_SERVICES, 0x8e37d489, 0x41e0, 0x413a, 0x90, 0x68, 0x28, 0x7c, 0x88, 0x6d, 0x8d, 0xda);")
+cpp_quote("EXTERN_GUID(MF_PROGRESSIVE_CODING_CONTENT, 0x8f020eea, 0x1508, 0x471f, 0x9d, 0xa6, 0x50, 0x7d, 0x7c, 0xfa, 0x40, 0xdb);")
+cpp_quote("EXTERN_GUID(MF_NALU_LENGTH_SET, 0xa7911d53, 0x12a4, 0x4965, 0xae, 0x70, 0x6e, 0xad, 0xd6, 0xff, 0x05, 0x51);")
+
cpp_quote("EXTERN_GUID(MF_SAMPLEGRABBERSINK_SAMPLE_TIME_OFFSET, 0x62e3d776, 0x8100, 0x4e03, 0xa6, 0xe8, 0xbd, 0x38, 0x57, 0xac, 0x9c, 0x47);")
cpp_quote("EXTERN_GUID(MF_SAMPLEGRABBERSINK_IGNORE_CLOCK, 0x0efda2c0, 0x2b69, 0x4e2e, 0xab, 0x8d, 0x46, 0xdc, 0xbf, 0xf7, 0xd2, 0x5d);")
--
2.33.1
Nov. 8, 2021
[PATCH 1/6] propsys: Fix VT_UI4 comparison, use ulVal instead of uiVal.
by Rémi Bernon
Signed-off-by: Rémi Bernon <rbernon(a)codeweavers.com>
---
v2: Use a generic function to check media type items against expected
items, instead of hardcoding attributes.
dlls/propsys/propvar.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/dlls/propsys/propvar.c b/dlls/propsys/propvar.c
index 977fbebb918..be27c5cb641 100644
--- a/dlls/propsys/propvar.c
+++ b/dlls/propsys/propvar.c
@@ -865,7 +865,7 @@ INT WINAPI PropVariantCompareEx(REFPROPVARIANT propvar1, REFPROPVARIANT propvar2
CMP_NUM_VALUE(lVal);
break;
case VT_UI4:
- CMP_NUM_VALUE(uiVal);
+ CMP_NUM_VALUE(ulVal);
break;
case VT_I8:
CMP_NUM_VALUE(hVal.QuadPart);
--
2.33.1
Nov. 8, 2021
[PATCH 13/13] dbghelp/pdb: add line number to inline sites
by Eric Pouech
requires to join DEBUG_S_INLINEES subsection with symbol annotations
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/msc.c | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 84 insertions(+), 2 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 1be73b723f0..c0d09b75c56 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -1814,6 +1814,70 @@ static BOOL codeview_advance_binannot(struct cv_binannot* cvba)
return TRUE;
}
+static inline int binannot_getsigned(unsigned i)
+{
+ return (i & 1) ? -(int)(i >> 1) : (int)(i >> 1);
+}
+
+static BOOL cv_dbgsubsect_find_inlinee(const struct msc_debug_info* msc_dbg,
+ unsigned inlineeid,
+ const struct cv_module_snarf* cvmod,
+ const struct CV_DebugSSubsectionHeader_t* hdr_files,
+ unsigned* srcfile, unsigned* srcline)
+{
+ const struct CV_DebugSSubsectionHeader_t* hdr;
+ const struct CV_DebugSSubsectionHeader_t* next_hdr;
+ const struct CV_InlineeSourceLine_t* inlsrc;
+ const struct CV_InlineeSourceLineEx_t* inlsrcex;
+ const struct CV_Checksum_t* chksms;
+
+ for (hdr = cvmod->dbgsubsect; CV_IS_INSIDE(hdr, cvmod->dbgsubsect + cvmod->dbgsubsect_size); hdr = next_hdr)
+ {
+ next_hdr = CV_RECORD_GAP(hdr, hdr->cbLen);
+ if (hdr->type != DEBUG_S_INLINEELINES) continue;
+ /* subsection starts with a DWORD signature */
+ switch (*(DWORD*)CV_RECORD_AFTER(hdr))
+ {
+ case CV_INLINEE_SOURCE_LINE_SIGNATURE:
+ inlsrc = CV_RECORD_GAP(hdr, sizeof(DWORD));
+ while (CV_IS_INSIDE(inlsrc, next_hdr))
+ {
+ if (inlsrc->inlinee == inlineeid)
+ {
+ chksms = CV_RECORD_GAP(hdr_files, inlsrc->fileId);
+ if (!CV_IS_INSIDE(chksms, CV_RECORD_GAP(hdr_files, hdr_files->cbLen))) return FALSE;
+ *srcfile = source_new(msc_dbg->module, NULL,
+ (chksms->strOffset < cvmod->strsize) ? cvmod->strimage + chksms->strOffset : "<<str-out-of-bounds>>");
+ *srcline = inlsrc->sourceLineNum;
+ return TRUE;
+ }
+ ++inlsrc;
+ }
+ break;
+ case CV_INLINEE_SOURCE_LINE_SIGNATURE_EX:
+ inlsrcex = CV_RECORD_GAP(hdr, sizeof(DWORD));
+ while (CV_IS_INSIDE(inlsrcex, next_hdr))
+ {
+ if (inlsrcex->inlinee == inlineeid)
+ {
+ chksms = CV_RECORD_GAP(hdr_files, inlsrcex->fileId);
+ if (!CV_IS_INSIDE(chksms, CV_RECORD_GAP(hdr_files, hdr_files->cbLen))) return FALSE;
+ *srcfile = source_new(msc_dbg->module, NULL,
+ (chksms->strOffset < cvmod->strsize) ? cvmod->strimage + chksms->strOffset : "<<str-out-of-bounds>>");
+ *srcline = inlsrcex->sourceLineNum;
+ return TRUE;
+ }
+ inlsrcex = CV_RECORD_GAP(inlsrcex, inlsrcex->countOfExtraFiles * sizeof(inlsrcex->extraFileId[0]));
+ }
+ break;
+ default:
+ FIXME("Unknown signature %x in INLINEELINES subsection\n", *(DWORD*)CV_RECORD_AFTER(hdr));
+ break;
+ }
+ }
+ return FALSE;
+}
+
static struct symt_inlinesite* codeview_create_inline_site(const struct msc_debug_info* msc_dbg,
const struct cv_module_snarf* cvmod,
struct symt_function* top_func,
@@ -1827,8 +1891,9 @@ static struct symt_inlinesite* codeview_create_inline_site(const struct msc_debu
DWORD64 addr;
struct symt_inlinesite* inlined;
struct cv_binannot cvba;
- BOOL found = FALSE;
- unsigned first, offset, length;
+ BOOL srcok, found = FALSE;
+ unsigned first, offset, length, line, srcfile;
+ const struct CV_Checksum_t* chksms;
if (!cvmod->ipi_ctp || !(cvt = codeview_jump_to_type(cvmod->ipi_ctp, inlinee)))
{
@@ -1891,7 +1956,12 @@ static struct symt_inlinesite* codeview_create_inline_site(const struct msc_debu
break;
}
if (!hdr_files) return FALSE;
+ srcok = cv_dbgsubsect_find_inlinee(msc_dbg, inlinee, cvmod, hdr_files, &srcfile, &line);
+ if (srcok)
+ symt_add_func_line(msc_dbg->module, &inlined->func, srcfile, line, top_func->address + offset);
+ else
+ srcfile = line = 0;
for (;;)
{
if (!codeview_advance_binannot(&cvba)) break;
@@ -1904,21 +1974,33 @@ static struct symt_inlinesite* codeview_create_inline_site(const struct msc_debu
case BA_OP_ChangeCodeOffset:
offset += cvba.arg1;
length = 1;
+ if (srcok)
+ symt_add_func_line(msc_dbg->module, &inlined->func, srcfile, line, top_func->address + offset);
break;
case BA_OP_ChangeCodeLength:
length = cvba.arg1;
break;
case BA_OP_ChangeFile:
+ chksms = CV_RECORD_GAP(hdr_files, cvba.arg1);
+ if (CV_IS_INSIDE(chksms, CV_RECORD_GAP(hdr_files, hdr_files->cbLen)))
+ srcfile = source_new(msc_dbg->module, NULL,
+ (chksms->strOffset < cvmod->strsize) ? cvmod->strimage + chksms->strOffset : "<<str-out-of-bounds>>");
break;
case BA_OP_ChangeLineOffset:
+ line += binannot_getsigned(cvba.arg1);
break;
case BA_OP_ChangeCodeOffsetAndLineOffset:
+ if (srcok)
+ symt_add_func_line(msc_dbg->module, &inlined->func, srcfile, line, top_func->address + offset);
+ line += binannot_getsigned(cvba.arg2);
offset += cvba.arg1;
length = 1;
break;
case BA_OP_ChangeCodeLengthAndCodeOffset:
offset += cvba.arg2;
length = cvba.arg1;
+ if (srcok)
+ symt_add_func_line(msc_dbg->module, &inlined->func, srcfile, line, top_func->address + offset);
break;
default:
WARN("Unsupported op %d\n", cvba.opcode);
Nov. 8, 2021
[PATCH 12/13] dbghelp/pdb: handle when a block in DEBUG_S_LINES subsection spreads across several symbols
by Eric Pouech
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/msc.c | 23 +++++++++++------------
1 file changed, 11 insertions(+), 12 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 4fba245d815..1be73b723f0 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -1480,7 +1480,6 @@ static void codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const B
static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const struct cv_module_snarf* cvmod)
{
unsigned i;
- DWORD_PTR addr;
const void* hdr_last = (const char*)cvmod->dbgsubsect + cvmod->dbgsubsect_size;
const struct CV_DebugSSubsectionHeader_t* hdr;
const struct CV_DebugSSubsectionHeader_t* hdr_next;
@@ -1512,6 +1511,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
hdr_next = CV_RECORD_GAP(hdr, hdr->cbLen);
if (!(hdr->type & DEBUG_S_IGNORE))
{
+ ULONG_PTR lineblk_base;
/* FIXME: should also check that whole lines_blk fits in linetab + size */
switch (hdr->type)
{
@@ -1520,7 +1520,6 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
files_hdr = CV_RECORD_AFTER(lines_hdr);
/* Skip blocks that are too small - Intel C Compiler generates these. */
if (!CV_IS_INSIDE(files_hdr, hdr_next)) break;
- addr = codeview_get_address(msc_dbg, lines_hdr->segCon, lines_hdr->offCon);
TRACE("block from %04x:%08x #%x\n",
lines_hdr->segCon, lines_hdr->offCon, lines_hdr->cbCon);
chksms = CV_RECORD_GAP(hdr_files, files_hdr->offFile);
@@ -1531,20 +1530,20 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
}
source = source_new(msc_dbg->module, NULL,
(chksms->strOffset < cvmod->strsize) ? cvmod->strimage + chksms->strOffset : "<<stroutofbounds>>");
- func = (struct symt_function*)symt_find_nearest(msc_dbg->module, addr);
- /* FIXME: at least labels support line numbers */
- if (!symt_check_tag(&func->symt, SymTagFunction) && !symt_check_tag(&func->symt, SymTagInlineSite))
- {
- WARN("--not a func at %04x:%08x %Ix tag=%d\n",
- lines_hdr->segCon, lines_hdr->offCon, addr, func ? func->symt.tag : -1);
- break;
- }
+ lineblk_base = codeview_get_address(msc_dbg, lines_hdr->segCon, lines_hdr->offCon);
lines = CV_RECORD_AFTER(files_hdr);
for (i = 0; i < files_hdr->nLines; i++)
{
+ func = (struct symt_function*)symt_find_nearest(msc_dbg->module, lineblk_base + lines[i].offset);
+ /* FIXME: at least labels support line numbers */
+ if (!symt_check_tag(&func->symt, SymTagFunction) && !symt_check_tag(&func->symt, SymTagInlineSite))
+ {
+ WARN("--not a func at %04x:%08x %Ix tag=%d\n",
+ lines_hdr->segCon, lines_hdr->offCon + lines[i].offset, lineblk_base + lines[i].offset, func ? func->symt.tag : -1);
+ continue;
+ }
symt_add_func_line(msc_dbg->module, func, source,
- lines[i].linenumStart,
- func->address + lines[i].offset);
+ lines[i].linenumStart, lineblk_base + lines[i].offset);
}
break;
case DEBUG_S_FILECHKSMS: /* skip */
Nov. 8, 2021
[PATCH 11/13] dbghelp/pdb: added support for inline sites
by Eric Pouech
- support S_INLINESITE and S_INLINESITE_END
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/msc.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 241 insertions(+), 22 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 12334a2e69d..4fba245d815 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -1762,11 +1762,180 @@ static unsigned codeview_transform_defrange(const struct msc_debug_info* msc_dbg
return len;
}
+static unsigned codeview_binannot_uncompress(const unsigned char** pptr)
+{
+ unsigned res = (unsigned)(-1);
+ const unsigned char* ptr = *pptr;
+
+ if ((*ptr & 0x80) == 0x00)
+ res = (unsigned)(*ptr++);
+ else if ((*ptr & 0xC0) == 0x80)
+ {
+ res = (unsigned)((*ptr++ & 0x3f) << 8);
+ res |= *ptr++;
+ }
+ else if ((*ptr & 0xE0) == 0xC0)
+ {
+ res = (*ptr++ & 0x1f) << 24;
+ res |= *ptr++ << 16;
+ res |= *ptr++ << 8;
+ res |= *ptr++;
+ }
+ else res = (unsigned)(-1);
+ *pptr = ptr;
+ return res;
+}
+
+struct cv_binannot
+{
+ const unsigned char* annot; /* current pointer */
+ const unsigned char* last_annot; /* end of binary annotation stream (first byte after) */
+ unsigned opcode; /* last decoded opcode */
+ unsigned arg1, arg2;
+};
+
+static BOOL codeview_advance_binannot(struct cv_binannot* cvba)
+{
+ if (cvba->annot >= cvba->last_annot) return FALSE;
+ cvba->opcode = codeview_binannot_uncompress(&cvba->annot);
+ if (cvba->opcode <= BA_OP_Invalid || cvba->opcode > BA_OP_ChangeColumnEnd) return FALSE;
+ if (cvba->annot >= cvba->last_annot) return FALSE;
+ cvba->arg1 = codeview_binannot_uncompress(&cvba->annot);
+ if (cvba->opcode == BA_OP_ChangeCodeOffsetAndLineOffset)
+ {
+ cvba->arg2 = cvba->arg1 >> 4;
+ cvba->arg1 &= 0x0F;
+ }
+ else if (cvba->opcode == BA_OP_ChangeCodeLengthAndCodeOffset)
+ {
+ if (cvba->annot >= cvba->last_annot) return FALSE;
+ cvba->arg2 = codeview_binannot_uncompress(&cvba->annot);
+ }
+ else cvba->arg2 = 0;
+ return TRUE;
+}
+
+static struct symt_inlinesite* codeview_create_inline_site(const struct msc_debug_info* msc_dbg,
+ const struct cv_module_snarf* cvmod,
+ struct symt_function* top_func,
+ struct symt* container,
+ cv_itemid_t inlinee,
+ const unsigned char* annot,
+ const unsigned char* last_annot)
+{
+ const struct CV_DebugSSubsectionHeader_t* hdr_files = NULL;
+ const union codeview_type* cvt;
+ DWORD64 addr;
+ struct symt_inlinesite* inlined;
+ struct cv_binannot cvba;
+ BOOL found = FALSE;
+ unsigned first, offset, length;
+
+ if (!cvmod->ipi_ctp || !(cvt = codeview_jump_to_type(cvmod->ipi_ctp, inlinee)))
+ {
+ FIXME("Couldn't find type %x in IPI stream\n", inlinee);
+ return NULL;
+ }
+
+ addr = top_func->address;
+ /* grasp first code offset in binary annotation to compute inline site start address */
+ cvba.annot = annot;
+ cvba.last_annot = last_annot;
+ while (codeview_advance_binannot(&cvba))
+ if (cvba.opcode == BA_OP_CodeOffset ||
+ cvba.opcode == BA_OP_ChangeCodeOffset ||
+ cvba.opcode == BA_OP_ChangeCodeOffsetAndLineOffset)
+ {
+ addr += first = cvba.arg1;
+ length = 0;
+ found = TRUE;
+ break;
+ }
+ else if (cvba.opcode == BA_OP_ChangeCodeLengthAndCodeOffset)
+ {
+ addr += first = cvba.arg2;
+ length = cvba.arg1;
+ found = TRUE;
+ break;
+ }
+ offset = first;
+
+ if (!found)
+ {
+ FIXME("Couldn't find start address of inlined\n");
+ return NULL;
+ }
+
+ switch (cvt->generic.id)
+ {
+ case LF_FUNC_ID:
+ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container,
+ cvt->func_id_v3.name, addr,
+ codeview_get_type(cvt->func_id_v3.type, FALSE));
+ break;
+ case LF_MFUNC_ID:
+ /* FIXME we just declare a function, not a method */
+ inlined = symt_new_inlinesite(msc_dbg->module, top_func, container,
+ cvt->mfunc_id_v3.name, addr,
+ codeview_get_type(cvt->mfunc_id_v3.type, FALSE));
+ break;
+ default:
+ FIXME("unsupported inlinee kind %x\n", cvt->generic.id);
+ return NULL;
+ }
+
+ for (hdr_files = cvmod->dbgsubsect;
+ CV_IS_INSIDE(hdr_files, cvmod->dbgsubsect + cvmod->dbgsubsect_size);
+ hdr_files = CV_RECORD_GAP(hdr_files, hdr_files->cbLen))
+ {
+ if (hdr_files->type == DEBUG_S_FILECHKSMS)
+ break;
+ }
+ if (!hdr_files) return FALSE;
+
+ for (;;)
+ {
+ if (!codeview_advance_binannot(&cvba)) break;
+ switch (cvba.opcode)
+ {
+ case BA_OP_CodeOffset:
+ first = offset = cvba.arg1;
+ length = 1;
+ break;
+ case BA_OP_ChangeCodeOffset:
+ offset += cvba.arg1;
+ length = 1;
+ break;
+ case BA_OP_ChangeCodeLength:
+ length = cvba.arg1;
+ break;
+ case BA_OP_ChangeFile:
+ break;
+ case BA_OP_ChangeLineOffset:
+ break;
+ case BA_OP_ChangeCodeOffsetAndLineOffset:
+ offset += cvba.arg1;
+ length = 1;
+ break;
+ case BA_OP_ChangeCodeLengthAndCodeOffset:
+ offset += cvba.arg2;
+ length = cvba.arg1;
+ break;
+ default:
+ WARN("Unsupported op %d\n", cvba.opcode);
+ break;
+ }
+ }
+ symt_add_inlinesite_range(msc_dbg->module, inlined, top_func->address + first, top_func->address + offset + length);
+ return inlined;
+}
+
static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
const BYTE* root, unsigned offset, unsigned size,
const struct cv_module_snarf* cvmod,
BOOL do_globals)
{
+ struct symt_function* top_func = NULL;
struct symt_function* curr_func = NULL;
int i, length;
struct symt_block* block = NULL;
@@ -1869,12 +2038,13 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
*/
case S_GPROC32_16t:
case S_LPROC32_16t:
- if (curr_func) FIXME("nested function\n");
- curr_func = symt_new_function(msc_dbg->module, compiland,
- terminate_string(&sym->proc_v1.p_name),
- codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset),
- sym->proc_v1.proc_len,
- codeview_get_type(sym->proc_v1.proctype, FALSE));
+ if (top_func) FIXME("nested function\n");
+ top_func = symt_new_function(msc_dbg->module, compiland,
+ terminate_string(&sym->proc_v1.p_name),
+ codeview_get_address(msc_dbg, sym->proc_v1.segment, sym->proc_v1.offset),
+ sym->proc_v1.proc_len,
+ codeview_get_type(sym->proc_v1.proctype, FALSE));
+ curr_func = top_func;
loc.kind = loc_absolute;
loc.offset = sym->proc_v1.debug_start;
symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL);
@@ -1883,12 +2053,13 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
break;
case S_GPROC32_ST:
case S_LPROC32_ST:
- if (curr_func) FIXME("nested function\n");
- curr_func = symt_new_function(msc_dbg->module, compiland,
- terminate_string(&sym->proc_v2.p_name),
- codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset),
- sym->proc_v2.proc_len,
- codeview_get_type(sym->proc_v2.proctype, FALSE));
+ if (top_func) FIXME("nested function\n");
+ top_func = symt_new_function(msc_dbg->module, compiland,
+ terminate_string(&sym->proc_v2.p_name),
+ codeview_get_address(msc_dbg, sym->proc_v2.segment, sym->proc_v2.offset),
+ sym->proc_v2.proc_len,
+ codeview_get_type(sym->proc_v2.proctype, FALSE));
+ curr_func = top_func;
loc.kind = loc_absolute;
loc.offset = sym->proc_v2.debug_start;
symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL);
@@ -1897,12 +2068,13 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
break;
case S_GPROC32:
case S_LPROC32:
- if (curr_func) FIXME("nested function\n");
- curr_func = symt_new_function(msc_dbg->module, compiland,
- sym->proc_v3.name,
- codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset),
- sym->proc_v3.proc_len,
- codeview_get_type(sym->proc_v3.proctype, FALSE));
+ if (top_func) FIXME("nested function\n");
+ top_func = symt_new_function(msc_dbg->module, compiland,
+ sym->proc_v3.name,
+ codeview_get_address(msc_dbg, sym->proc_v3.segment, sym->proc_v3.offset),
+ sym->proc_v3.proc_len,
+ codeview_get_type(sym->proc_v3.proctype, FALSE));
+ curr_func = top_func;
loc.kind = loc_absolute;
loc.offset = sym->proc_v3.debug_start;
symt_add_function_point(msc_dbg->module, curr_func, SymTagFuncDebugStart, &loc, NULL);
@@ -2007,9 +2179,10 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
{
block = symt_close_func_block(msc_dbg->module, curr_func, block, 0);
}
- else if (curr_func)
+ else if (top_func)
{
- curr_func = NULL;
+ if (curr_func != top_func) FIXME("shouldn't close a top function with an opened inlined function\n");
+ top_func = curr_func = NULL;
}
break;
@@ -2164,6 +2337,54 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
codeview_get_type(sym->local_v3.symtype, FALSE),
sym->local_v3.name);
break;
+ case S_INLINESITE:
+ {
+ struct symt_inlinesite* inlined = codeview_create_inline_site(msc_dbg, cvmod, top_func,
+ block ? &block->symt : &curr_func->symt,
+ sym->inline_site_v3.inlinee,
+ sym->inline_site_v3.binaryAnnotations,
+ (const unsigned char*)sym + length);
+ if (inlined)
+ {
+ curr_func = (struct symt_function*)inlined;
+ block = NULL;
+ }
+ else
+ {
+ /* skip all records until paired S_INLINESITE_END */
+ sym = (const union codeview_symbol*)(root + sym->inline_site_v3.pEnd);
+ if (sym->generic.id != S_INLINESITE_END) FIXME("complete wreckage\n");
+ length = sym->inline_site_v3.pEnd - i + sym->generic.len;
+ }
+ }
+ break;
+ case S_INLINESITE2:
+ {
+ struct symt_inlinesite* inlined = codeview_create_inline_site(msc_dbg, cvmod, top_func,
+ block ? &block->symt : &curr_func->symt,
+ sym->inline_site2_v3.inlinee,
+ sym->inline_site2_v3.binaryAnnotations,
+ (const unsigned char*)sym + length);
+ if (inlined)
+ {
+ curr_func = (struct symt_function*)inlined;
+ block = NULL;
+ }
+ else
+ {
+ /* skip all records until paired S_INLINESITE_END */
+ sym = (const union codeview_symbol*)(root + sym->inline_site2_v3.pEnd);
+ if (sym->generic.id != S_INLINESITE_END) FIXME("complete wreckage\n");
+ length = sym->inline_site_v3.pEnd - i + sym->generic.len;
+ }
+ }
+ break;
+
+ case S_INLINESITE_END:
+ block = symt_check_tag(curr_func->container, SymTagBlock) ?
+ (struct symt_block*)curr_func->container : NULL;
+ curr_func = (struct symt_function*)symt_get_upper_inlined((struct symt_inlinesite*)curr_func);
+ break;
/*
* These are special, in that they are always followed by an
@@ -2213,8 +2434,6 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
case S_DEFRANGE_REGISTER_REL:
case S_BUILDINFO:
- case S_INLINESITE:
- case S_INLINESITE_END:
case S_FILESTATIC:
case S_CALLEES:
case S_UNAMESPACE:
Nov. 8, 2021
[PATCH 10/13] dbghelp/pdb: let codeview_snarf() handle all module debug information
by Eric Pouech
(except old linetab v1)
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/msc.c | 62 ++++++++++++++++++++++++++++++++--------------------
1 file changed, 38 insertions(+), 24 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index d7b1e74edcd..12334a2e69d 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -90,6 +90,15 @@ struct pdb_module_info
#define loc_cv_local_range (loc_user + 0) /* loc.offset contain the copy of all defrange* Codeview records following S_LOCAL */
+struct cv_module_snarf
+{
+ const struct codeview_type_parse* ipi_ctp;
+ const struct CV_DebugSSubsectionHeader_t* dbgsubsect;
+ unsigned dbgsubsect_size;
+ const char* strimage;
+ unsigned strsize;
+};
+
/*========================================================================
* Debug file access helper routines
*/
@@ -1468,11 +1477,11 @@ static void codeview_snarf_linetab(const struct msc_debug_info* msc_dbg, const B
}
}
-static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const BYTE* linetab, DWORD size,
- const char* strimage, DWORD strsize)
+static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const struct cv_module_snarf* cvmod)
{
unsigned i;
DWORD_PTR addr;
+ const void* hdr_last = (const char*)cvmod->dbgsubsect + cvmod->dbgsubsect_size;
const struct CV_DebugSSubsectionHeader_t* hdr;
const struct CV_DebugSSubsectionHeader_t* hdr_next;
const struct CV_DebugSSubsectionHeader_t* hdr_files = NULL;
@@ -1484,15 +1493,13 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
struct symt_function* func;
/* locate DEBUG_S_FILECHKSMS (if any) */
- hdr = (const struct CV_DebugSSubsectionHeader_t*)linetab;
- while (CV_IS_INSIDE(hdr, linetab + size))
+ for (hdr = cvmod->dbgsubsect; CV_IS_INSIDE(hdr, hdr_last); hdr = CV_RECORD_GAP(hdr, hdr->cbLen))
{
if (hdr->type == DEBUG_S_FILECHKSMS)
{
hdr_files = hdr;
break;
}
- hdr = CV_RECORD_GAP(hdr, hdr->cbLen);
}
if (!hdr_files)
{
@@ -1500,8 +1507,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
return;
}
- hdr = (const struct CV_DebugSSubsectionHeader_t*)linetab;
- while (CV_IS_INSIDE(hdr, linetab + size))
+ for (hdr = cvmod->dbgsubsect; CV_IS_INSIDE(hdr, hdr_last); hdr = hdr_next)
{
hdr_next = CV_RECORD_GAP(hdr, hdr->cbLen);
if (!(hdr->type & DEBUG_S_IGNORE))
@@ -1524,7 +1530,7 @@ static void codeview_snarf_linetab2(const struct msc_debug_info* msc_dbg, const
break;
}
source = source_new(msc_dbg->module, NULL,
- (chksms->strOffset < strsize) ? strimage + chksms->strOffset : "<<stroutofbounds>>");
+ (chksms->strOffset < cvmod->strsize) ? cvmod->strimage + chksms->strOffset : "<<stroutofbounds>>");
func = (struct symt_function*)symt_find_nearest(msc_dbg->module, addr);
/* FIXME: at least labels support line numbers */
if (!symt_check_tag(&func->symt, SymTagFunction) && !symt_check_tag(&func->symt, SymTagInlineSite))
@@ -1756,8 +1762,10 @@ static unsigned codeview_transform_defrange(const struct msc_debug_info* msc_dbg
return len;
}
-static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* root,
- int offset, int size, BOOL do_globals)
+static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg,
+ const BYTE* root, unsigned offset, unsigned size,
+ const struct cv_module_snarf* cvmod,
+ BOOL do_globals)
{
struct symt_function* curr_func = NULL;
int i, length;
@@ -2220,7 +2228,7 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
break;
}
}
-
+ if (cvmod) codeview_snarf_linetab2(msc_dbg, cvmod);
return TRUE;
}
@@ -3011,6 +3019,8 @@ static BOOL pdb_process_internal(const struct process* pcs,
PDB_SYMBOLS symbols;
BYTE* globalimage;
BYTE* modimage;
+ BYTE* ipi_image;
+ struct codeview_type_parse ipi_ctp;
BYTE* file;
int header_size = 0;
PDB_STREAM_INDEXES* psi;
@@ -3052,12 +3062,15 @@ static BOOL pdb_process_internal(const struct process* pcs,
pdb_lookup, pdb_module_info, module_index);
pdb_process_types(msc_dbg, pdb_file);
+ ipi_image = pdb_read_file(pdb_file, 4);
+ pdb_init_type_parse(msc_dbg, &ipi_ctp, ipi_image);
+
/* Read global symbol table */
globalimage = pdb_read_file(pdb_file, symbols.gsym_file);
if (globalimage)
{
- codeview_snarf(msc_dbg, globalimage, 0,
- pdb_get_file_size(pdb_file, symbols.gsym_file), FALSE);
+ codeview_snarf(msc_dbg, globalimage, 0, pdb_get_file_size(pdb_file, symbols.gsym_file),
+ NULL, FALSE);
}
/* Read per-module symbols' tables */
@@ -3074,19 +3087,18 @@ static BOOL pdb_process_internal(const struct process* pcs,
modimage = pdb_read_file(pdb_file, sfile.file);
if (modimage)
{
- if (sfile.symbol_size)
- codeview_snarf(msc_dbg, modimage, sizeof(DWORD),
- sfile.symbol_size, TRUE);
-
- if (sfile.lineno_size && sfile.lineno2_size) FIXME("Both line info present... only supporting first\n");
- if (sfile.lineno_size)
+ struct cv_module_snarf cvmod = {&ipi_ctp, (const void*)(modimage + sfile.symbol_size), sfile.lineno2_size,
+ files_image + 12, files_size};
+ codeview_snarf(msc_dbg, modimage, sizeof(DWORD), sfile.symbol_size,
+ &cvmod, TRUE);
+
+ if (sfile.lineno_size && sfile.lineno2_size)
+ FIXME("Both line info present... only supporting second\n");
+ else if (sfile.lineno_size)
codeview_snarf_linetab(msc_dbg,
modimage + sfile.symbol_size,
sfile.lineno_size,
pdb_file->kind == PDB_JG);
- else if (sfile.lineno2_size && files_image)
- codeview_snarf_linetab2(msc_dbg, modimage + sfile.symbol_size, sfile.lineno2_size,
- files_image + 12, files_size);
pdb_free(modimage);
}
@@ -3101,6 +3113,8 @@ static BOOL pdb_process_internal(const struct process* pcs,
pdb_get_file_size(pdb_file, symbols.gsym_file));
pdb_free(globalimage);
}
+ HeapFree(GetProcessHeap(), 0, (DWORD*)ipi_ctp.offset);
+ pdb_free(ipi_image);
}
else
pdb_process_symbol_imports(pcs, msc_dbg, NULL, NULL, image,
@@ -3565,8 +3579,8 @@ static BOOL codeview_process_info(const struct process* pcs,
if (ent->SubSection == sstAlignSym)
{
- codeview_snarf(msc_dbg, msc_dbg->root + ent->lfo, sizeof(DWORD),
- ent->cb, TRUE);
+ codeview_snarf(msc_dbg, msc_dbg->root + ent->lfo, sizeof(DWORD), ent->cb,
+ NULL, TRUE);
/*
* Check the next and previous entry. If either is a
Nov. 8, 2021
[PATCH 09/13] dbghelp/pdb: introduce codeview_init_type_parse() to handle init of type streams
by Eric Pouech
currently used for TPI stream
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/msc.c | 87 +++++++++++++++++++++++++++++-----------------------
1 file changed, 48 insertions(+), 39 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 0247a90f6ff..d7b1e74edcd 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -2717,53 +2717,62 @@ static HANDLE map_pdb_file(const struct process* pcs,
return hMap;
}
+static BOOL pdb_init_type_parse(const struct msc_debug_info* msc_dbg,
+ struct codeview_type_parse* ctp,
+ BYTE* image)
+{
+ PDB_TYPES types;
+ DWORD total;
+ const BYTE* ptr;
+ DWORD* offset;
+
+ pdb_convert_types_header(&types, image);
+
+ /* Check for unknown versions */
+ switch (types.version)
+ {
+ case 19950410: /* VC 4.0 */
+ case 19951122:
+ case 19961031: /* VC 5.0 / 6.0 */
+ case 19990903: /* VC 7.0 */
+ case 20040203: /* VC 8.0 */
+ break;
+ default:
+ ERR("-Unknown type info version %d\n", types.version);
+ }
+
+ ctp->module = msc_dbg->module;
+ /* reconstruct the types offset...
+ * FIXME: maybe it's present in the newest PDB_TYPES structures
+ */
+ total = types.last_index - types.first_index + 1;
+ offset = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD) * total);
+ if (!offset) return FALSE;
+ ctp->table = ptr = image + types.type_offset;
+ ctp->num = 0;
+ while (ptr < ctp->table + types.type_size && ctp->num < total)
+ {
+ offset[ctp->num++] = ptr - ctp->table;
+ ptr += ((const union codeview_type*)ptr)->generic.len + 2;
+ }
+ ctp->offset = offset;
+ return TRUE;
+}
+
static void pdb_process_types(const struct msc_debug_info* msc_dbg,
const struct pdb_file_info* pdb_file)
{
- BYTE* types_image = NULL;
+ struct codeview_type_parse ctp;
+ BYTE* types_image = pdb_read_file(pdb_file, 2);
- types_image = pdb_read_file(pdb_file, 2);
if (types_image)
{
- PDB_TYPES types;
- struct codeview_type_parse ctp;
- DWORD total;
- const BYTE* ptr;
- DWORD* offset;
-
- pdb_convert_types_header(&types, types_image);
-
- /* Check for unknown versions */
- switch (types.version)
+ if (pdb_init_type_parse(msc_dbg, &ctp, types_image))
{
- case 19950410: /* VC 4.0 */
- case 19951122:
- case 19961031: /* VC 5.0 / 6.0 */
- case 19990903: /* VC 7.0 */
- case 20040203: /* VC 8.0 */
- break;
- default:
- ERR("-Unknown type info version %d\n", types.version);
+ /* Read type table */
+ codeview_parse_type_table(&ctp);
+ HeapFree(GetProcessHeap(), 0, (DWORD*)ctp.offset);
}
-
- ctp.module = msc_dbg->module;
- /* reconstruct the types offset...
- * FIXME: maybe it's present in the newest PDB_TYPES structures
- */
- total = types.last_index - types.first_index + 1;
- offset = HeapAlloc(GetProcessHeap(), 0, sizeof(DWORD) * total);
- ctp.table = ptr = types_image + types.type_offset;
- ctp.num = 0;
- while (ptr < ctp.table + types.type_size && ctp.num < total)
- {
- offset[ctp.num++] = ptr - ctp.table;
- ptr += ((const union codeview_type*)ptr)->generic.len + 2;
- }
- ctp.offset = offset;
-
- /* Read type table */
- codeview_parse_type_table(&ctp);
- HeapFree(GetProcessHeap(), 0, offset);
pdb_free(types_image);
}
}
Nov. 8, 2021
[PATCH 08/13] dbghelp/pdb: implement loc_compute for pdb backend
by Eric Pouech
Add support for S_DEFRANGE* entries
Signed-off-by: Eric Pouech <eric.pouech(a)gmail.com>
---
dlls/dbghelp/msc.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 60 insertions(+), 3 deletions(-)
diff --git a/dlls/dbghelp/msc.c b/dlls/dbghelp/msc.c
index 0c58e5fc14f..0247a90f6ff 100644
--- a/dlls/dbghelp/msc.c
+++ b/dlls/dbghelp/msc.c
@@ -1699,7 +1699,11 @@ static unsigned codeview_transform_defrange(const struct msc_debug_info* msc_dbg
switch (symrange->generic.id)
{
case S_DEFRANGE:
+ codeview_xform_range(msc_dbg, locinfo, &symrange->defrange_v3.range);
+ /* FIXME: transformation unsupported; let loc_compute bark if actually needed */
+ break;
case S_DEFRANGE_SUBFIELD:
+ codeview_xform_range(msc_dbg, locinfo, &symrange->defrange_subfield_v3.range);
/* FIXME: transformation unsupported; let loc_compute bark if actually needed */
break;
case S_DEFRANGE_REGISTER:
@@ -1734,7 +1738,7 @@ static unsigned codeview_transform_defrange(const struct msc_debug_info* msc_dbg
unsigned gaplen = (const char*)get_next_sym(symrange) - (const char*)gap;
locinfo->ngaps = gaplen / sizeof(*gap);
memcpy(locinfo->gaps, gap, gaplen);
- locinfo = (struct cv_local_info*)((unsigned char*)(locinfo + 1) + gaplen);
+ locinfo = (struct cv_local_info*)((char*)(locinfo + 1) + gaplen);
}
else
{
@@ -2220,13 +2224,66 @@ static BOOL codeview_snarf(const struct msc_debug_info* msc_dbg, const BYTE* roo
return TRUE;
}
+static BOOL codeview_is_inside(const struct cv_local_info* locinfo, const struct symt_function* func, DWORD_PTR ip)
+{
+ unsigned i;
+ /* ip must be in local_info range, but not in any of its gaps */
+ if (ip < locinfo->start || ip >= locinfo->start + locinfo->rangelen) return FALSE;
+ for (i = 0; i < locinfo->ngaps; ++i)
+ if (func->address + locinfo->gaps[i].gapStartOffset <= ip &&
+ ip < func->address + locinfo->gaps[i].gapStartOffset + locinfo->gaps[i].cbRange)
+ return FALSE;
+ return TRUE;
+}
+
static void pdb_location_compute(struct process* pcs,
const struct module_format* modfmt,
const struct symt_function* func,
struct location* loc)
-
{
- loc->kind = loc_register;
+ const struct cv_local_info* locinfo;
+
+ switch (loc->kind)
+ {
+ case loc_cv_local_range:
+ for (locinfo = (const struct cv_local_info*)loc->offset;
+ locinfo->kind != 0;
+ locinfo = (const struct cv_local_info*)((const char*)(locinfo + 1) + locinfo->ngaps * sizeof(locinfo->gaps[0])))
+ {
+ if (!codeview_is_inside(locinfo, func, pcs->localscope_pc)) continue;
+ switch (locinfo->kind)
+ {
+ case S_DEFRANGE:
+ case S_DEFRANGE_SUBFIELD:
+ default:
+ FIXME("Unsupported defrange %d\n", locinfo->kind);
+ loc->kind = loc_error;
+ loc->reg = loc_err_internal;
+ return;
+ case S_DEFRANGE_SUBFIELD_REGISTER:
+ FIXME("sub-field part not handled\n");
+ /* fall through */
+ case S_DEFRANGE_REGISTER:
+ loc->kind = loc_register;
+ loc->reg = locinfo->reg;
+ return;
+ case S_DEFRANGE_REGISTER_REL:
+ loc->kind = loc_regrel;
+ loc->reg = locinfo->reg;
+ loc->offset = locinfo->offset;
+ return;
+ case S_DEFRANGE_FRAMEPOINTER_REL:
+ case S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE:
+ loc->kind = loc_regrel;
+ loc->reg = dbghelp_current_cpu->frame_regno;
+ loc->offset = locinfo->offset;
+ return;
+ }
+ }
+ break;
+ default: break;
+ }
+ loc->kind = loc_error;
loc->reg = loc_err_internal;
}
Nov. 8, 2021