The Legend of Heroes: Trails of Cold Steel II crashes on its intro videos because it mistakenly destroys and frees its own filter when the IMediaSeeking object is destroyed, which is retrieved as a separate object when we query for it, with a refcount of 1 regardless of the filter's refcount. It does so with msvcrt operator delete, even if the filter had outstanding ref counts. It happens to work on Windows because Windows caches the object exposing the interface.
Signed-off-by: Gabriel Ivăncescu gabrielopcode@gmail.com --- dlls/quartz/filtergraph.c | 42 ++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 18 deletions(-)
diff --git a/dlls/quartz/filtergraph.c b/dlls/quartz/filtergraph.c index c659cf9..0de16c4 100644 --- a/dlls/quartz/filtergraph.c +++ b/dlls/quartz/filtergraph.c @@ -153,6 +153,7 @@ struct filter { struct list entry, sorted_entry; IBaseFilter *filter; + IMediaSeeking *seeking; WCHAR *name; BOOL sorting; }; @@ -569,6 +570,15 @@ static IBaseFilter *find_filter_by_name(IFilterGraphImpl *graph, const WCHAR *na return NULL; }
+static IMediaSeeking *get_filter_seeking(struct filter *filter) +{ + /* Cache the IMediaSeeking object as some broken apps actually depend on this. */ + if (!filter->seeking) + if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void**)&filter->seeking))) + filter->seeking = NULL; + return filter->seeking; +} + static BOOL has_output_pins(IBaseFilter *filter) { IEnumPins *enumpins; @@ -593,24 +603,19 @@ static BOOL has_output_pins(IBaseFilter *filter) return FALSE; }
-static BOOL is_renderer(IBaseFilter *filter) +static BOOL is_renderer(struct filter *filter) { IAMFilterMiscFlags *flags; - IMediaSeeking *seeking; BOOL ret = FALSE;
- if (SUCCEEDED(IBaseFilter_QueryInterface(filter, &IID_IAMFilterMiscFlags, (void **)&flags))) + if (SUCCEEDED(IBaseFilter_QueryInterface(filter->filter, &IID_IAMFilterMiscFlags, (void **)&flags))) { if (IAMFilterMiscFlags_GetMiscFlags(flags) & AM_FILTER_MISC_FLAGS_IS_RENDERER) ret = TRUE; IAMFilterMiscFlags_Release(flags); } - else if (SUCCEEDED(IBaseFilter_QueryInterface(filter, &IID_IMediaSeeking, (void **)&seeking))) - { - IMediaSeeking_Release(seeking); - if (!has_output_pins(filter)) - ret = TRUE; - } + else if (get_filter_seeking(filter) && !has_output_pins(filter->filter)) + ret = TRUE; return ret; }
@@ -675,12 +680,13 @@ static HRESULT WINAPI FilterGraph2_AddFilter(IFilterGraph2 *iface, }
IBaseFilter_AddRef(entry->filter = filter); + entry->seeking = NULL; list_add_head(&graph->filters, &entry->entry); list_add_head(&graph->sorted_filters, &entry->sorted_entry); entry->sorting = FALSE; ++graph->version;
- if (is_renderer(filter)) + if (is_renderer(entry)) ++graph->nRenderers;
return duplicate_name ? VFW_S_DUPLICATE_NAME : hr; @@ -755,11 +761,13 @@ static HRESULT WINAPI FilterGraph2_RemoveFilter(IFilterGraph2 *iface, IBaseFilte hr = IBaseFilter_JoinFilterGraph(pFilter, NULL, NULL); if (SUCCEEDED(hr)) { - if (is_renderer(pFilter)) + if (is_renderer(entry)) --This->nRenderers;
IBaseFilter_SetSyncSource(pFilter, NULL); IBaseFilter_Release(pFilter); + if (entry->seeking) + IMediaSeeking_Release(entry->seeking); list_remove(&entry->entry); list_remove(&entry->sorted_entry); CoTaskMemFree(entry->name); @@ -2219,13 +2227,11 @@ static HRESULT all_renderers_seek(IFilterGraphImpl *This, fnFoundSeek FoundSeek,
LIST_FOR_EACH_ENTRY(filter, &This->filters, struct filter, entry) { - IMediaSeeking *seek = NULL; + IMediaSeeking *seek = get_filter_seeking(filter);
- IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seek); if (!seek) continue; hr = FoundSeek(This, seek, arg); - IMediaSeeking_Release(seek); if (hr_return != E_NOTIMPL) allnotimpl = FALSE; if (hr_return == S_OK || (FAILED(hr) && hr != E_NOTIMPL && SUCCEEDED(hr_return))) @@ -2429,11 +2435,11 @@ static HRESULT WINAPI MediaSeeking_GetStopPosition(IMediaSeeking *iface, LONGLON
LIST_FOR_EACH_ENTRY(filter, &graph->filters, struct filter, entry) { - if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seeking))) + seeking = get_filter_seeking(filter); + if (!seeking) continue;
filter_hr = IMediaSeeking_GetStopPosition(seeking, &filter_stop); - IMediaSeeking_Release(seeking); if (SUCCEEDED(filter_hr)) { hr = S_OK; @@ -2539,12 +2545,12 @@ static HRESULT WINAPI MediaSeeking_SetPositions(IMediaSeeking *iface, LONGLONG * { LONGLONG current = current_ptr ? *current_ptr : 0, stop = stop_ptr ? *stop_ptr : 0;
- if (FAILED(IBaseFilter_QueryInterface(filter->filter, &IID_IMediaSeeking, (void **)&seeking))) + seeking = get_filter_seeking(filter); + if (!seeking) continue;
filter_hr = IMediaSeeking_SetPositions(seeking, ¤t, current_flags | AM_SEEKING_ReturnTime, &stop, stop_flags); - IMediaSeeking_Release(seeking); if (SUCCEEDED(filter_hr)) { hr = S_OK;