1 /*
2 * Copyright 1999 Marcus Meissner
3 * Copyright 2002-2003 Michael Günnewig
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 */
19
20 /* TODO:
21 * - IAVIStreaming interface is missing for the IAVIStreamImpl
22 * - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
23 * - IAVIStream_fnReadFormat: formatchanges aren't read in.
24 * - IAVIStream_fnDelete: a stub.
25 * - IAVIStream_fnSetInfo: a stub.
26 * - make thread safe
27 *
28 * KNOWN Bugs:
29 * - native version can hangup when reading a file generated with this DLL.
30 * When index is missing it works, but index seems to be okay.
31 */
32
33 #include <assert.h>
34 #include <stdarg.h>
35
36 #include "windef.h"
37 #include "winbase.h"
38 #include "wingdi.h"
39 #include "winuser.h"
40 #include "winnls.h"
41 #include "winerror.h"
42 #include "mmsystem.h"
43 #include "vfw.h"
44
45 #include "avifile_private.h"
46 #include "extrachunk.h"
47
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
50
51 WINE_DEFAULT_DEBUG_CHANNEL(avifile);
52
53 #ifndef IDX_PER_BLOCK
54 #define IDX_PER_BLOCK 2730
55 #endif
56
57 /***********************************************************************/
58
59 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile* iface,REFIID refiid,LPVOID *obj);
60 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile* iface);
61 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile* iface);
62 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile*iface,AVIFILEINFOW*afi,LONG size);
63 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile*iface,PAVISTREAM*avis,DWORD fccType,LONG lParam);
64 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile*iface,PAVISTREAM*avis,AVISTREAMINFOW*asi);
65 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG size);
66 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile*iface,DWORD ckid,LPVOID lpData,LONG *size);
67 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile*iface);
68 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile*iface,DWORD fccType,LONG lParam);
69
70 static const struct IAVIFileVtbl iavift = {
71 IAVIFile_fnQueryInterface,
72 IAVIFile_fnAddRef,
73 IAVIFile_fnRelease,
74 IAVIFile_fnInfo,
75 IAVIFile_fnGetStream,
76 IAVIFile_fnCreateStream,
77 IAVIFile_fnWriteData,
78 IAVIFile_fnReadData,
79 IAVIFile_fnEndRecord,
80 IAVIFile_fnDeleteStream
81 };
82
83 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile*iface,REFIID refiid,LPVOID*obj);
84 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile*iface);
85 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile*iface);
86 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile*iface,CLSID*pClassID);
87 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile*iface);
88 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile*iface,LPCOLESTR pszFileName,DWORD dwMode);
89 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile*iface,LPCOLESTR pszFileName,BOOL fRemember);
90 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile*iface,LPCOLESTR pszFileName);
91 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile*iface,LPOLESTR*ppszFileName);
92
93 static const struct IPersistFileVtbl ipersistft = {
94 IPersistFile_fnQueryInterface,
95 IPersistFile_fnAddRef,
96 IPersistFile_fnRelease,
97 IPersistFile_fnGetClassID,
98 IPersistFile_fnIsDirty,
99 IPersistFile_fnLoad,
100 IPersistFile_fnSave,
101 IPersistFile_fnSaveCompleted,
102 IPersistFile_fnGetCurFile
103 };
104
105 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream*iface,REFIID refiid,LPVOID *obj);
106 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream*iface);
107 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface);
108 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream*iface,LPARAM lParam1,LPARAM lParam2);
109 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream*iface,AVISTREAMINFOW *psi,LONG size);
110 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream*iface,LONG pos,LONG flags);
111 static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG *formatsize);
112 static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream*iface,LONG pos,LPVOID format,LONG formatsize);
113 static HRESULT WINAPI IAVIStream_fnRead(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,LONG *bytesread,LONG *samplesread);
114 static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream*iface,LONG start,LONG samples,LPVOID buffer,LONG buffersize,DWORD flags,LONG *sampwritten,LONG *byteswritten);
115 static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream*iface,LONG start,LONG samples);
116 static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG *lpread);
117 static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream*iface,DWORD fcc,LPVOID lp,LONG size);
118 static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream*iface,AVISTREAMINFOW*info,LONG infolen);
119
120 static const struct IAVIStreamVtbl iavist = {
121 IAVIStream_fnQueryInterface,
122 IAVIStream_fnAddRef,
123 IAVIStream_fnRelease,
124 IAVIStream_fnCreate,
125 IAVIStream_fnInfo,
126 IAVIStream_fnFindSample,
127 IAVIStream_fnReadFormat,
128 IAVIStream_fnSetFormat,
129 IAVIStream_fnRead,
130 IAVIStream_fnWrite,
131 IAVIStream_fnDelete,
132 IAVIStream_fnReadData,
133 IAVIStream_fnWriteData,
134 IAVIStream_fnSetInfo
135 };
136
137 typedef struct _IAVIFileImpl IAVIFileImpl;
138
139 typedef struct _IPersistFileImpl {
140 /* IUnknown stuff */
141 const IPersistFileVtbl *lpVtbl;
142
143 /* IPersistFile stuff */
144 IAVIFileImpl *paf;
145 } IPersistFileImpl;
146
147 typedef struct _IAVIStreamImpl {
148 /* IUnknown stuff */
149 const IAVIStreamVtbl *lpVtbl;
150 LONG ref;
151
152 /* IAVIStream stuff */
153 IAVIFileImpl *paf;
154 DWORD nStream; /* the n-th stream in file */
155 AVISTREAMINFOW sInfo;
156
157 LPVOID lpFormat;
158 DWORD cbFormat;
159
160 LPVOID lpHandlerData;
161 DWORD cbHandlerData;
162
163 EXTRACHUNKS extra;
164
165 LPDWORD lpBuffer;
166 DWORD cbBuffer; /* size of lpBuffer */
167 DWORD dwCurrentFrame; /* frame/block currently in lpBuffer */
168
169 LONG lLastFrame; /* last correct index in idxFrames */
170 AVIINDEXENTRY *idxFrames;
171 DWORD nIdxFrames; /* upper index limit of idxFrames */
172 AVIINDEXENTRY *idxFmtChanges;
173 DWORD nIdxFmtChanges; /* upper index limit of idxFmtChanges */
174 } IAVIStreamImpl;
175
176 struct _IAVIFileImpl {
177 /* IUnknown stuff */
178 const IAVIFileVtbl *lpVtbl;
179 LONG ref;
180
181 /* IAVIFile stuff... */
182 IPersistFileImpl iPersistFile;
183
184 AVIFILEINFOW fInfo;
185 IAVIStreamImpl *ppStreams[MAX_AVISTREAMS];
186
187 EXTRACHUNKS fileextra;
188
189 DWORD dwMoviChunkPos; /* some stuff for saving ... */
190 DWORD dwIdxChunkPos;
191 DWORD dwNextFramePos;
192 DWORD dwInitialFrames;
193
194 MMCKINFO ckLastRecord;
195 AVIINDEXENTRY *idxRecords; /* won't be updated while loading */
196 DWORD nIdxRecords; /* current fill level */
197 DWORD cbIdxRecords; /* size of idxRecords */
198
199 /* IPersistFile stuff ... */
200 HMMIO hmmio;
201 LPWSTR szFileName;
202 UINT uMode;
203 BOOL fDirty;
204 };
205
206 /***********************************************************************/
207
208 static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
209 DWORD offset, DWORD flags);
210 static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
211 static DWORD AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
212 static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
213 const AVISTREAMINFOW *asi);
214 static void AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
215 static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
216 static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
217 static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
218 LONG count, DWORD pos, BOOL *bAbsolute);
219 static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
220 LPVOID buffer, LONG size);
221 static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
222 LPLONG offset);
223 static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
224 static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
225 static ULONG AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
226 LONG lSkip);
227 static void AVIFILE_UpdateInfo(IAVIFileImpl *This);
228 static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
229 FOURCC ckid, DWORD flags, LPVOID buffer,
230 LONG size);
231
232 HRESULT AVIFILE_CreateAVIFile(REFIID riid, LPVOID *ppv)
233 {
234 IAVIFileImpl *pfile;
235 HRESULT hr;
236
237 assert(riid != NULL && ppv != NULL);
238
239 *ppv = NULL;
240
241 pfile = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
242 if (pfile == NULL)
243 return AVIERR_MEMORY;
244
245 pfile->lpVtbl = &iavift;
246 pfile->ref = 0;
247 pfile->iPersistFile.lpVtbl = &ipersistft;
248 pfile->iPersistFile.paf = pfile;
249
250 hr = IAVIFile_QueryInterface((IAVIFile*)pfile, riid, ppv);
251 if (FAILED(hr))
252 HeapFree(GetProcessHeap(), 0, pfile);
253
254 return hr;
255 }
256
257 static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID refiid,
258 LPVOID *obj)
259 {
260 IAVIFileImpl *This = (IAVIFileImpl *)iface;
261
262 TRACE("(%p,%s,%p)\n", This, debugstr_guid(refiid), obj);
263
264 if (IsEqualGUID(&IID_IUnknown, refiid) ||
265 IsEqualGUID(&IID_IAVIFile, refiid)) {
266 *obj = iface;
267 IAVIFile_AddRef(iface);
268
269 return S_OK;
270 } else if (IsEqualGUID(&IID_IPersistFile, refiid)) {
271 *obj = &This->iPersistFile;
272 IAVIFile_AddRef(iface);
273
274 return S_OK;
275 }
276
277 return OLE_E_ENUM_NOMORE;
278 }
279
280 static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
281 {
282 IAVIFileImpl *This = (IAVIFileImpl *)iface;
283 ULONG ref = InterlockedIncrement(&This->ref);
284
285 TRACE("(%p) -> %d\n", iface, ref);
286
287 return ref;
288 }
289
290 static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
291 {
292 IAVIFileImpl *This = (IAVIFileImpl *)iface;
293 UINT i;
294 ULONG ref = InterlockedDecrement(&This->ref);
295
296 TRACE("(%p) -> %d\n", iface, ref);
297
298 if (!ref) {
299 if (This->fDirty) {
300 /* need to write headers to file */
301 AVIFILE_SaveFile(This);
302 }
303
304 for (i = 0; i < This->fInfo.dwStreams; i++) {
305 if (This->ppStreams[i] != NULL) {
306 if (This->ppStreams[i]->ref != 0) {
307 ERR(": someone has still %u reference to stream %u (%p)!\n",
308 This->ppStreams[i]->ref, i, This->ppStreams[i]);
309 }
310 AVIFILE_DestructAVIStream(This->ppStreams[i]);
311 HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
312 This->ppStreams[i] = NULL;
313 }
314 }
315
316 if (This->idxRecords != NULL) {
317 HeapFree(GetProcessHeap(), 0, This->idxRecords);
318 This->idxRecords = NULL;
319 This->nIdxRecords = 0;
320 }
321
322 if (This->fileextra.lp != NULL) {
323 HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
324 This->fileextra.lp = NULL;
325 This->fileextra.cb = 0;
326 }
327
328 HeapFree(GetProcessHeap(), 0, This->szFileName);
329 This->szFileName = NULL;
330
331 if (This->hmmio != NULL) {
332 mmioClose(This->hmmio, 0);
333 This->hmmio = NULL;
334 }
335
336 HeapFree(GetProcessHeap(), 0, This);
337 }
338 return ref;
339 }
340
341 static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, LPAVIFILEINFOW afi,
342 LONG size)
343 {
344 IAVIFileImpl *This = (IAVIFileImpl *)iface;
345
346 TRACE("(%p,%p,%d)\n",iface,afi,size);
347
348 if (afi == NULL)
349 return AVIERR_BADPARAM;
350 if (size < 0)
351 return AVIERR_BADSIZE;
352
353 AVIFILE_UpdateInfo(This);
354
355 memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
356
357 if ((DWORD)size < sizeof(This->fInfo))
358 return AVIERR_BUFFERTOOSMALL;
359 return AVIERR_OK;
360 }
361
362 static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, PAVISTREAM *avis,
363 DWORD fccType, LONG lParam)
364 {
365 IAVIFileImpl *This = (IAVIFileImpl *)iface;
366
367 ULONG nStream;
368
369 TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
370
371 if (avis == NULL || lParam < 0)
372 return AVIERR_BADPARAM;
373
374 nStream = AVIFILE_SearchStream(This, fccType, lParam);
375
376 /* Does the requested stream exist? */
377 if (nStream < This->fInfo.dwStreams &&
378 This->ppStreams[nStream] != NULL) {
379 *avis = (PAVISTREAM)This->ppStreams[nStream];
380 IAVIStream_AddRef(*avis);
381
382 return AVIERR_OK;
383 }
384
385 /* Sorry, but the specified stream doesn't exist */
386 return AVIERR_NODATA;
387 }
388
389 static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface,PAVISTREAM *avis,
390 LPAVISTREAMINFOW asi)
391 {
392 IAVIFileImpl *This = (IAVIFileImpl *)iface;
393
394 DWORD n;
395
396 TRACE("(%p,%p,%p)\n", iface, avis, asi);
397
398 /* check parameters */
399 if (avis == NULL || asi == NULL)
400 return AVIERR_BADPARAM;
401
402 *avis = NULL;
403
404 /* Does the user have write permission? */
405 if ((This->uMode & MMIO_RWMODE) == 0)
406 return AVIERR_READONLY;
407
408 /* Can we add another stream? */
409 n = This->fInfo.dwStreams;
410 if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
411 /* already reached max nr of streams
412 * or have already written frames to disk */
413 return AVIERR_UNSUPPORTED;
414 }
415
416 /* check AVISTREAMINFO for some really needed things */
417 if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
418 return AVIERR_BADFORMAT;
419
420 /* now it seems to be save to add the stream */
421 assert(This->ppStreams[n] == NULL);
422 This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
423 sizeof(IAVIStreamImpl));
424 if (This->ppStreams[n] == NULL)
425 return AVIERR_MEMORY;
426
427 /* initialize the new allocated stream */
428 AVIFILE_ConstructAVIStream(This, n, asi);
429
430 This->fInfo.dwStreams++;
431 This->fDirty = TRUE;
432
433 /* update our AVIFILEINFO structure */
434 AVIFILE_UpdateInfo(This);
435
436 /* return it */
437 *avis = (PAVISTREAM)This->ppStreams[n];
438 IAVIStream_AddRef(*avis);
439
440 return AVIERR_OK;
441 }
442
443 static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid,
444 LPVOID lpData, LONG size)
445 {
446 IAVIFileImpl *This = (IAVIFileImpl *)iface;
447
448 TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
449
450 /* check parameters */
451 if (lpData == NULL)
452 return AVIERR_BADPARAM;
453 if (size < 0)
454 return AVIERR_BADSIZE;
455
456 /* Do we have write permission? */
457 if ((This->uMode & MMIO_RWMODE) == 0)
458 return AVIERR_READONLY;
459
460 This->fDirty = TRUE;
461
462 return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
463 }
464
465 static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid,
466 LPVOID lpData, LONG *size)
467 {
468 IAVIFileImpl *This = (IAVIFileImpl *)iface;
469
470 TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
471
472 return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
473 }
474
475 static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
476 {
477 IAVIFileImpl *This = (IAVIFileImpl *)iface;
478
479 TRACE("(%p)\n",iface);
480
481 if ((This->uMode & MMIO_RWMODE) == 0)
482 return AVIERR_READONLY;
483
484 This->fDirty = TRUE;
485
486 /* no frames written to any stream? -- compute start of 'movi'-chunk */
487 if (This->dwMoviChunkPos == 0)
488 AVIFILE_ComputeMoviStart(This);
489
490 This->fInfo.dwFlags |= AVIFILEINFO_ISINTERLEAVED;
491
492 /* already written frames to any stream, ... */
493 if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
494 /* close last record */
495 if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
496 return AVIERR_FILEWRITE;
497
498 AVIFILE_AddRecord(This);
499
500 if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
501 This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
502 }
503
504 /* write out a new record into file, but don't close it */
505 This->ckLastRecord.cksize = 0;
506 This->ckLastRecord.fccType = listtypeAVIRECORD;
507 if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
508 return AVIERR_FILEWRITE;
509 if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
510 return AVIERR_FILEWRITE;
511 This->dwNextFramePos += 3 * sizeof(DWORD);
512
513 return AVIERR_OK;
514 }
515
516 static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType,
517 LONG lParam)
518 {
519 IAVIFileImpl *This = (IAVIFileImpl *)iface;
520
521 ULONG nStream;
522
523 TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
524
525 /* check parameter */
526 if (lParam < 0)
527 return AVIERR_BADPARAM;
528
529 /* Have user write permissions? */
530 if ((This->uMode & MMIO_RWMODE) == 0)
531 return AVIERR_READONLY;
532
533 nStream = AVIFILE_SearchStream(This, fccType, lParam);
534
535 /* Does the requested stream exist? */
536 if (nStream < This->fInfo.dwStreams &&
537 This->ppStreams[nStream] != NULL) {
538 /* ... so delete it now */
539 HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
540
541 if (This->fInfo.dwStreams - nStream > 0)
542 memcpy(This->ppStreams + nStream, This->ppStreams + nStream + 1,
543 (This->fInfo.dwStreams - nStream) * sizeof(IAVIStreamImpl*));
544
545 This->ppStreams[This->fInfo.dwStreams] = NULL;
546 This->fInfo.dwStreams--;
547 This->fDirty = TRUE;
548
549 /* This->fInfo will be updated further when asked for */
550 return AVIERR_OK;
551 } else
552 return AVIERR_NODATA;
553 }
554
555 /***********************************************************************/
556
557 static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface,
558 REFIID refiid, LPVOID *obj)
559 {
560 IPersistFileImpl *This = (IPersistFileImpl *)iface;
561
562 assert(This->paf != NULL);
563
564 return IAVIFile_QueryInterface((PAVIFILE)This->paf, refiid, obj);
565 }
566
567 static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
568 {
569 IPersistFileImpl *This = (IPersistFileImpl *)iface;
570
571 assert(This->paf != NULL);
572
573 return IAVIFile_AddRef((PAVIFILE)This->paf);
574 }
575
576 static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
577 {
578 IPersistFileImpl *This = (IPersistFileImpl *)iface;
579
580 assert(This->paf != NULL);
581
582 return IAVIFile_Release((PAVIFILE)This->paf);
583 }
584
585 static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface,
586 LPCLSID pClassID)
587 {
588 TRACE("(%p,%p)\n", iface, pClassID);
589
590 if (pClassID == NULL)
591 return AVIERR_BADPARAM;
592
593 *pClassID = CLSID_AVIFile;
594
595 return AVIERR_OK;
596 }
597
598 static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
599 {
600 IPersistFileImpl *This = (IPersistFileImpl *)iface;
601
602 TRACE("(%p)\n", iface);
603
604 assert(This->paf != NULL);
605
606 return (This->paf->fDirty ? S_OK : S_FALSE);
607 }
608
609 static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface,
610 LPCOLESTR pszFileName, DWORD dwMode)
611 {
612 IPersistFileImpl *This = (IPersistFileImpl *)iface;
613
614 int len;
615
616 TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
617
618 /* check parameter */
619 if (pszFileName == NULL)
620 return AVIERR_BADPARAM;
621
622 assert(This->paf != NULL);
623 if (This->paf->hmmio != NULL)
624 return AVIERR_ERROR; /* No reuse of this object for another file! */
625
626 /* remember mode and name */
627 This->paf->uMode = dwMode;
628
629 len = lstrlenW(pszFileName) + 1;
630 This->paf->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
631 if (This->paf->szFileName == NULL)
632 return AVIERR_MEMORY;
633 lstrcpyW(This->paf->szFileName, pszFileName);
634
635 /* try to open the file */
636 This->paf->hmmio = mmioOpenW(This->paf->szFileName, NULL,
637 MMIO_ALLOCBUF | dwMode);
638 if (This->paf->hmmio == NULL) {
639 /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
640 LPSTR szFileName;
641
642 len = WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1,
643 NULL, 0, NULL, NULL);
644 szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
645 if (szFileName == NULL)
646 return AVIERR_MEMORY;
647
648 WideCharToMultiByte(CP_ACP, 0, This->paf->szFileName, -1, szFileName,
649 len, NULL, NULL);
650
651 This->paf->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
652 HeapFree(GetProcessHeap(), 0, szFileName);
653 if (This->paf->hmmio == NULL)
654 return AVIERR_FILEOPEN;
655 }
656
657 /* should we create a new file? */
658 if (dwMode & OF_CREATE) {
659 memset(& This->paf->fInfo, 0, sizeof(This->paf->fInfo));
660 This->paf->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
661
662 return AVIERR_OK;
663 } else
664 return AVIFILE_LoadFile(This->paf);
665 }
666
667 static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface,
668 LPCOLESTR pszFileName,BOOL fRemember)
669 {
670 TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
671
672 /* We write directly to disk, so nothing to do. */
673
674 return AVIERR_OK;
675 }
676
677 static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface,
678 LPCOLESTR pszFileName)
679 {
680 TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));
681
682 /* We write directly to disk, so nothing to do. */
683
684 return AVIERR_OK;
685 }
686
687 static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface,
688 LPOLESTR *ppszFileName)
689 {
690 IPersistFileImpl *This = (IPersistFileImpl *)iface;
691
692 TRACE("(%p,%p)\n", iface, ppszFileName);
693
694 if (ppszFileName == NULL)
695 return AVIERR_BADPARAM;
696
697 *ppszFileName = NULL;
698
699 assert(This->paf != NULL);
700
701 if (This->paf->szFileName != NULL) {
702 int len = lstrlenW(This->paf->szFileName) + 1;
703
704 *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
705 if (*ppszFileName == NULL)
706 return AVIERR_MEMORY;
707
708 strcpyW(*ppszFileName, This->paf->szFileName);
709 }
710
711 return AVIERR_OK;
712 }
713
714 /***********************************************************************/
715
716 static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface,
717 REFIID refiid, LPVOID *obj)
718 {
719 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
720
721 TRACE("(%p,%s,%p)\n", iface, debugstr_guid(refiid), obj);
722
723 if (IsEqualGUID(&IID_IUnknown, refiid) ||
724 IsEqualGUID(&IID_IAVIStream, refiid)) {
725 *obj = This;
726 IAVIStream_AddRef(iface);
727
728 return S_OK;
729 }
730 /* FIXME: IAVIStreaming interface */
731
732 return OLE_E_ENUM_NOMORE;
733 }
734
735 static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
736 {
737 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
738 ULONG ref = InterlockedIncrement(&This->ref);
739
740 TRACE("(%p) -> %d\n", iface, ref);
741
742 /* also add ref to parent, so that it doesn't kill us */
743 if (This->paf != NULL)
744 IAVIFile_AddRef((PAVIFILE)This->paf);
745
746 return ref;
747 }
748
749 static ULONG WINAPI IAVIStream_fnRelease(IAVIStream* iface)
750 {
751 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
752 ULONG ref = InterlockedDecrement(&This->ref);
753
754 TRACE("(%p) -> %d\n", iface, ref);
755
756 if (This->paf != NULL)
757 IAVIFile_Release((PAVIFILE)This->paf);
758
759 return ref;
760 }
761
762 static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1,
763 LPARAM lParam2)
764 {
765 TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);
766
767 /* This IAVIStream interface needs an AVIFile */
768 return AVIERR_UNSUPPORTED;
769 }
770
771 static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface,LPAVISTREAMINFOW psi,
772 LONG size)
773 {
774 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
775
776 TRACE("(%p,%p,%d)\n", iface, psi, size);
777
778 if (psi == NULL)
779 return AVIERR_BADPARAM;
780 if (size < 0)
781 return AVIERR_BADSIZE;
782
783 memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
784
785 if ((DWORD)size < sizeof(This->sInfo))
786 return AVIERR_BUFFERTOOSMALL;
787 return AVIERR_OK;
788 }
789
790 static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos,
791 LONG flags)
792 {
793 IAVIStreamImpl *This = (IAVIStreamImpl *)iface;
794
795 LONG offset = 0;
796
797 TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
798
799 if (flags & FIND_FROM_START) {
800 pos = This->sInfo.dwStart;
801 flags &= ~(FIND_FROM_START|FIND_PREV);
802 flags |= FIND_NEXT;
803 }
804
805 if (This->sInfo.dwSampleSize != 0) {
806 /* convert samples into block number with offset */
807 AVIFILE_SamplesToBlock(This, &pos, &offset);
808 }
809
810 if (flags & FIND_TYPE) {
811 if (flags & FIND_KEY) {
812 while (0 <= pos && pos <= This->lLastFrame) {
813 if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
814 goto RETURN_FOUND;
815
816 if (flags & FIND_NEXT)
817 pos++;
818 else
819 pos--;
820 };
821 } else if (flags & FIND_ANY) {
822 while (0 <= pos && pos <= This->lLastFrame) {
823 if (This->idxFrames[pos].dwChunkLength > 0)
824 goto RETURN_FOUND;
825
826 if (flags & FIND_NEXT)
827 pos++;
828 else
829 pos--;
830
831 };
832 } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
833 This->sInfo.fccType == streamtypeVIDEO) {
834 if (flags & FIND_NEXT) {
835 ULONG n;
836
837 for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
838 if (This->idxFmtChange