From: Paul Gofman pgofman@codeweavers.com
--- dlls/avifil32/tests/api.c | 76 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+)
diff --git a/dlls/avifil32/tests/api.c b/dlls/avifil32/tests/api.c index f7571f48b6a..b7b33efd3d0 100644 --- a/dlls/avifil32/tests/api.c +++ b/dlls/avifil32/tests/api.c @@ -771,10 +771,85 @@ static void test_COM_editstream(void) while (IAVIEditStream_Release(edit)); }
+static void test_avifile_write(void) +{ + WCHAR fn[MAX_PATH]; + IPersistFile *persist; + AVISTREAMINFOW si; + PAVIFILE avifile; + PAVISTREAM stm; + HRESULT hr; + BOOL ret; + + GetTempPathW(MAX_PATH, fn); + wcscat(fn, L"test.avi"); + + hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC, &IID_IAVIFile, (void **)&avifile); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IAVIFile_QueryInterface(avifile, &IID_IPersistFile, (void **)&persist); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IPersistFile_Load(persist, fn, 0); + ok(hr == AVIERR_FILEOPEN, "got %#lx.\n", hr); + /* Consequent load attempt even with better flags will fail with ~0u. */ + hr = IPersistFile_Load(persist, fn, STGM_CREATE | STGM_WRITE); + todo_wine ok(hr == ~0u, "got %#lx.\n", hr); + IPersistFile_Release(persist); + IAVIFile_Release(avifile); + + hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC, &IID_IAVIFile, (void **)&avifile); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IAVIFile_QueryInterface(avifile, &IID_IPersistFile, (void **)&persist); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IPersistFile_Load(persist, fn, STGM_CREATE | STGM_WRITE); + ok(hr == S_OK, "got %#lx.\n", hr); + /* Consequent load attempt will fail with ~0u. */ + hr = IPersistFile_Load(persist, fn, STGM_CREATE | STGM_WRITE); + todo_wine ok(hr == ~0u, "got %#lx.\n", hr); + IPersistFile_Release(persist); + IAVIFile_Release(avifile); + + ret = DeleteFileW(fn); + ok(ret, "got error %lu.\n", GetLastError()); + + hr = CoCreateInstance(&CLSID_AVIFile, NULL, CLSCTX_INPROC, &IID_IAVIFile, (void **)&avifile); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IAVIFile_QueryInterface(avifile, &IID_IPersistFile, (void **)&persist); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IPersistFile_Load(persist, fn, STGM_CREATE); + ok(hr == S_OK, "got %#lx.\n", hr); + + memset(&si, 0, sizeof(si)); + si.fccType = streamtypeAUDIO; + si.dwScale = 1; + si.dwRate = 48000; + si.dwLength = 4; + si.dwQuality = ~0u; + si.dwSampleSize = 4; + hr = IAVIFile_CreateStream(avifile, &stm, &si); + ok(hr == AVIERR_READONLY, "got %#lx.\n", hr); + + IPersistFile_Release(persist); + IAVIFile_Release(avifile); + + ret = DeleteFileW(fn); + ok(ret, "got error %lu.\n", GetLastError()); + + hr = AVIFileOpenW(&avifile, fn, OF_CREATE, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = AVIFileCreateStreamW(avifile, &stm, &si); + todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + if (hr == S_OK) + IAVIStream_Release(stm); + IAVIFile_Release(avifile); + ret = DeleteFileW(fn); + ok(ret, "got error %lu.\n", GetLastError()); +} + START_TEST(api) {
AVIFileInit(); + test_EditStreamSetInfo(); test_AVISaveOptions(); test_default_data(); @@ -784,6 +859,7 @@ START_TEST(api) test_COM(); test_COM_wavfile(); test_COM_editstream(); + test_avifile_write(); AVIFileExit();
}
From: Paul Gofman pgofman@codeweavers.com
--- dlls/avifil32/api.c | 3 +++ dlls/avifil32/tests/api.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/dlls/avifil32/api.c b/dlls/avifil32/api.c index fe0c163cf0a..f2ed1736882 100644 --- a/dlls/avifil32/api.c +++ b/dlls/avifil32/api.c @@ -260,6 +260,9 @@ HRESULT WINAPI AVIFileOpenW(PAVIFILE *ppfile, LPCWSTR szFile, UINT uMode, return hr; }
+ if (uMode & OF_CREATE) + uMode |= OF_WRITE; + hr = IPersistFile_Load(ppersist, szFile, uMode); IPersistFile_Release(ppersist); if (FAILED(hr)) { diff --git a/dlls/avifil32/tests/api.c b/dlls/avifil32/tests/api.c index b7b33efd3d0..4d698b4a28b 100644 --- a/dlls/avifil32/tests/api.c +++ b/dlls/avifil32/tests/api.c @@ -837,7 +837,7 @@ static void test_avifile_write(void) hr = AVIFileOpenW(&avifile, fn, OF_CREATE, NULL); ok(hr == S_OK, "got %#lx.\n", hr); hr = AVIFileCreateStreamW(avifile, &stm, &si); - todo_wine ok(hr == S_OK, "got %#lx.\n", hr); + ok(hr == S_OK, "got %#lx.\n", hr); if (hr == S_OK) IAVIStream_Release(stm); IAVIFile_Release(avifile);
From: Paul Gofman pgofman@codeweavers.com
--- dlls/avifil32/avifile.c | 17 ++++++++++++--- dlls/avifil32/tests/api.c | 44 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 3 deletions(-)
diff --git a/dlls/avifil32/avifile.c b/dlls/avifil32/avifile.c index 719d96edf86..6aae4736c4d 100644 --- a/dlls/avifil32/avifile.c +++ b/dlls/avifil32/avifile.c @@ -1399,9 +1399,19 @@ static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DW This->idxFrames[This->lLastFrame].dwChunkLength = size;
/* update AVISTREAMINFO structure if necessary */ - if (This->sInfo.dwLength <= This->lLastFrame) - This->sInfo.dwLength = This->lLastFrame + 1; + if (This->sInfo.dwSampleSize) + { + unsigned int block;
+ size = 0; + for (block = 0; block <= This->lLastFrame; block++) + size += This->idxFrames[block].dwChunkLength; + This->sInfo.dwLength = max(This->sInfo.dwLength, size / This->sInfo.dwSampleSize); + } + else + { + This->sInfo.dwLength = max(This->sInfo.dwLength, This->lLastFrame + 1); + } return AVIERR_OK; }
@@ -1989,7 +1999,8 @@ static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos, assert(This != NULL); assert(This->paf != NULL); assert(This->paf->hmmio != NULL); - assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); + if (!This->sInfo.dwSampleSize) + assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength); assert(pos <= This->lLastFrame);
/* should we read as much as block gives us? */ diff --git a/dlls/avifil32/tests/api.c b/dlls/avifil32/tests/api.c index 4d698b4a28b..f737175c0f0 100644 --- a/dlls/avifil32/tests/api.c +++ b/dlls/avifil32/tests/api.c @@ -775,7 +775,9 @@ static void test_avifile_write(void) { WCHAR fn[MAX_PATH]; IPersistFile *persist; + PCMWAVEFORMAT afmt; AVISTREAMINFOW si; + USHORT buffer[64]; PAVIFILE avifile; PAVISTREAM stm; HRESULT hr; @@ -838,6 +840,48 @@ static void test_avifile_write(void) ok(hr == S_OK, "got %#lx.\n", hr); hr = AVIFileCreateStreamW(avifile, &stm, &si); ok(hr == S_OK, "got %#lx.\n", hr); + + memset(&afmt, 0, sizeof(afmt)); + afmt.wBitsPerSample = 16; + afmt.wf.wFormatTag = WAVE_FORMAT_PCM; + afmt.wf.nChannels = 2; + afmt.wf.nSamplesPerSec = 44800; + afmt.wf.nAvgBytesPerSec = afmt.wf.nSamplesPerSec * afmt.wf.nChannels; + //afmt.wf.wBitsPerSample = afmt.wf.nChannels * 2 * 8; + afmt.wf.nBlockAlign = afmt.wf.nChannels * 2; + hr = AVIStreamSetFormat(stm, 0, &afmt, sizeof(afmt)); + ok(hr == S_OK, "got %#lx.\n", hr); + + memset(buffer, 0xcc, sizeof(buffer)); + + hr = IAVIStream_Info(stm, &si, sizeof(si)); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(!si.dwLength, "got %lu.\n", si.dwLength); + ok(!si.dwStart, "got %lu.\n", si.dwStart); + ok(!si.dwSuggestedBufferSize, "got %lu.\n", si.dwSuggestedBufferSize); + hr = AVIStreamWrite(stm, 0, 2, buffer, si.dwSampleSize * 2, 0, NULL, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IAVIStream_Info(stm, &si, sizeof(si)); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(si.dwLength == 2, "got %lu.\n", si.dwLength); + ok(!si.dwStart, "got %lu.\n", si.dwStart); + ok(si.dwSuggestedBufferSize == 8, "got %lu.\n", si.dwSuggestedBufferSize); + hr = AVIStreamWrite(stm, 2, 2, buffer, si.dwSampleSize * 2, 0, NULL, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IAVIStream_Info(stm, &si, sizeof(si)); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(si.dwLength == 4, "got %lu.\n", si.dwLength); + ok(!si.dwStart, "got %lu.\n", si.dwStart); + ok(si.dwSuggestedBufferSize == 8, "got %lu.\n", si.dwSuggestedBufferSize); + + hr = AVIStreamWrite(stm, 4, 4, buffer, si.dwSampleSize * 4, 0, NULL, NULL); + ok(hr == S_OK, "got %#lx.\n", hr); + hr = IAVIStream_Info(stm, &si, sizeof(si)); + ok(hr == S_OK, "got %#lx.\n", hr); + ok(si.dwLength == 8, "got %lu.\n", si.dwLength); + ok(!si.dwStart, "got %lu.\n", si.dwStart); + ok(si.dwSuggestedBufferSize == 16, "got %lu.\n", si.dwSuggestedBufferSize); + if (hr == S_OK) IAVIStream_Release(stm); IAVIFile_Release(avifile);
This fixes recording of in-game video in The Sims 2 Legacy Collection:
1. An AVI file created with AVIFileOpenW(... OF_CREATE) is currently not modifiable (in the absence of file access flags). The tests suggest that it is the case on Windows if using AVIFile class directly but not the case if using AVIFileOpenW(), so looks like missing OF_WRITE should be added in AVIFileOpenW and not inside the class implementation.
2. Then that files in AVIStreamWrite() (as reproduced in test in patch 3) when audio samples are not written one by one (must probably always be the case in practice). That fails the check in IAVIStream_fnWrite() (```if (This->sInfo.dwLength != start)``` for the ```This->sInfo.dwSampleSize != 0``` case). It looks like that check might be correct, but for fixed size samples the counting of dwLength is incorrect in the couple of places (which patch 3 is fixing). The rest of the code seems to correctly handle that and properly assume that dwLength is number of samples and a frame can contain multiple samples for fixed sample size (!=0).
Alfred Agrell (@Alcaro) commented about dlls/avifil32/tests/api.c:
hr = AVIFileOpenW(&avifile, fn, OF_CREATE, NULL); ok(hr == S_OK, "got %#lx.\n", hr); hr = AVIFileCreateStreamW(avifile, &stm, &si);
- todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
- ok(hr == S_OK, "got %#lx.\n", hr); if (hr == S_OK)
Now that the ok is no longer todo, might as well remove this check.
Especially when the next commit adds eight different writes to that variable, so it's not even checking the right hr anymore.