avifile.c 78.4 KB
Newer Older
1
/*
2
 * Copyright 1999 Marcus Meissner
3
 * Copyright 2002-2003 Michael Günnewig
4 5 6 7 8 9 10 11 12 13 14 15 16
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
17
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18 19
 */

20 21 22 23 24 25
/* TODO:
 *  - IAVIStreaming interface is missing for the IAVIStreamImpl
 *  - IAVIStream_fnFindSample: FIND_INDEX isn't supported.
 *  - IAVIStream_fnReadFormat: formatchanges aren't read in.
 *  - IAVIStream_fnDelete: a stub.
 *  - IAVIStream_fnSetInfo: a stub.
26 27 28 29 30
 *  - make thread safe
 *
 * KNOWN Bugs:
 *  - native version can hangup when reading a file generated with this DLL.
 *    When index is missing it works, but index seems to be okay.
31 32
 */

33
#define COBJMACROS
34
#include <assert.h>
35
#include <stdarg.h>
36

37
#include "windef.h"
38
#include "winbase.h"
39
#include "wingdi.h"
40
#include "winuser.h"
41 42
#include "winnls.h"
#include "winerror.h"
43
#include "mmsystem.h"
44
#include "vfw.h"
45 46

#include "avifile_private.h"
47
#include "extrachunk.h"
48

49
#include "wine/unicode.h"
50 51 52 53
#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(avifile);

54 55 56 57
#ifndef IDX_PER_BLOCK
#define IDX_PER_BLOCK 2730
#endif

58 59 60
typedef struct _IAVIFileImpl IAVIFileImpl;

typedef struct _IAVIStreamImpl {
61 62
  IAVIStream        IAVIStream_iface;
  LONG              ref;
63 64

  IAVIFileImpl     *paf;
65
  DWORD             nStream;       /* the n-th stream in file */
66
  AVISTREAMINFOW    sInfo;
67 68 69 70 71 72 73 74 75 76 77 78 79

  LPVOID            lpFormat;
  DWORD             cbFormat;

  LPVOID            lpHandlerData;
  DWORD             cbHandlerData;

  EXTRACHUNKS       extra;

  LPDWORD           lpBuffer;
  DWORD             cbBuffer;       /* size of lpBuffer */
  DWORD             dwCurrentFrame; /* frame/block currently in lpBuffer */

80
  LONG              lLastFrame;    /* last correct index in idxFrames */
81 82 83 84
  AVIINDEXENTRY    *idxFrames;
  DWORD             nIdxFrames;     /* upper index limit of idxFrames */
  AVIINDEXENTRY    *idxFmtChanges;
  DWORD             nIdxFmtChanges; /* upper index limit of idxFmtChanges */
85 86
} IAVIStreamImpl;

87 88 89 90 91
static inline IAVIStreamImpl *impl_from_IAVIStream(IAVIStream *iface)
{
  return CONTAINING_RECORD(iface, IAVIStreamImpl, IAVIStream_iface);
}

92
struct _IAVIFileImpl {
93
  IUnknown          IUnknown_inner;
94
  IAVIFile          IAVIFile_iface;
95
  IPersistFile      IPersistFile_iface;
96
  IUnknown         *outer_unk;
97
  LONG              ref;
98 99 100 101

  AVIFILEINFOW      fInfo;
  IAVIStreamImpl   *ppStreams[MAX_AVISTREAMS];

102 103 104 105 106
  EXTRACHUNKS       fileextra;

  DWORD             dwMoviChunkPos;  /* some stuff for saving ... */
  DWORD             dwIdxChunkPos;
  DWORD             dwNextFramePos;
107
  DWORD             dwInitialFrames;
108

109
  MMCKINFO          ckLastRecord;
110
  AVIINDEXENTRY    *idxRecords;      /* won't be updated while loading */
111 112
  DWORD             nIdxRecords;     /* current fill level */
  DWORD             cbIdxRecords;    /* size of idxRecords */
113

114 115 116 117 118 119 120
  /* IPersistFile stuff ... */
  HMMIO             hmmio;
  LPWSTR            szFileName;
  UINT              uMode;
  BOOL              fDirty;
};

121 122 123 124 125
static inline IAVIFileImpl *impl_from_IUnknown(IUnknown *iface)
{
  return CONTAINING_RECORD(iface, IAVIFileImpl, IUnknown_inner);
}

126 127 128 129 130
static inline IAVIFileImpl *impl_from_IAVIFile(IAVIFile *iface)
{
  return CONTAINING_RECORD(iface, IAVIFileImpl, IAVIFile_iface);
}

131 132 133 134 135
static inline IAVIFileImpl *impl_from_IPersistFile(IPersistFile *iface)
{
  return CONTAINING_RECORD(iface, IAVIFileImpl, IPersistFile_iface);
}

136 137
/***********************************************************************/

138 139
static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size,
				DWORD offset, DWORD flags);
140
static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This);
141 142
static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This);
static void    AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr,
143
					  const AVISTREAMINFOW *asi);
144 145
static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This);
static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This);
146 147
static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset);
static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
148 149
				  LONG count, DWORD pos, BOOL *bAbsolute);
static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD start,
150
				 LPVOID buffer, DWORD size);
151
static void    AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos,
152 153
				      LPLONG offset);
static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This);
154 155
static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This);
static ULONG   AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fccType,
156 157 158
				    LONG lSkip);
static void    AVIFILE_UpdateInfo(IAVIFileImpl *This);
static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
159
				  FOURCC ckid, DWORD flags, LPCVOID buffer,
160 161
				  LONG size);

162
static HRESULT WINAPI IUnknown_fnQueryInterface(IUnknown *iface, REFIID riid, void **ppv)
163
{
164
  IAVIFileImpl *This = impl_from_IUnknown(iface);
165

166
  TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);
167

168 169 170 171 172
  if (!ppv) {
    WARN("invalid parameter\n");
    return E_INVALIDARG;
  }
  *ppv = NULL;
173

174 175 176 177 178 179 180 181 182
  if (IsEqualIID(riid, &IID_IUnknown))
    *ppv = &This->IUnknown_inner;
  else if (IsEqualIID(riid, &IID_IAVIFile))
    *ppv = &This->IAVIFile_iface;
  else if (IsEqualGUID(riid, &IID_IPersistFile))
    *ppv = &This->IPersistFile_iface;
  else {
    WARN("unknown IID %s\n", debugstr_guid(riid));
    return E_NOINTERFACE;
183
  }
184

185 186 187
  /* Violation of the COM aggregation ref counting rule */
  IUnknown_AddRef(&This->IUnknown_inner);
  return S_OK;
188 189
}

190
static ULONG WINAPI IUnknown_fnAddRef(IUnknown *iface)
191
{
192
  IAVIFileImpl *This = impl_from_IUnknown(iface);
193
  ULONG ref = InterlockedIncrement(&This->ref);
194

195
  TRACE("(%p) ref=%d\n", This, ref);
196 197

  return ref;
198 199
}

200
static ULONG WINAPI IUnknown_fnRelease(IUnknown *iface)
201
{
202
  IAVIFileImpl *This = impl_from_IUnknown(iface);
203
  ULONG ref = InterlockedDecrement(&This->ref);
204
  UINT i;
205

206
  TRACE("(%p) ref=%d\n", This, ref);
207

208
  if (!ref) {
209
    if (This->fDirty)
210
      AVIFILE_SaveFile(This);
211

212 213
    for (i = 0; i < This->fInfo.dwStreams; i++) {
      if (This->ppStreams[i] != NULL) {
214
        if (This->ppStreams[i]->ref != 0)
215
          ERR(": someone has still %u reference to stream %u (%p)!\n",
216 217 218 219
              This->ppStreams[i]->ref, i, This->ppStreams[i]);
        AVIFILE_DestructAVIStream(This->ppStreams[i]);
        HeapFree(GetProcessHeap(), 0, This->ppStreams[i]);
        This->ppStreams[i] = NULL;
220 221 222 223
      }
    }

    if (This->idxRecords != NULL) {
224
      HeapFree(GetProcessHeap(), 0, This->idxRecords);
225 226 227 228 229
      This->idxRecords  = NULL;
      This->nIdxRecords = 0;
    }

    if (This->fileextra.lp != NULL) {
230
      HeapFree(GetProcessHeap(), 0, This->fileextra.lp);
231 232 233 234
      This->fileextra.lp = NULL;
      This->fileextra.cb = 0;
    }

235 236 237
    HeapFree(GetProcessHeap(), 0, This->szFileName);
    This->szFileName = NULL;

238
    if (This->hmmio != NULL) {
239
      mmioClose(This->hmmio, 0);
240
      This->hmmio = NULL;
241 242
    }

243
    HeapFree(GetProcessHeap(), 0, This);
244
  }
245
  return ref;
246 247
}

248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
static const IUnknownVtbl unk_vtbl =
{
  IUnknown_fnQueryInterface,
  IUnknown_fnAddRef,
  IUnknown_fnRelease
};

static HRESULT WINAPI IAVIFile_fnQueryInterface(IAVIFile *iface, REFIID riid, void **ppv)
{
  IAVIFileImpl *This = impl_from_IAVIFile(iface);

  return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
}

static ULONG WINAPI IAVIFile_fnAddRef(IAVIFile *iface)
{
  IAVIFileImpl *This = impl_from_IAVIFile(iface);

  return IUnknown_AddRef(This->outer_unk);
}

static ULONG WINAPI IAVIFile_fnRelease(IAVIFile *iface)
{
  IAVIFileImpl *This = impl_from_IAVIFile(iface);

  return IUnknown_Release(This->outer_unk);
}

276
static HRESULT WINAPI IAVIFile_fnInfo(IAVIFile *iface, AVIFILEINFOW *afi, LONG size)
277
{
278
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
279

280
  TRACE("(%p,%p,%d)\n",iface,afi,size);
281 282 283 284 285 286

  if (afi == NULL)
    return AVIERR_BADPARAM;
  if (size < 0)
    return AVIERR_BADSIZE;

287 288
  AVIFILE_UpdateInfo(This);

289
  memcpy(afi, &This->fInfo, min((DWORD)size, sizeof(This->fInfo)));
290

291
  if ((DWORD)size < sizeof(This->fInfo))
292 293
    return AVIERR_BUFFERTOOSMALL;
  return AVIERR_OK;
294 295
}

296 297
static HRESULT WINAPI IAVIFile_fnGetStream(IAVIFile *iface, IAVIStream **avis, DWORD fccType,
    LONG lParam)
298
{
299
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
300 301
  ULONG nStream;

302
  TRACE("(%p,%p,0x%08X,%d)\n", iface, avis, fccType, lParam);
303 304 305 306 307 308 309 310 311

  if (avis == NULL || lParam < 0)
    return AVIERR_BADPARAM;

  nStream = AVIFILE_SearchStream(This, fccType, lParam);

  /* Does the requested stream exist? */
  if (nStream < This->fInfo.dwStreams &&
      This->ppStreams[nStream] != NULL) {
312
    *avis = &This->ppStreams[nStream]->IAVIStream_iface;
313 314 315 316 317 318 319
    IAVIStream_AddRef(*avis);

    return AVIERR_OK;
  }

  /* Sorry, but the specified stream doesn't exist */
  return AVIERR_NODATA;
320 321
}

322 323
static HRESULT WINAPI IAVIFile_fnCreateStream(IAVIFile *iface, IAVIStream **avis,
    AVISTREAMINFOW *asi)
324
{
325
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
326
  DWORD n;
327

328 329 330 331 332 333
  TRACE("(%p,%p,%p)\n", iface, avis, asi);

  /* check parameters */
  if (avis == NULL || asi == NULL)
    return AVIERR_BADPARAM;

334 335
  *avis = NULL;

336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
  /* Does the user have write permission? */
  if ((This->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  /* Can we add another stream? */
  n = This->fInfo.dwStreams;
  if (n >= MAX_AVISTREAMS || This->dwMoviChunkPos != 0) {
    /* already reached max nr of streams
     * or have already written frames to disk */
    return AVIERR_UNSUPPORTED;
  }

  /* check AVISTREAMINFO for some really needed things */
  if (asi->fccType == 0 || asi->dwScale == 0 || asi->dwRate == 0)
    return AVIERR_BADFORMAT;

  /* now it seems to be save to add the stream */
  assert(This->ppStreams[n] == NULL);
354
  This->ppStreams[n] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
355 356 357 358 359 360 361 362 363 364 365 366 367
						   sizeof(IAVIStreamImpl));
  if (This->ppStreams[n] == NULL)
    return AVIERR_MEMORY;

  /* initialize the new allocated stream */
  AVIFILE_ConstructAVIStream(This, n, asi);

  This->fInfo.dwStreams++;
  This->fDirty = TRUE;

  /* update our AVIFILEINFO structure */
  AVIFILE_UpdateInfo(This);

368
  /* return it */
369
  *avis = &This->ppStreams[n]->IAVIStream_iface;
370 371
  IAVIStream_AddRef(*avis);

372
  return AVIERR_OK;
373 374
}

375
static HRESULT WINAPI IAVIFile_fnWriteData(IAVIFile *iface, DWORD ckid, void *lpData, LONG size)
376
{
377
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
378

379
  TRACE("(%p,0x%08X,%p,%d)\n", iface, ckid, lpData, size);
380 381 382 383 384 385 386 387 388 389 390 391 392 393

  /* check parameters */
  if (lpData == NULL)
    return AVIERR_BADPARAM;
  if (size < 0)
    return AVIERR_BADSIZE;

  /* Do we have write permission? */
  if ((This->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  This->fDirty = TRUE;

  return WriteExtraChunk(&This->fileextra, ckid, lpData, size);
394 395
}

396
static HRESULT WINAPI IAVIFile_fnReadData(IAVIFile *iface, DWORD ckid, void *lpData, LONG *size)
397
{
398
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
399

400
  TRACE("(%p,0x%08X,%p,%p)\n", iface, ckid, lpData, size);
401 402

  return ReadExtraChunk(&This->fileextra, ckid, lpData, size);
403 404
}

405 406
static HRESULT WINAPI IAVIFile_fnEndRecord(IAVIFile *iface)
{
407
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
408

409
  TRACE("(%p)\n",iface);
410 411 412 413 414 415

  if ((This->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  This->fDirty = TRUE;

416 417 418
  /* no frames written to any stream? -- compute start of 'movi'-chunk */
  if (This->dwMoviChunkPos == 0)
    AVIFILE_ComputeMoviStart(This);
419

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
  This->fInfo.dwFlags  |= AVIFILEINFO_ISINTERLEAVED;

  /* already written frames to any stream, ... */
  if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
    /* close last record */
    if (mmioAscend(This->hmmio, &This->ckLastRecord, 0) != 0)
      return AVIERR_FILEWRITE;

    AVIFILE_AddRecord(This);

    if (This->fInfo.dwSuggestedBufferSize < This->ckLastRecord.cksize + 3 * sizeof(DWORD))
      This->fInfo.dwSuggestedBufferSize = This->ckLastRecord.cksize + 3 * sizeof(DWORD);
  }

  /* write out a new record into file, but don't close it */
  This->ckLastRecord.cksize  = 0;
  This->ckLastRecord.fccType = listtypeAVIRECORD;
  if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
    return AVIERR_FILEWRITE;
  if (mmioCreateChunk(This->hmmio, &This->ckLastRecord, MMIO_CREATELIST) != 0)
    return AVIERR_FILEWRITE;
  This->dwNextFramePos += 3 * sizeof(DWORD);

  return AVIERR_OK;
444 445
}

446
static HRESULT WINAPI IAVIFile_fnDeleteStream(IAVIFile *iface, DWORD fccType, LONG lParam)
447
{
448
  IAVIFileImpl *This = impl_from_IAVIFile(iface);
449 450
  ULONG nStream;

451
  TRACE("(%p,0x%08X,%d)\n", iface, fccType, lParam);
452 453 454 455 456

  /* check parameter */
  if (lParam < 0)
    return AVIERR_BADPARAM;

457
  /* Have user write permissions? */
458 459 460 461 462 463 464 465 466
  if ((This->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  nStream = AVIFILE_SearchStream(This, fccType, lParam);

  /* Does the requested stream exist? */
  if (nStream < This->fInfo.dwStreams &&
      This->ppStreams[nStream] != NULL) {
    /* ... so delete it now */
467
    HeapFree(GetProcessHeap(), 0, This->ppStreams[nStream]);
468 469 470 471
    This->fInfo.dwStreams--;
    if (nStream < This->fInfo.dwStreams)
      memmove(&This->ppStreams[nStream], &This->ppStreams[nStream + 1],
             (This->fInfo.dwStreams - nStream) * sizeof(This->ppStreams[0]));
472 473 474 475 476 477 478 479

    This->ppStreams[This->fInfo.dwStreams] = NULL;
    This->fDirty = TRUE;

    /* This->fInfo will be updated further when asked for */
    return AVIERR_OK;
  } else
    return AVIERR_NODATA;
480 481
}

482 483 484 485 486 487 488 489 490 491 492 493 494
static const struct IAVIFileVtbl avif_vt = {
  IAVIFile_fnQueryInterface,
  IAVIFile_fnAddRef,
  IAVIFile_fnRelease,
  IAVIFile_fnInfo,
  IAVIFile_fnGetStream,
  IAVIFile_fnCreateStream,
  IAVIFile_fnWriteData,
  IAVIFile_fnReadData,
  IAVIFile_fnEndRecord,
  IAVIFile_fnDeleteStream
};

495

496
static HRESULT WINAPI IPersistFile_fnQueryInterface(IPersistFile *iface, REFIID riid, void **ppv)
497
{
498
  IAVIFileImpl *This = impl_from_IPersistFile(iface);
499

500
  return IUnknown_QueryInterface(This->outer_unk, riid, ppv);
501 502
}

503
static ULONG WINAPI IPersistFile_fnAddRef(IPersistFile *iface)
504
{
505
  IAVIFileImpl *This = impl_from_IPersistFile(iface);
506

507
  return IUnknown_AddRef(This->outer_unk);
508 509
}

510
static ULONG WINAPI IPersistFile_fnRelease(IPersistFile *iface)
511
{
512
  IAVIFileImpl *This = impl_from_IPersistFile(iface);
513

514
  return IUnknown_Release(This->outer_unk);
515 516
}

517
static HRESULT WINAPI IPersistFile_fnGetClassID(IPersistFile *iface, LPCLSID pClassID)
518 519
{
  TRACE("(%p,%p)\n", iface, pClassID);
520

521 522 523
  if (pClassID == NULL)
    return AVIERR_BADPARAM;

524
  *pClassID = CLSID_AVIFile;
525

526
  return AVIERR_OK;
527 528
}

529 530
static HRESULT WINAPI IPersistFile_fnIsDirty(IPersistFile *iface)
{
531
  IAVIFileImpl *This = impl_from_IPersistFile(iface);
532 533

  TRACE("(%p)\n", iface);
534

535
  return (This->fDirty ? S_OK : S_FALSE);
536 537
}

538
static HRESULT WINAPI IPersistFile_fnLoad(IPersistFile *iface, LPCOLESTR pszFileName, DWORD dwMode)
539
{
540
  IAVIFileImpl *This = impl_from_IPersistFile(iface);
541 542
  int len;

543
  TRACE("(%p,%s,0x%08X)\n", iface, debugstr_w(pszFileName), dwMode);
544 545 546 547

  /* check parameter */
  if (pszFileName == NULL)
    return AVIERR_BADPARAM;
548

549
  if (This->hmmio != NULL)
550 551
    return AVIERR_ERROR; /* No reuse of this object for another file! */

Austin English's avatar
Austin English committed
552
  /* remember mode and name */
553
  This->uMode = dwMode;
554

555
  len = lstrlenW(pszFileName) + 1;
556 557
  This->szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
  if (This->szFileName == NULL)
558
    return AVIERR_MEMORY;
559
  lstrcpyW(This->szFileName, pszFileName);
560 561

  /* try to open the file */
562 563
  This->hmmio = mmioOpenW(This->szFileName, NULL, MMIO_ALLOCBUF | dwMode);
  if (This->hmmio == NULL) {
564
    /* mmioOpenW not in native DLLs of Win9x -- try mmioOpenA */
565 566
    LPSTR szFileName;

567
    len = WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, NULL, 0, NULL, NULL);
568
    szFileName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(CHAR));
569 570 571
    if (szFileName == NULL)
      return AVIERR_MEMORY;

572
    WideCharToMultiByte(CP_ACP, 0, This->szFileName, -1, szFileName, len, NULL, NULL);
573

574
    This->hmmio = mmioOpenA(szFileName, NULL, MMIO_ALLOCBUF | dwMode);
575
    HeapFree(GetProcessHeap(), 0, szFileName);
576
    if (This->hmmio == NULL)
577 578
      return AVIERR_FILEOPEN;
  }
579 580 581

  /* should we create a new file? */
  if (dwMode & OF_CREATE) {
582 583
    memset(& This->fInfo, 0, sizeof(This->fInfo));
    This->fInfo.dwFlags = AVIFILEINFO_HASINDEX | AVIFILEINFO_TRUSTCKTYPE;
584 585 586

    return AVIERR_OK;
  } else
587
    return AVIFILE_LoadFile(This);
588 589
}

590 591
static HRESULT WINAPI IPersistFile_fnSave(IPersistFile *iface, LPCOLESTR pszFileName,
    BOOL fRemember)
592 593
{
  TRACE("(%p,%s,%d)\n", iface, debugstr_w(pszFileName), fRemember);
594

595 596 597
  /* We write directly to disk, so nothing to do. */

  return AVIERR_OK;
598 599
}

600
static HRESULT WINAPI IPersistFile_fnSaveCompleted(IPersistFile *iface, LPCOLESTR pszFileName)
601 602 603 604 605 606
{
  TRACE("(%p,%s)\n", iface, debugstr_w(pszFileName));

  /* We write directly to disk, so nothing to do. */

  return AVIERR_OK;
607 608
}

609
static HRESULT WINAPI IPersistFile_fnGetCurFile(IPersistFile *iface, LPOLESTR *ppszFileName)
610
{
611
  IAVIFileImpl *This = impl_from_IPersistFile(iface);
612 613 614 615 616 617 618 619

  TRACE("(%p,%p)\n", iface, ppszFileName);

  if (ppszFileName == NULL)
    return AVIERR_BADPARAM;

  *ppszFileName = NULL;

620 621
  if (This->szFileName != NULL) {
    int len = lstrlenW(This->szFileName) + 1;
622

623
    *ppszFileName = CoTaskMemAlloc(len * sizeof(WCHAR));
624 625 626
    if (*ppszFileName == NULL)
      return AVIERR_MEMORY;

627
    strcpyW(*ppszFileName, This->szFileName);
628 629 630
  }

  return AVIERR_OK;
631 632
}

633 634 635 636 637 638 639 640 641 642 643 644
static const struct IPersistFileVtbl pf_vt = {
  IPersistFile_fnQueryInterface,
  IPersistFile_fnAddRef,
  IPersistFile_fnRelease,
  IPersistFile_fnGetClassID,
  IPersistFile_fnIsDirty,
  IPersistFile_fnLoad,
  IPersistFile_fnSave,
  IPersistFile_fnSaveCompleted,
  IPersistFile_fnGetCurFile
};

645
HRESULT AVIFILE_CreateAVIFile(IUnknown *pUnkOuter, REFIID riid, void **ppv)
646 647 648 649 650 651 652 653 654
{
  IAVIFileImpl *obj;
  HRESULT hr;

  *ppv = NULL;
  obj = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIFileImpl));
  if (!obj)
    return AVIERR_MEMORY;

655
  obj->IUnknown_inner.lpVtbl = &unk_vtbl;
656 657
  obj->IAVIFile_iface.lpVtbl = &avif_vt;
  obj->IPersistFile_iface.lpVtbl = &pf_vt;
658 659 660 661 662 663 664 665
  obj->ref = 1;
  if (pUnkOuter)
    obj->outer_unk = pUnkOuter;
  else
    obj->outer_unk = &obj->IUnknown_inner;

  hr = IUnknown_QueryInterface(&obj->IUnknown_inner, riid, ppv);
  IUnknown_Release(&obj->IUnknown_inner);
666 667 668 669

  return hr;
}

670

671
static HRESULT WINAPI IAVIStream_fnQueryInterface(IAVIStream *iface, REFIID riid, void **ppv)
672
{
673
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
674

675 676 677 678 679 680 681
  TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppv);

  if (!ppv) {
    WARN("invalid parameter\n");
    return E_INVALIDARG;
  }
  *ppv = NULL;
682

683 684
  if (IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IAVIStream, riid)) {
    *ppv = iface;
685 686
    IAVIStream_AddRef(iface);

687 688 689 690
    return S_OK;
  }
  /* FIXME: IAVIStreaming interface */

691
  return E_NOINTERFACE;
692 693
}

694 695
static ULONG WINAPI IAVIStream_fnAddRef(IAVIStream *iface)
{
696
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
697
  ULONG ref = InterlockedIncrement(&This->ref);
698

699
  TRACE("(%p) ref=%d\n", This, ref);
700 701 702

  /* also add ref to parent, so that it doesn't kill us */
  if (This->paf != NULL)
703
    IAVIFile_AddRef(&This->paf->IAVIFile_iface);
704

705
  return ref;
706 707
}

708
static ULONG WINAPI IAVIStream_fnRelease(IAVIStream *iface)
709
{
710
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
711
  ULONG ref = InterlockedDecrement(&This->ref);
712

713
  TRACE("(%p) ref=%d\n", This, ref);
714

715
  if (This->paf != NULL)
716
    IAVIFile_Release(&This->paf->IAVIFile_iface);
717

718
  return ref;
719 720
}

721
static HRESULT WINAPI IAVIStream_fnCreate(IAVIStream *iface, LPARAM lParam1, LPARAM lParam2)
722 723 724 725 726
{
  TRACE("(%p,0x%08lX,0x%08lX)\n", iface, lParam1, lParam2);

  /* This IAVIStream interface needs an AVIFile */
  return AVIERR_UNSUPPORTED;
727 728
}

729
static HRESULT WINAPI IAVIStream_fnInfo(IAVIStream *iface, AVISTREAMINFOW *psi, LONG size)
730
{
731
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
732

733
  TRACE("(%p,%p,%d)\n", iface, psi, size);
734 735 736 737 738 739

  if (psi == NULL)
    return AVIERR_BADPARAM;
  if (size < 0)
    return AVIERR_BADSIZE;

740
  memcpy(psi, &This->sInfo, min((DWORD)size, sizeof(This->sInfo)));
741

742
  if ((DWORD)size < sizeof(This->sInfo))
743 744
    return AVIERR_BUFFERTOOSMALL;
  return AVIERR_OK;
745 746
}

747
static LONG WINAPI IAVIStream_fnFindSample(IAVIStream *iface, LONG pos, LONG flags)
748
{
749
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
750 751
  LONG offset = 0;

752
  TRACE("(%p,%d,0x%08X)\n",iface,pos,flags);
753 754 755 756 757 758 759 760 761 762 763 764 765 766

  if (flags & FIND_FROM_START) {
    pos = This->sInfo.dwStart;
    flags &= ~(FIND_FROM_START|FIND_PREV);
    flags |= FIND_NEXT;
  }

  if (This->sInfo.dwSampleSize != 0) {
    /* convert samples into block number with offset */
    AVIFILE_SamplesToBlock(This, &pos, &offset);
  }

  if (flags & FIND_TYPE) {
    if (flags & FIND_KEY) {
767
      while (0 <= pos && pos <= This->lLastFrame) {
768 769 770 771 772 773 774 775 776
	if (This->idxFrames[pos].dwFlags & AVIIF_KEYFRAME)
	  goto RETURN_FOUND;

	if (flags & FIND_NEXT)
	  pos++;
	else
	  pos--;
      };
    } else if (flags & FIND_ANY) {
777
      while (0 <= pos && pos <= This->lLastFrame) {
778 779 780 781 782 783 784 785 786 787 788 789
	if (This->idxFrames[pos].dwChunkLength > 0)
	  goto RETURN_FOUND;

	if (flags & FIND_NEXT)
	  pos++;
	else
	  pos--;

      };
    } else if ((flags & FIND_FORMAT) && This->idxFmtChanges != NULL &&
	       This->sInfo.fccType == streamtypeVIDEO) {
      if (flags & FIND_NEXT) {
790 791
	ULONG n;

792
	for (n = 0; n < This->sInfo.dwFormatChangeCount; n++)
793 794
	  if (This->idxFmtChanges[n].ckid >= pos) {
            pos = This->idxFmtChanges[n].ckid;
795
	    goto RETURN_FOUND;
796
          }
797
      } else {
798 799 800
	LONG n;

	for (n = (LONG)This->sInfo.dwFormatChangeCount; n >= 0; n--) {
801 802
	  if (This->idxFmtChanges[n].ckid <= pos) {
            pos = This->idxFmtChanges[n].ckid;
803
	    goto RETURN_FOUND;
804
          }
805 806
	}

807
	if (pos > (LONG)This->sInfo.dwStart)
808 809 810 811 812 813 814
	  return 0; /* format changes always for first frame */
      }
    }

    return -1;
  }

815 816 817
 RETURN_FOUND:
  if (pos < (LONG)This->sInfo.dwStart)
    return -1;
818

819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
  switch (flags & FIND_RET) {
  case FIND_LENGTH:
    /* physical size */
    pos = This->idxFrames[pos].dwChunkLength;
    break;
  case FIND_OFFSET:
    /* physical position */
    pos = This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD)
      + offset * This->sInfo.dwSampleSize;
    break;
  case FIND_SIZE:
    /* logical size */
    if (This->sInfo.dwSampleSize)
      pos = This->sInfo.dwSampleSize;
    else
      pos = 1;
    break;
  case FIND_INDEX:
    FIXME(": FIND_INDEX flag is not supported!\n");
    /* This is an index in the index-table on disc. */
    break;
  }; /* else logical position */

  return pos;
843 844
}

845 846
static HRESULT WINAPI IAVIStream_fnReadFormat(IAVIStream *iface, LONG pos, void *format,
    LONG *formatsize)
847
{
848
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
849

850
  TRACE("(%p,%d,%p,%p)\n", iface, pos, format, formatsize);
851 852 853 854 855 856 857 858 859 860 861 862

  if (formatsize == NULL)
    return AVIERR_BADPARAM;

  /* only interested in needed buffersize? */
  if (format == NULL || *formatsize <= 0) {
    *formatsize = This->cbFormat;

    return AVIERR_OK;
  }

  /* copy initial format (only as much as will fit) */
863 864
  memcpy(format, This->lpFormat, min(*(DWORD*)formatsize, This->cbFormat));
  if (*(DWORD*)formatsize < This->cbFormat) {
865 866 867 868 869 870 871 872 873 874 875
    *formatsize = This->cbFormat;
    return AVIERR_BUFFERTOOSMALL;
  }

  /* Could format change? When yes will it change? */
  if ((This->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
      pos > This->sInfo.dwStart) {
    LONG lLastFmt;

    lLastFmt = IAVIStream_fnFindSample(iface, pos, FIND_FORMAT|FIND_PREV);
    if (lLastFmt > 0) {
876
      FIXME(": need to read formatchange for %d -- unimplemented!\n",lLastFmt);
877 878 879 880 881
    }
  }

  *formatsize = This->cbFormat;
  return AVIERR_OK;
882 883
}

884 885
static HRESULT WINAPI IAVIStream_fnSetFormat(IAVIStream *iface, LONG pos, void *format,
    LONG formatsize)
886
{
887 888
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
  BITMAPINFOHEADER *lpbiNew = format;
889

890
  TRACE("(%p,%d,%p,%d)\n", iface, pos, format, formatsize);
891 892 893 894 895 896 897 898 899 900

  /* check parameters */
  if (format == NULL || formatsize <= 0)
    return AVIERR_BADPARAM;

  /* Do we have write permission? */
  if ((This->paf->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  /* can only set format before frame is written! */
901
  if (This->lLastFrame > pos)
902 903 904
    return AVIERR_UNSUPPORTED;

  /* initial format or a formatchange? */
905
  if (This->lpFormat == NULL) {
906 907
    /* initial format */
    if (This->paf->dwMoviChunkPos != 0)
Austin English's avatar
Austin English committed
908
      return AVIERR_ERROR; /* user has used API in wrong sequence! */
909

910
    This->lpFormat = HeapAlloc(GetProcessHeap(), 0, formatsize);
911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932
    if (This->lpFormat == NULL)
      return AVIERR_MEMORY;
    This->cbFormat = formatsize;

    memcpy(This->lpFormat, format, formatsize);

    /* update some infos about stream */
    if (This->sInfo.fccType == streamtypeVIDEO) {
      LONG lDim;

      lDim = This->sInfo.rcFrame.right - This->sInfo.rcFrame.left;
      if (lDim < lpbiNew->biWidth)
	This->sInfo.rcFrame.right = This->sInfo.rcFrame.left + lpbiNew->biWidth;
      lDim = This->sInfo.rcFrame.bottom - This->sInfo.rcFrame.top;
      if (lDim < lpbiNew->biHeight)
	This->sInfo.rcFrame.bottom = This->sInfo.rcFrame.top + lpbiNew->biHeight;
    } else if (This->sInfo.fccType == streamtypeAUDIO)
      This->sInfo.dwSampleSize = ((LPWAVEFORMATEX)This->lpFormat)->nBlockAlign;

    return AVIERR_OK;
  } else {
    MMCKINFO           ck;
933
    LPBITMAPINFOHEADER lpbiOld = This->lpFormat;
934 935
    RGBQUAD           *rgbNew  = (RGBQUAD*)((LPBYTE)lpbiNew + lpbiNew->biSize);
    AVIPALCHANGE      *lppc = NULL;
936
    UINT               n;
937

938
    /* perhaps format change, check it ... */
939 940 941
    if (This->cbFormat != formatsize)
      return AVIERR_UNSUPPORTED;

942
    /* no format change, only the initial one */
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960
    if (memcmp(This->lpFormat, format, formatsize) == 0)
      return AVIERR_OK;

    /* check that's only the palette, which changes */
    if (lpbiOld->biSize        != lpbiNew->biSize ||
	lpbiOld->biWidth       != lpbiNew->biWidth ||
	lpbiOld->biHeight      != lpbiNew->biHeight ||
	lpbiOld->biPlanes      != lpbiNew->biPlanes ||
	lpbiOld->biBitCount    != lpbiNew->biBitCount ||
	lpbiOld->biCompression != lpbiNew->biCompression ||
	lpbiOld->biClrUsed     != lpbiNew->biClrUsed)
      return AVIERR_UNSUPPORTED;

    This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;

    /* simply say all colors have changed */
    ck.ckid   = MAKEAVICKID(cktypePALchange, This->nStream);
    ck.cksize = 2 * sizeof(WORD) + lpbiOld->biClrUsed * sizeof(PALETTEENTRY);
961
    lppc = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
962 963 964 965 966 967 968 969 970 971 972 973 974
    if (lppc == NULL)
      return AVIERR_MEMORY;

    lppc->bFirstEntry = 0;
    lppc->bNumEntries = (lpbiOld->biClrUsed < 256 ? lpbiOld->biClrUsed : 0);
    lppc->wFlags      = 0;
    for (n = 0; n < lpbiOld->biClrUsed; n++) {
      lppc->peNew[n].peRed   = rgbNew[n].rgbRed;
      lppc->peNew[n].peGreen = rgbNew[n].rgbGreen;
      lppc->peNew[n].peBlue  = rgbNew[n].rgbBlue;
      lppc->peNew[n].peFlags = 0;
    }

975 976 977 978 979 980
    if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1 ||
        mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK ||
        mmioWrite(This->paf->hmmio, (HPSTR)lppc, ck.cksize) != ck.cksize ||
        mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
    {
      HeapFree(GetProcessHeap(), 0, lppc);
981
      return AVIERR_FILEWRITE;
982 983
    }

984 985
    This->paf->dwNextFramePos += ck.cksize + 2 * sizeof(DWORD);

986
    HeapFree(GetProcessHeap(), 0, lppc);
987 988 989

    return AVIFILE_AddFrame(This, cktypePALchange, n, ck.dwDataOffset, 0);
  }
990 991
}

992 993
static HRESULT WINAPI IAVIStream_fnRead(IAVIStream *iface, LONG start, LONG samples, void *buffer,
    LONG buffersize, LONG *bytesread, LONG *samplesread)
994
{
995 996 997
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
  DWORD size;
  HRESULT hr;
998

999
  TRACE("(%p,%d,%d,%p,%d,%p,%p)\n", iface, start, samples, buffer,
1000 1001 1002
 	buffersize, bytesread, samplesread);

  /* clear return parameters if given */
1003 1004 1005 1006 1007
  if (bytesread != NULL)
    *bytesread = 0;
  if (samplesread != NULL)
    *samplesread = 0;

1008
  /* check parameters */
1009
  if ((LONG)This->sInfo.dwStart > start)
1010
    return AVIERR_NODATA; /* couldn't read before start of stream */
1011
  if (This->sInfo.dwStart + This->sInfo.dwLength < (DWORD)start)
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
    return AVIERR_NODATA; /* start is past end of stream */

  /* should we read as much as possible? */
  if (samples == -1) {
    /* User should know how much we have read */
    if (bytesread == NULL && samplesread == NULL)
      return AVIERR_BADPARAM;

    if (This->sInfo.dwSampleSize != 0)
      samples = buffersize / This->sInfo.dwSampleSize;
    else
      samples = 1;
  }

  /* limit to end of stream */
1027
  if ((LONG)This->sInfo.dwLength < samples)
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037
    samples = This->sInfo.dwLength;
  if ((start - This->sInfo.dwStart) > (This->sInfo.dwLength - samples))
    samples = This->sInfo.dwLength - (start - This->sInfo.dwStart);

  /* nothing to read? Then leave ... */
  if (samples == 0)
    return AVIERR_OK;

  if (This->sInfo.dwSampleSize != 0) {
    /* fixed samplesize -- we can read over frame/block boundaries */
Mike McCormack's avatar
Mike McCormack committed
1038
    LONG block = start;
1039 1040
    LONG offset = 0;

1041 1042 1043 1044 1045 1046 1047 1048 1049
    if (!buffer)
    {
      if (bytesread)
        *bytesread = samples*This->sInfo.dwSampleSize;
      if (samplesread)
        *samplesread = samples;
      return AVIERR_OK;
    }

1050 1051 1052 1053 1054 1055 1056
    /* convert start sample to block,offset pair */
    AVIFILE_SamplesToBlock(This, &block, &offset);

    /* convert samples to bytes */
    samples *= This->sInfo.dwSampleSize;

    while (samples > 0 && buffersize > 0) {
1057
      LONG blocksize;
1058 1059 1060 1061 1062 1063 1064
      if (block != This->dwCurrentFrame) {
	hr = AVIFILE_ReadBlock(This, block, NULL, 0);
	if (FAILED(hr))
	  return hr;
      }

      size = min((DWORD)samples, (DWORD)buffersize);
1065 1066 1067
      blocksize = This->lpBuffer[1];
      TRACE("blocksize = %u\n",blocksize);
      size = min(size, blocksize - offset);
1068 1069 1070 1071
      memcpy(buffer, ((BYTE*)&This->lpBuffer[2]) + offset, size);

      block++;
      offset = 0;
1072
      buffer = ((LPBYTE)buffer)+size;
1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091
      samples    -= size;
      buffersize -= size;

      /* fill out return parameters if given */
      if (bytesread != NULL)
	*bytesread   += size;
      if (samplesread != NULL)
	*samplesread += size / This->sInfo.dwSampleSize;
    }

    if (samples == 0)
      return AVIERR_OK;
    else
      return AVIERR_BUFFERTOOSMALL;
  } else {
    /* variable samplesize -- we can only read one full frame/block */
    if (samples > 1)
      samples = 1;

1092
    assert(start <= This->lLastFrame);
1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
    size = This->idxFrames[start].dwChunkLength;
    if (buffer != NULL && buffersize >= size) {
      hr = AVIFILE_ReadBlock(This, start, buffer, size);
      if (FAILED(hr))
	return hr;
    } else if (buffer != NULL)
      return AVIERR_BUFFERTOOSMALL;

    /* fill out return parameters if given */
    if (bytesread != NULL)
      *bytesread = size;
    if (samplesread != NULL)
      *samplesread = samples;

    return AVIERR_OK;
  }
1109 1110
}

1111 1112
static HRESULT WINAPI IAVIStream_fnWrite(IAVIStream *iface, LONG start, LONG samples, void *buffer,
    LONG buffersize, DWORD flags, LONG *sampwritten, LONG *byteswritten)
1113
{
1114 1115
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
  FOURCC ckid;
1116 1117
  HRESULT hr;

1118
  TRACE("(%p,%d,%d,%p,%d,0x%08X,%p,%p)\n", iface, start, samples,
1119 1120
	buffer, buffersize, flags, sampwritten, byteswritten);

1121
  /* clear return parameters if given */
1122 1123 1124 1125 1126
  if (sampwritten != NULL)
    *sampwritten = 0;
  if (byteswritten != NULL)
    *byteswritten = 0;

1127 1128
  /* check parameters */
  if (buffer == NULL && (buffersize > 0 || samples > 0))
1129 1130
    return AVIERR_BADPARAM;

1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148
  /* Have we write permission? */
  if ((This->paf->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  switch (This->sInfo.fccType) {
  case streamtypeAUDIO:
    ckid = MAKEAVICKID(cktypeWAVEbytes, This->nStream);
    break;
  default:
    if ((flags & AVIIF_KEYFRAME) && buffersize != 0)
      ckid = MAKEAVICKID(cktypeDIBbits, This->nStream);
    else
      ckid = MAKEAVICKID(cktypeDIBcompressed, This->nStream);
    break;
  };

  /* append to end of stream? */
  if (start == -1) {
1149
    if (This->lLastFrame == -1)
1150 1151 1152
      start = This->sInfo.dwStart;
    else
      start = This->sInfo.dwLength;
1153 1154
  } else if (This->lLastFrame == -1)
    This->sInfo.dwStart = start;
1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165

  if (This->sInfo.dwSampleSize != 0) {
    /* fixed sample size -- audio like */
    if (samples * This->sInfo.dwSampleSize != buffersize)
      return AVIERR_BADPARAM;

    /* Couldn't skip audio-like data -- User must supply appropriate silence */
    if (This->sInfo.dwLength != start)
      return AVIERR_UNSUPPORTED;

    /* Convert position to frame/block */
1166
    start = This->lLastFrame + 1;
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176

    if ((This->paf->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) == 0) {
      FIXME(": not interleaved, could collect audio data!\n");
    }
  } else {
    /* variable sample size -- video like */
    if (samples > 1)
      return AVIERR_UNSUPPORTED;

    /* must we fill up with empty frames? */
1177
    if (This->lLastFrame != -1) {
1178 1179
      FOURCC ckid2 = MAKEAVICKID(cktypeDIBcompressed, This->nStream);

1180 1181
      while (start > This->lLastFrame + 1) {
	hr = AVIFILE_WriteBlock(This, This->lLastFrame + 1, ckid2, 0, NULL, 0);
1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198
	if (FAILED(hr))
	  return hr;
      }
    }
  }

  /* write the block now */
  hr = AVIFILE_WriteBlock(This, start, ckid, flags, buffer, buffersize);
  if (SUCCEEDED(hr)) {
    /* fill out return parameters if given */
    if (sampwritten != NULL)
      *sampwritten = samples;
    if (byteswritten != NULL)
      *byteswritten = buffersize;
  }

  return hr;
1199 1200
}

1201
static HRESULT WINAPI IAVIStream_fnDelete(IAVIStream *iface, LONG start, LONG samples)
1202
{
1203
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1204

1205
  FIXME("(%p,%d,%d): stub\n", iface, start, samples);
1206

1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236
  /* check parameters */
  if (start < 0 || samples < 0)
    return AVIERR_BADPARAM;

  /* Delete before start of stream? */
  if (start + samples < This->sInfo.dwStart)
    return AVIERR_OK;

  /* Delete after end of stream? */
  if (start > This->sInfo.dwLength)
    return AVIERR_OK;

  /* For the rest we need write permissions */
  if ((This->paf->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  /* 1. overwrite the data with JUNK
   *
   * if ISINTERLEAVED {
   *   2. concat all neighboured JUNK-blocks in this record to one
   *   3. if this record only contains JUNK and is at end set dwNextFramePos
   *      to start of this record, repeat this.
   * } else {
   *   2. concat all neighboured JUNK-blocks.
   *   3. if the JUNK block is at the end, then set dwNextFramePos to
   *      start of this block.
   * }
   */

  return AVIERR_UNSUPPORTED;
1237 1238
}

1239
static HRESULT WINAPI IAVIStream_fnReadData(IAVIStream *iface, DWORD fcc, void *lp, LONG *lpread)
1240
{
1241
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1242

1243
  TRACE("(%p,0x%08X,%p,%p)\n", iface, fcc, lp, lpread);
1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259

  if (fcc == ckidSTREAMHANDLERDATA) {
    if (This->lpHandlerData != NULL && This->cbHandlerData > 0) {
      if (lp == NULL || *lpread <= 0) {
	*lpread = This->cbHandlerData;
	return AVIERR_OK;
      }

      memcpy(lp, This->lpHandlerData, min(This->cbHandlerData, *lpread));
      if (*lpread < This->cbHandlerData)
	return AVIERR_BUFFERTOOSMALL;
      return AVIERR_OK;
    } else
      return AVIERR_NODATA;
  } else
    return ReadExtraChunk(&This->extra, fcc, lp, lpread);
1260 1261
}

1262
static HRESULT WINAPI IAVIStream_fnWriteData(IAVIStream *iface, DWORD fcc, void *lp, LONG size)
1263
{
1264
  IAVIStreamImpl *This = impl_from_IAVIStream(iface);
1265

1266
  TRACE("(%p,0x%08x,%p,%d)\n", iface, fcc, lp, size);
1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285

  /* check parameters */
  if (lp == NULL)
    return AVIERR_BADPARAM;
  if (size <= 0)
    return AVIERR_BADSIZE;

  /* need write permission */
  if ((This->paf->uMode & MMIO_RWMODE) == 0)
    return AVIERR_READONLY;

  /* already written something to this file? */
  if (This->paf->dwMoviChunkPos != 0) {
    /* the data will be inserted before the 'movi' chunk, so check for
     * enough space */
    DWORD dwPos = AVIFILE_ComputeMoviStart(This->paf);

    /* ckid,size => 2 * sizeof(DWORD) */
    dwPos += 2 * sizeof(DWORD) + size;
1286
    if (dwPos >= This->paf->dwMoviChunkPos - 2 * sizeof(DWORD))
1287 1288 1289 1290 1291 1292 1293
      return AVIERR_UNSUPPORTED; /* not enough space left */
  }

  This->paf->fDirty = TRUE;

  if (fcc == ckidSTREAMHANDLERDATA) {
    if (This->lpHandlerData != NULL) {
Andrey Gusev's avatar
Andrey Gusev committed
1294
      FIXME(": handler data already set -- overwrite?\n");
1295 1296 1297
      return AVIERR_UNSUPPORTED;
    }

1298
    This->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, size);
1299 1300 1301 1302 1303 1304 1305 1306
    if (This->lpHandlerData == NULL)
      return AVIERR_MEMORY;
    This->cbHandlerData = size;
    memcpy(This->lpHandlerData, lp, size);

    return AVIERR_OK;
  } else
    return WriteExtraChunk(&This->extra, fcc, lp, size);
1307 1308
}

1309
static HRESULT WINAPI IAVIStream_fnSetInfo(IAVIStream *iface, AVISTREAMINFOW *info, LONG infolen)
1310
{
1311
  FIXME("(%p,%p,%d): stub\n", iface, info, infolen);
1312 1313

  return E_FAIL;
1314
}
1315

1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332
static const struct IAVIStreamVtbl avist_vt = {
  IAVIStream_fnQueryInterface,
  IAVIStream_fnAddRef,
  IAVIStream_fnRelease,
  IAVIStream_fnCreate,
  IAVIStream_fnInfo,
  IAVIStream_fnFindSample,
  IAVIStream_fnReadFormat,
  IAVIStream_fnSetFormat,
  IAVIStream_fnRead,
  IAVIStream_fnWrite,
  IAVIStream_fnDelete,
  IAVIStream_fnReadData,
  IAVIStream_fnWriteData,
  IAVIStream_fnSetInfo
};

1333 1334 1335

static HRESULT AVIFILE_AddFrame(IAVIStreamImpl *This, DWORD ckid, DWORD size, DWORD offset, DWORD flags)
{
1336 1337
  UINT n;

1338 1339 1340
  /* pre-conditions */
  assert(This != NULL);

1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355
  switch (TWOCCFromFOURCC(ckid)) {
  case cktypeDIBbits:
    if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
      flags |= AVIIF_KEYFRAME;
    break;
  case cktypeDIBcompressed:
    if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
      flags &= ~AVIIF_KEYFRAME;
    break;
  case cktypePALchange:
    if (This->sInfo.fccType != streamtypeVIDEO) {
      ERR(": found palette change in non-video stream!\n");
      return AVIERR_BADFORMAT;
    }

1356
    if (This->idxFmtChanges == NULL || This->nIdxFmtChanges <= This->sInfo.dwFormatChangeCount) {
1357 1358 1359 1360
      DWORD new_count = This->nIdxFmtChanges + 16;
      void *new_buffer;

      if (This->idxFmtChanges == NULL) {
1361
	This->idxFmtChanges =
1362 1363 1364 1365 1366 1367 1368 1369 1370
          HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count * sizeof(AVIINDEXENTRY));
        if (!This->idxFmtChanges) return AVIERR_MEMORY;
      } else {
        new_buffer = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFmtChanges,
                new_count * sizeof(AVIINDEXENTRY));
        if (!new_buffer) return AVIERR_MEMORY;
        This->idxFmtChanges = new_buffer;
      }
      This->nIdxFmtChanges = new_count;
1371
    }
1372

1373 1374
    This->sInfo.dwFlags |= AVISTREAMINFO_FORMATCHANGES;
    n = ++This->sInfo.dwFormatChangeCount;
1375 1376 1377 1378
    This->idxFmtChanges[n].ckid          = This->lLastFrame;
    This->idxFmtChanges[n].dwFlags       = 0;
    This->idxFmtChanges[n].dwChunkOffset = offset;
    This->idxFmtChanges[n].dwChunkLength = size;
1379

1380
    return AVIERR_OK;
1381 1382 1383 1384 1385 1386 1387 1388 1389
  case cktypeWAVEbytes:
    if (This->paf->fInfo.dwFlags & AVIFILEINFO_TRUSTCKTYPE)
      flags |= AVIIF_KEYFRAME;
    break;
  default:
    WARN(": unknown TWOCC 0x%04X found\n", TWOCCFromFOURCC(ckid));
    break;
  };

Austin English's avatar
Austin English committed
1390
  /* first frame is always a keyframe */
1391
  if (This->lLastFrame == -1)
1392 1393 1394 1395 1396 1397
    flags |= AVIIF_KEYFRAME;

  if (This->sInfo.dwSuggestedBufferSize < size)
    This->sInfo.dwSuggestedBufferSize = size;

  /* get memory for index */
1398
  if (This->idxFrames == NULL || This->lLastFrame + 1 >= This->nIdxFrames) {
1399
    This->nIdxFrames += 512;
1400
    if (This->idxFrames == NULL)
1401
      This->idxFrames = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->nIdxFrames * sizeof(AVIINDEXENTRY));
1402
      else
1403
	This->idxFrames = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxFrames,
1404
			   This->nIdxFrames * sizeof(AVIINDEXENTRY));
1405 1406 1407 1408
    if (This->idxFrames == NULL)
      return AVIERR_MEMORY;
  }

1409 1410 1411 1412 1413 1414
  This->lLastFrame++;
  This->idxFrames[This->lLastFrame].ckid          = ckid;
  This->idxFrames[This->lLastFrame].dwFlags       = flags;
  This->idxFrames[This->lLastFrame].dwChunkOffset = offset;
  This->idxFrames[This->lLastFrame].dwChunkLength = size;

1415
  /* update AVISTREAMINFO structure if necessary */
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426
  if (This->sInfo.dwLength <= This->lLastFrame)
    This->sInfo.dwLength = This->lLastFrame + 1;

  return AVIERR_OK;
}

static HRESULT AVIFILE_AddRecord(IAVIFileImpl *This)
{
  /* pre-conditions */
  assert(This != NULL && This->ppStreams[0] != NULL);

1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439
  if (This->idxRecords == NULL || This->cbIdxRecords / sizeof(AVIINDEXENTRY) <= This->nIdxRecords) {
    DWORD new_count = This->cbIdxRecords + 1024 * sizeof(AVIINDEXENTRY);
    void *mem;
    if (!This->idxRecords)
      mem = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, new_count);
    else
      mem = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->idxRecords, new_count);
    if (mem) {
      This->cbIdxRecords = new_count;
      This->idxRecords = mem;
    } else {
      HeapFree(GetProcessHeap(), 0, This->idxRecords);
      This->idxRecords = NULL;
1440
      return AVIERR_MEMORY;
1441
    }
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
  }

  assert(This->nIdxRecords < This->cbIdxRecords/sizeof(AVIINDEXENTRY));

  This->idxRecords[This->nIdxRecords].ckid          = listtypeAVIRECORD;
  This->idxRecords[This->nIdxRecords].dwFlags       = AVIIF_LIST;
  This->idxRecords[This->nIdxRecords].dwChunkOffset =
    This->ckLastRecord.dwDataOffset - 2 * sizeof(DWORD);
  This->idxRecords[This->nIdxRecords].dwChunkLength =
    This->ckLastRecord.cksize;
  This->nIdxRecords++;
1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469

  return AVIERR_OK;
}

static DWORD   AVIFILE_ComputeMoviStart(IAVIFileImpl *This)
{
  DWORD dwPos;
  DWORD nStream;

  /* RIFF,hdrl,movi,avih => (3 * 3 + 2) * sizeof(DWORD) = 11 * sizeof(DWORD) */
  dwPos = 11 * sizeof(DWORD) + sizeof(MainAVIHeader);

  for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
    IAVIStreamImpl *pStream = This->ppStreams[nStream];

    /* strl,strh,strf => (3 + 2 * 2) * sizeof(DWORD) = 7 * sizeof(DWORD) */
    dwPos += 7 * sizeof(DWORD) + sizeof(AVIStreamHeader);
1470
    dwPos += ((pStream->cbFormat + 1) & ~1U);
1471
    if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0)
1472
      dwPos += 2 * sizeof(DWORD) + ((pStream->cbHandlerData + 1) & ~1U);
1473
    if (pStream->sInfo.szName[0])
1474
      dwPos += 2 * sizeof(DWORD) + ((lstrlenW(pStream->sInfo.szName) + 1) & ~1U);
1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489
  }

  if (This->dwMoviChunkPos == 0) {
    This->dwNextFramePos = dwPos;

    /* pad to multiple of AVI_HEADERSIZE only if we are more than 8 bytes away from it */
    if (((dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1)) - dwPos > 2 * sizeof(DWORD))
      This->dwNextFramePos = (dwPos + AVI_HEADERSIZE) & ~(AVI_HEADERSIZE - 1);

    This->dwMoviChunkPos = This->dwNextFramePos - sizeof(DWORD);
  }

  return dwPos;
}

1490
static void AVIFILE_ConstructAVIStream(IAVIFileImpl *paf, DWORD nr, const AVISTREAMINFOW *asi)
1491 1492 1493 1494 1495 1496 1497 1498 1499 1500
{
  IAVIStreamImpl *pstream;

  /* pre-conditions */
  assert(paf != NULL);
  assert(nr < MAX_AVISTREAMS);
  assert(paf->ppStreams[nr] != NULL);

  pstream = paf->ppStreams[nr];

1501
  pstream->IAVIStream_iface.lpVtbl = &avist_vt;
1502 1503 1504
  pstream->ref            = 0;
  pstream->paf            = paf;
  pstream->nStream        = nr;
1505
  pstream->dwCurrentFrame = (DWORD)-1;
1506
  pstream->lLastFrame    = -1;
1507 1508 1509 1510 1511 1512 1513

  if (asi != NULL) {
    memcpy(&pstream->sInfo, asi, sizeof(pstream->sInfo));

    if (asi->dwLength > 0) {
      /* pre-allocate mem for frame-index structure */
      pstream->idxFrames =
1514
	HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwLength * sizeof(AVIINDEXENTRY));
1515 1516 1517 1518 1519 1520
      if (pstream->idxFrames != NULL)
	pstream->nIdxFrames = asi->dwLength;
    }
    if (asi->dwFormatChangeCount > 0) {
      /* pre-allocate mem for formatchange-index structure */
      pstream->idxFmtChanges =
1521
	HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, asi->dwFormatChangeCount * sizeof(AVIINDEXENTRY));
1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542
      if (pstream->idxFmtChanges != NULL)
	pstream->nIdxFmtChanges = asi->dwFormatChangeCount;
    }

    /* These values will be computed */
    pstream->sInfo.dwLength              = 0;
    pstream->sInfo.dwSuggestedBufferSize = 0;
    pstream->sInfo.dwFormatChangeCount   = 0;
    pstream->sInfo.dwEditCount           = 1;
    if (pstream->sInfo.dwSampleSize > 0)
      SetRectEmpty(&pstream->sInfo.rcFrame);
  }

  pstream->sInfo.dwCaps = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
}

static void    AVIFILE_DestructAVIStream(IAVIStreamImpl *This)
{
  /* pre-conditions */
  assert(This != NULL);

1543
  This->dwCurrentFrame = (DWORD)-1;
1544
  This->lLastFrame    = -1;
1545 1546
  This->paf = NULL;
  if (This->idxFrames != NULL) {
1547
    HeapFree(GetProcessHeap(), 0, This->idxFrames);
1548 1549 1550
    This->idxFrames  = NULL;
    This->nIdxFrames = 0;
  }
1551 1552
  HeapFree(GetProcessHeap(), 0, This->idxFmtChanges);
  This->idxFmtChanges = NULL;
1553
  if (This->lpBuffer != NULL) {
1554
    HeapFree(GetProcessHeap(), 0, This->lpBuffer);
1555 1556 1557 1558
    This->lpBuffer = NULL;
    This->cbBuffer = 0;
  }
  if (This->lpHandlerData != NULL) {
1559
    HeapFree(GetProcessHeap(), 0, This->lpHandlerData);
1560 1561 1562 1563
    This->lpHandlerData = NULL;
    This->cbHandlerData = 0;
  }
  if (This->extra.lp != NULL) {
1564
    HeapFree(GetProcessHeap(), 0, This->extra.lp);
1565 1566 1567 1568
    This->extra.lp = NULL;
    This->extra.cb = 0;
  }
  if (This->lpFormat != NULL) {
1569
    HeapFree(GetProcessHeap(), 0, This->lpFormat);
1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585
    This->lpFormat = NULL;
    This->cbFormat = 0;
  }
}

static HRESULT AVIFILE_LoadFile(IAVIFileImpl *This)
{
  MainAVIHeader   MainAVIHdr;
  MMCKINFO        ckRIFF;
  MMCKINFO        ckLIST1;
  MMCKINFO        ckLIST2;
  MMCKINFO        ck;
  IAVIStreamImpl *pStream;
  DWORD           nStream;
  HRESULT         hr;

1586
  if (This->hmmio == NULL)
1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611
    return AVIERR_FILEOPEN;

  /* initialize stream ptr's */
  memset(This->ppStreams, 0, sizeof(This->ppStreams));

  /* try to get "RIFF" chunk -- must not be at beginning of file! */
  ckRIFF.fccType = formtypeAVI;
  if (mmioDescend(This->hmmio, &ckRIFF, NULL, MMIO_FINDRIFF) != S_OK) {
    ERR(": not an AVI!\n");
    return AVIERR_FILEREAD;
  }

  /* get "LIST" "hdrl" */
  ckLIST1.fccType = listtypeAVIHEADER;
  hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF, MMIO_FINDLIST);
  if (FAILED(hr))
    return hr;

  /* get "avih" chunk */
  ck.ckid = ckidAVIMAINHDR;
  hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, MMIO_FINDCHUNK);
  if (FAILED(hr))
    return hr;

  if (ck.cksize != sizeof(MainAVIHdr)) {
1612
    ERR(": invalid size of %d for MainAVIHeader!\n", ck.cksize);
1613 1614 1615 1616 1617
    return AVIERR_BADFORMAT;
  }
  if (mmioRead(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
    return AVIERR_FILEREAD;

1618 1619
  /* check for MAX_AVISTREAMS limit */
  if (MainAVIHdr.dwStreams > MAX_AVISTREAMS) {
1620
    WARN("file contains %u streams, but only supports %d -- change MAX_AVISTREAMS!\n", MainAVIHdr.dwStreams, MAX_AVISTREAMS);
1621 1622 1623
    return AVIERR_UNSUPPORTED;
  }

1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638
  /* adjust permissions if copyrighted material in file */
  if (MainAVIHdr.dwFlags & AVIFILEINFO_COPYRIGHTED) {
    This->uMode &= ~MMIO_RWMODE;
    This->uMode |= MMIO_READ;
  }

  /* convert MainAVIHeader into AVIFILINFOW */
  memset(&This->fInfo, 0, sizeof(This->fInfo));
  This->fInfo.dwRate                = MainAVIHdr.dwMicroSecPerFrame;
  This->fInfo.dwScale               = 1000000;
  This->fInfo.dwMaxBytesPerSec      = MainAVIHdr.dwMaxBytesPerSec;
  This->fInfo.dwFlags               = MainAVIHdr.dwFlags;
  This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
  This->fInfo.dwLength              = MainAVIHdr.dwTotalFrames;
  This->fInfo.dwStreams             = MainAVIHdr.dwStreams;
1639
  This->fInfo.dwSuggestedBufferSize = 0;
1640 1641 1642
  This->fInfo.dwWidth               = MainAVIHdr.dwWidth;
  This->fInfo.dwHeight              = MainAVIHdr.dwHeight;
  LoadStringW(AVIFILE_hModule, IDS_AVIFILETYPE, This->fInfo.szFileType,
1643
	      sizeof(This->fInfo.szFileType)/sizeof(This->fInfo.szFileType[0]));
1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658

  /* go back to into header list */
  if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEREAD;

  /* foreach stream exists a "LIST","strl" chunk */
  for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
    /* get next nested chunk in this "LIST","strl" */
    if (mmioDescend(This->hmmio, &ckLIST2, &ckLIST1, 0) != S_OK)
      return AVIERR_FILEREAD;

    /* nested chunk must be of type "LIST","strl" -- when not normally JUNK */
    if (ckLIST2.ckid == FOURCC_LIST &&
	ckLIST2.fccType == listtypeSTREAMHEADER) {
      pStream = This->ppStreams[nStream] =
1659
	HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IAVIStreamImpl));
1660 1661 1662 1663 1664 1665 1666 1667 1668 1669
      if (pStream == NULL)
	return AVIERR_MEMORY;
      AVIFILE_ConstructAVIStream(This, nStream, NULL);

      ck.ckid = 0;
      while (mmioDescend(This->hmmio, &ck, &ckLIST2, 0) == S_OK) {
	switch (ck.ckid) {
	case ckidSTREAMHANDLERDATA:
	  if (pStream->lpHandlerData != NULL)
	    return AVIERR_BADFORMAT;
1670
	  pStream->lpHandlerData = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1671 1672 1673 1674
	  if (pStream->lpHandlerData == NULL)
	    return AVIERR_MEMORY;
	  pStream->cbHandlerData = ck.cksize;

1675
          if (mmioRead(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
1676 1677 1678 1679 1680
	    return AVIERR_FILEREAD;
	  break;
	case ckidSTREAMFORMAT:
	  if (pStream->lpFormat != NULL)
	    return AVIERR_BADFORMAT;
1681 1682 1683
          if (ck.cksize == 0)
            break;

1684
	  pStream->lpFormat = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1685 1686 1687 1688
	  if (pStream->lpFormat == NULL)
	    return AVIERR_MEMORY;
	  pStream->cbFormat = ck.cksize;

1689
          if (mmioRead(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
1690 1691 1692
	    return AVIERR_FILEREAD;

	  if (pStream->sInfo.fccType == streamtypeVIDEO) {
1693
            LPBITMAPINFOHEADER lpbi = pStream->lpFormat;
1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717

	    /* some corrections to the video format */
	    if (lpbi->biClrUsed == 0 && lpbi->biBitCount <= 8)
	      lpbi->biClrUsed = 1u << lpbi->biBitCount;
	    if (lpbi->biCompression == BI_RGB && lpbi->biSizeImage == 0)
	      lpbi->biSizeImage = DIBWIDTHBYTES(*lpbi) * lpbi->biHeight;
	    if (lpbi->biCompression != BI_RGB && lpbi->biBitCount == 8) {
	      if (pStream->sInfo.fccHandler == mmioFOURCC('R','L','E','0') ||
		  pStream->sInfo.fccHandler == mmioFOURCC('R','L','E',' '))
		lpbi->biCompression = BI_RLE8;
	    }
	    if (lpbi->biCompression == BI_RGB &&
		(pStream->sInfo.fccHandler == 0 ||
		 pStream->sInfo.fccHandler == mmioFOURCC('N','O','N','E')))
	      pStream->sInfo.fccHandler = comptypeDIB;

	    /* init rcFrame if it's empty */
	    if (IsRectEmpty(&pStream->sInfo.rcFrame))
	      SetRect(&pStream->sInfo.rcFrame, 0, 0, lpbi->biWidth, lpbi->biHeight);
	  }
	  break;
	case ckidSTREAMHEADER:
	  {
	    static const WCHAR streamTypeFmt[] = {'%','4','.','4','h','s',0};
1718
	    static const WCHAR streamNameFmt[] = {'%','s',' ','%','s',' ','#','%','d',0};
1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740

	    AVIStreamHeader streamHdr;
	    WCHAR           szType[25];
	    UINT            count;
	    LONG            n = ck.cksize;

	    if (ck.cksize > sizeof(streamHdr))
	      n = sizeof(streamHdr);

	    if (mmioRead(This->hmmio, (HPSTR)&streamHdr, n) != n)
	      return AVIERR_FILEREAD;

	    pStream->sInfo.fccType               = streamHdr.fccType;
	    pStream->sInfo.fccHandler            = streamHdr.fccHandler;
	    pStream->sInfo.dwFlags               = streamHdr.dwFlags;
	    pStream->sInfo.wPriority             = streamHdr.wPriority;
	    pStream->sInfo.wLanguage             = streamHdr.wLanguage;
	    pStream->sInfo.dwInitialFrames       = streamHdr.dwInitialFrames;
	    pStream->sInfo.dwScale               = streamHdr.dwScale;
	    pStream->sInfo.dwRate                = streamHdr.dwRate;
	    pStream->sInfo.dwStart               = streamHdr.dwStart;
	    pStream->sInfo.dwLength              = streamHdr.dwLength;
1741
	    pStream->sInfo.dwSuggestedBufferSize = 0;
1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752
	    pStream->sInfo.dwQuality             = streamHdr.dwQuality;
	    pStream->sInfo.dwSampleSize          = streamHdr.dwSampleSize;
	    pStream->sInfo.rcFrame.left          = streamHdr.rcFrame.left;
	    pStream->sInfo.rcFrame.top           = streamHdr.rcFrame.top;
	    pStream->sInfo.rcFrame.right         = streamHdr.rcFrame.right;
	    pStream->sInfo.rcFrame.bottom        = streamHdr.rcFrame.bottom;
	    pStream->sInfo.dwEditCount           = 0;
	    pStream->sInfo.dwFormatChangeCount   = 0;

	    /* generate description for stream like "filename.avi Type #n" */
	    if (streamHdr.fccType == streamtypeVIDEO)
1753
	      LoadStringW(AVIFILE_hModule, IDS_VIDEO, szType, sizeof(szType)/sizeof(szType[0]));
1754
	    else if (streamHdr.fccType == streamtypeAUDIO)
1755
	      LoadStringW(AVIFILE_hModule, IDS_AUDIO, szType, sizeof(szType)/sizeof(szType[0]));
1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774
	    else
	      wsprintfW(szType, streamTypeFmt, (char*)&streamHdr.fccType);

	    /* get count of this streamtype up to this stream */
	    count = 0;
	    for (n = nStream; 0 <= n; n--) {
	      if (This->ppStreams[n]->sInfo.fccHandler == streamHdr.fccType)
		count++;
	    }

	    memset(pStream->sInfo.szName, 0, sizeof(pStream->sInfo.szName));

	    /* FIXME: avoid overflow -- better use wsnprintfW, which doesn't exists ! */
	    wsprintfW(pStream->sInfo.szName, streamNameFmt,
		      AVIFILE_BasenameW(This->szFileName), szType, count);
	  }
	  break;
	case ckidSTREAMNAME:
	  { /* streamname will be saved as ASCII string */
1775
	    LPSTR str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
1776 1777 1778
	    if (str == NULL)
	      return AVIERR_MEMORY;

1779
	    if (mmioRead(This->hmmio, str, ck.cksize) != ck.cksize)
1780
	    {
1781
	      HeapFree(GetProcessHeap(), 0, str);
1782
	      return AVIERR_FILEREAD;
1783
	    }
1784 1785 1786 1787

	    MultiByteToWideChar(CP_ACP, 0, str, -1, pStream->sInfo.szName,
				sizeof(pStream->sInfo.szName)/sizeof(pStream->sInfo.szName[0]));

1788
	    HeapFree(GetProcessHeap(), 0, str);
1789 1790 1791 1792 1793 1794
	  }
	  break;
	case ckidAVIPADDING:
	case mmioFOURCC('p','a','d','d'):
	  break;
	default:
1795
          WARN(": found extra chunk 0x%08X\n", ck.ckid);
1796 1797 1798 1799
	  hr = ReadChunkIntoExtra(&pStream->extra, This->hmmio, &ck);
	  if (FAILED(hr))
	    return hr;
	};
1800 1801 1802 1803 1804 1805 1806 1807
	if (pStream->lpFormat != NULL && pStream->sInfo.fccType == streamtypeAUDIO)
	{
	  WAVEFORMATEX *wfx = pStream->lpFormat;          /* wfx->nBlockAlign = wfx->nChannels * wfx->wBitsPerSample / 8; could be added */
	  pStream->sInfo.dwSampleSize = wfx->nBlockAlign; /* to deal with corrupt wfx->nBlockAlign but Windows doesn't do this */
	  TRACE("Block size reset to %u, chan=%u bpp=%u\n", wfx->nBlockAlign, wfx->nChannels, wfx->wBitsPerSample);
	  pStream->sInfo.dwScale = 1;
	  pStream->sInfo.dwRate = wfx->nSamplesPerSec;
	}
1808 1809 1810 1811 1812 1813 1814 1815 1816
	if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
	  return AVIERR_FILEREAD;
      }
    } else {
      /* nested chunks in "LIST","hdrl" which are not of type "LIST","strl" */
      hr = ReadChunkIntoExtra(&This->fileextra, This->hmmio, &ckLIST2);
      if (FAILED(hr))
	return hr;
    }
1817 1818
    if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
      return AVIERR_FILEREAD;
1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832
  }

  /* read any extra headers in "LIST","hdrl" */
  FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckLIST1, 0);
  if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
    return AVIERR_FILEREAD;

  /* search "LIST","movi" chunk in "RIFF","AVI " */
  ckLIST1.fccType = listtypeAVIMOVIE;
  hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ckLIST1, &ckRIFF,
			      MMIO_FINDLIST);
  if (FAILED(hr))
    return hr;

1833
  This->dwMoviChunkPos = ckLIST1.dwDataOffset;
1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850
  This->dwIdxChunkPos  = ckLIST1.cksize + ckLIST1.dwDataOffset;
  if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
    return AVIERR_FILEREAD;

  /* try to find an index */
  ck.ckid = ckidAVINEWINDEX;
  hr = FindChunkAndKeepExtras(&This->fileextra, This->hmmio,
			      &ck, &ckRIFF, MMIO_FINDCHUNK);
  if (SUCCEEDED(hr) && ck.cksize > 0) {
    if (FAILED(AVIFILE_LoadIndex(This, ck.cksize, ckLIST1.dwDataOffset)))
      This->fInfo.dwFlags &= ~AVIFILEINFO_HASINDEX;
  }

  /* when we haven't found an index or it's bad, then build one
   * by parsing 'movi' chunk */
  if ((This->fInfo.dwFlags & AVIFILEINFO_HASINDEX) == 0) {
    for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
1851
      This->ppStreams[nStream]->lLastFrame = -1;
1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863

    if (mmioSeek(This->hmmio, ckLIST1.dwDataOffset + sizeof(DWORD), SEEK_SET) == -1) {
      ERR(": Oops, can't seek back to 'movi' chunk!\n");
      return AVIERR_FILEREAD;
    }

    /* seek through the 'movi' list until end */
    while (mmioDescend(This->hmmio, &ck, &ckLIST1, 0) == S_OK) {
      if (ck.ckid != FOURCC_LIST) {
	if (mmioAscend(This->hmmio, &ck, 0) == S_OK) {
	  nStream = StreamFromFOURCC(ck.ckid);

1864 1865 1866
	  if (nStream > This->fInfo.dwStreams)
	    return AVIERR_BADFORMAT;

1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887
	  AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
			   ck.dwDataOffset - 2 * sizeof(DWORD), 0);
	} else {
	  nStream = StreamFromFOURCC(ck.ckid);
	  WARN(": file seems to be truncated!\n");
	  if (nStream <= This->fInfo.dwStreams &&
	      This->ppStreams[nStream]->sInfo.dwSampleSize > 0) {
	    ck.cksize = mmioSeek(This->hmmio, 0, SEEK_END);
	    if (ck.cksize != -1) {
	      ck.cksize -= ck.dwDataOffset;
	      ck.cksize &= ~(This->ppStreams[nStream]->sInfo.dwSampleSize - 1);

	      AVIFILE_AddFrame(This->ppStreams[nStream], ck.ckid, ck.cksize,
			       ck.dwDataOffset - 2 * sizeof(DWORD), 0);
	    }
	  }
	}
      }
    }
  }

1888 1889 1890 1891 1892 1893 1894
  for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++)
  {
    DWORD sugbuf =  This->ppStreams[nStream]->sInfo.dwSuggestedBufferSize;
    if (This->fInfo.dwSuggestedBufferSize < sugbuf)
      This->fInfo.dwSuggestedBufferSize = sugbuf;
  }

1895 1896 1897 1898 1899 1900
  /* find other chunks */
  FindChunkAndKeepExtras(&This->fileextra, This->hmmio, &ck, &ckRIFF, 0);

  return AVIERR_OK;
}

1901
static HRESULT AVIFILE_LoadIndex(const IAVIFileImpl *This, DWORD size, DWORD offset)
1902 1903 1904 1905 1906 1907
{
  AVIINDEXENTRY *lp;
  DWORD          pos, n;
  HRESULT        hr = AVIERR_OK;
  BOOL           bAbsolute = TRUE;

1908
  lp = HeapAlloc(GetProcessHeap(), 0, IDX_PER_BLOCK * sizeof(AVIINDEXENTRY));
1909 1910 1911 1912 1913 1914 1915
  if (lp == NULL)
    return AVIERR_MEMORY;

  /* adjust limits for index tables, so that inserting will be faster */
  for (n = 0; n < This->fInfo.dwStreams; n++) {
    IAVIStreamImpl *pStream = This->ppStreams[n];

1916
    pStream->lLastFrame = -1;
1917 1918

    if (pStream->idxFrames != NULL) {
1919
      HeapFree(GetProcessHeap(), 0, pStream->idxFrames);
1920 1921 1922 1923 1924 1925
      pStream->idxFrames  = NULL;
      pStream->nIdxFrames = 0;
    }

    if (pStream->sInfo.dwSampleSize != 0) {
      if (n > 0 && This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
1926
	pStream->nIdxFrames = This->ppStreams[0]->nIdxFrames;
1927 1928 1929 1930 1931 1932 1933 1934
      } else if (pStream->sInfo.dwSuggestedBufferSize) {
	pStream->nIdxFrames =
	  pStream->sInfo.dwLength / pStream->sInfo.dwSuggestedBufferSize;
      }
    } else
      pStream->nIdxFrames = pStream->sInfo.dwLength;

    pStream->idxFrames =
1935
      HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, pStream->nIdxFrames * sizeof(AVIINDEXENTRY));
1936
    if (pStream->idxFrames == NULL && pStream->nIdxFrames > 0) {
1937
      pStream->nIdxFrames = 0;
1938
      HeapFree(GetProcessHeap(), 0, lp);
1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959
      return AVIERR_MEMORY;
    }
  }

  pos = (DWORD)-1;
  while (size != 0) {
    LONG read = min(IDX_PER_BLOCK * sizeof(AVIINDEXENTRY), size);

    if (mmioRead(This->hmmio, (HPSTR)lp, read) != read) {
      hr = AVIERR_FILEREAD;
      break;
    }
    size -= read;

    if (pos == (DWORD)-1)
      pos = offset - lp->dwChunkOffset + sizeof(DWORD);

    AVIFILE_ParseIndex(This, lp, read / sizeof(AVIINDEXENTRY),
		       pos, &bAbsolute);
  }

1960
  HeapFree(GetProcessHeap(), 0, lp);
1961

1962 1963 1964 1965
  /* checking ... */
  for (n = 0; n < This->fInfo.dwStreams; n++) {
    IAVIStreamImpl *pStream = This->ppStreams[n];

1966 1967
    if (pStream->sInfo.dwSampleSize == 0 &&
	pStream->sInfo.dwLength != pStream->lLastFrame+1)
1968
      ERR("stream %u length mismatch: dwLength=%u found=%d\n",
1969 1970 1971
	   n, pStream->sInfo.dwLength, pStream->lLastFrame);
  }

1972 1973 1974
  return hr;
}

1975
static HRESULT AVIFILE_ParseIndex(const IAVIFileImpl *This, AVIINDEXENTRY *lp,
1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989
				  LONG count, DWORD pos, BOOL *bAbsolute)
{
  if (lp == NULL)
    return AVIERR_BADPARAM;

  for (; count > 0; count--, lp++) {
    WORD nStream = StreamFromFOURCC(lp->ckid);

    if (lp->ckid == listtypeAVIRECORD || nStream == 0x7F)
      continue; /* skip these */

    if (nStream > This->fInfo.dwStreams)
      return AVIERR_BADFORMAT;

1990 1991 1992 1993 1994 1995
    /* Video frames can be either indexed in a relative position to the
     * "movi" chunk or in a absolute position in the file. If the index
     * is relative the frame offset will always be so small that it will
     * virtually never reach the "movi" offset so we can detect if the
     * video is relative very fast.
     */
1996
    if (*bAbsolute && lp->dwChunkOffset < This->dwMoviChunkPos)
1997 1998
      *bAbsolute = FALSE;

1999 2000
    if (!*bAbsolute)
      lp->dwChunkOffset += pos; /* make the offset absolute */
2001 2002 2003 2004 2005 2006 2007 2008 2009

    if (FAILED(AVIFILE_AddFrame(This->ppStreams[nStream], lp->ckid, lp->dwChunkLength, lp->dwChunkOffset, lp->dwFlags)))
      return AVIERR_MEMORY;
  }

  return AVIERR_OK;
}

static HRESULT AVIFILE_ReadBlock(IAVIStreamImpl *This, DWORD pos,
2010
				 LPVOID buffer, DWORD size)
2011 2012 2013 2014
{
  /* pre-conditions */
  assert(This != NULL);
  assert(This->paf != NULL);
2015
  assert(This->paf->hmmio != NULL);
2016
  assert(This->sInfo.dwStart <= pos && pos < This->sInfo.dwLength);
2017
  assert(pos <= This->lLastFrame);
2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028

  /* should we read as much as block gives us? */
  if (size == 0 || size > This->idxFrames[pos].dwChunkLength)
    size = This->idxFrames[pos].dwChunkLength;

  /* read into out own buffer or given one? */
  if (buffer == NULL) {
    /* we also read the chunk */
    size += 2 * sizeof(DWORD);

    /* check that buffer is big enough -- don't trust dwSuggestedBufferSize */
2029
    if (This->lpBuffer == NULL || This->cbBuffer < size) {
2030 2031
      DWORD maxSize = max(size, This->sInfo.dwSuggestedBufferSize);

2032
      if (This->lpBuffer == NULL) {
2033
	This->lpBuffer = HeapAlloc(GetProcessHeap(), 0, maxSize);
2034 2035 2036 2037 2038 2039
        if (!This->lpBuffer) return AVIERR_MEMORY;
      } else {
        void *new_buffer = HeapReAlloc(GetProcessHeap(), 0, This->lpBuffer, maxSize);
        if (!new_buffer) return AVIERR_MEMORY;
        This->lpBuffer = new_buffer;
      }
2040
      This->cbBuffer = maxSize;
2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051
    }

    /* now read the complete chunk into our buffer */
    if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset, SEEK_SET) == -1)
      return AVIERR_FILEREAD;
    if (mmioRead(This->paf->hmmio, (HPSTR)This->lpBuffer, size) != size)
      return AVIERR_FILEREAD;

    /* check if it was the correct block which we have read */
    if (This->lpBuffer[0] != This->idxFrames[pos].ckid ||
	This->lpBuffer[1] != This->idxFrames[pos].dwChunkLength) {
2052 2053
      ERR(": block %d not found at 0x%08X\n", pos, This->idxFrames[pos].dwChunkOffset);
      ERR(": Index says: '%4.4s'(0x%08X) size 0x%08X\n",
2054 2055
	  (char*)&This->idxFrames[pos].ckid, This->idxFrames[pos].ckid,
	  This->idxFrames[pos].dwChunkLength);
2056
      ERR(": Data  says: '%4.4s'(0x%08X) size 0x%08X\n",
2057 2058 2059 2060 2061 2062
	  (char*)&This->lpBuffer[0], This->lpBuffer[0], This->lpBuffer[1]);
      return AVIERR_FILEREAD;
    }
  } else {
    if (mmioSeek(This->paf->hmmio, This->idxFrames[pos].dwChunkOffset + 2 * sizeof(DWORD), SEEK_SET) == -1)
      return AVIERR_FILEREAD;
2063
    if (mmioRead(This->paf->hmmio, buffer, size) != size)
2064 2065 2066 2067 2068 2069
      return AVIERR_FILEREAD;
  }

  return AVIERR_OK;
}

2070
static void AVIFILE_SamplesToBlock(const IAVIStreamImpl *This, LPLONG pos, LPLONG offset)
2071
{
2072
  LONG block;
2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085

  /* pre-conditions */
  assert(This != NULL);
  assert(pos != NULL);
  assert(offset != NULL);
  assert(This->sInfo.dwSampleSize != 0);
  assert(*pos >= This->sInfo.dwStart);

  /* convert start sample to start bytes */
  (*offset)  = (*pos) - This->sInfo.dwStart;
  (*offset) *= This->sInfo.dwSampleSize;

  /* convert bytes to block number */
2086
  for (block = 0; block <= This->lLastFrame; block++) {
2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111
    if (This->idxFrames[block].dwChunkLength <= *offset)
      (*offset) -= This->idxFrames[block].dwChunkLength;
    else
      break;
  }

  *pos = block;
}

static HRESULT AVIFILE_SaveFile(IAVIFileImpl *This)
{
  MainAVIHeader   MainAVIHdr;
  IAVIStreamImpl* pStream;
  MMCKINFO        ckRIFF;
  MMCKINFO        ckLIST1;
  MMCKINFO        ckLIST2;
  MMCKINFO        ck;
  DWORD           nStream;
  DWORD           dwPos;
  HRESULT         hr;

  /* initialize some things */
  if (This->dwMoviChunkPos == 0)
    AVIFILE_ComputeMoviStart(This);

2112
  /* written one record too much? */
2113 2114 2115 2116 2117 2118
  if (This->ckLastRecord.dwFlags & MMIO_DIRTY) {
    This->dwNextFramePos -= 3 * sizeof(DWORD);
    if (This->nIdxRecords > 0)
      This->nIdxRecords--;
  }

2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134
  AVIFILE_UpdateInfo(This);

  assert(This->fInfo.dwScale != 0);

  memset(&MainAVIHdr, 0, sizeof(MainAVIHdr));
  MainAVIHdr.dwMicroSecPerFrame    = MulDiv(This->fInfo.dwRate, 1000000,
					    This->fInfo.dwScale);
  MainAVIHdr.dwMaxBytesPerSec      = This->fInfo.dwMaxBytesPerSec;
  MainAVIHdr.dwPaddingGranularity  = AVI_HEADERSIZE;
  MainAVIHdr.dwFlags               = This->fInfo.dwFlags;
  MainAVIHdr.dwTotalFrames         = This->fInfo.dwLength;
  MainAVIHdr.dwInitialFrames       = 0;
  MainAVIHdr.dwStreams             = This->fInfo.dwStreams;
  MainAVIHdr.dwSuggestedBufferSize = This->fInfo.dwSuggestedBufferSize;
  MainAVIHdr.dwWidth               = This->fInfo.dwWidth;
  MainAVIHdr.dwHeight              = This->fInfo.dwHeight;
2135
  MainAVIHdr.dwInitialFrames       = This->dwInitialFrames;
2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162

  /* now begin writing ... */
  mmioSeek(This->hmmio, 0, SEEK_SET);

  /* RIFF chunk */
  ckRIFF.cksize  = 0;
  ckRIFF.fccType = formtypeAVI;
  if (mmioCreateChunk(This->hmmio, &ckRIFF, MMIO_CREATERIFF) != S_OK)
    return AVIERR_FILEWRITE;

  /* AVI headerlist */
  ckLIST1.cksize  = 0;
  ckLIST1.fccType = listtypeAVIHEADER;
  if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
    return AVIERR_FILEWRITE;

  /* MainAVIHeader */
  ck.ckid    = ckidAVIMAINHDR;
  ck.cksize  = sizeof(MainAVIHdr);
  ck.fccType = 0;
  if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEWRITE;
  if (mmioWrite(This->hmmio, (HPSTR)&MainAVIHdr, ck.cksize) != ck.cksize)
    return AVIERR_FILEWRITE;
  if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEWRITE;

2163
  /* write the headers of each stream into a separate streamheader list */
2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183
  for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
    AVIStreamHeader strHdr;

    pStream = This->ppStreams[nStream];

    /* begin the new streamheader list */
    ckLIST2.cksize  = 0;
    ckLIST2.fccType = listtypeSTREAMHEADER;
    if (mmioCreateChunk(This->hmmio, &ckLIST2, MMIO_CREATELIST) != S_OK)
      return AVIERR_FILEWRITE;

    /* create an AVIStreamHeader from the AVSTREAMINFO */
    strHdr.fccType               = pStream->sInfo.fccType;
    strHdr.fccHandler            = pStream->sInfo.fccHandler;
    strHdr.dwFlags               = pStream->sInfo.dwFlags;
    strHdr.wPriority             = pStream->sInfo.wPriority;
    strHdr.wLanguage             = pStream->sInfo.wLanguage;
    strHdr.dwInitialFrames       = pStream->sInfo.dwInitialFrames;
    strHdr.dwScale               = pStream->sInfo.dwScale;
    strHdr.dwRate                = pStream->sInfo.dwRate;
2184
    strHdr.dwStart               = pStream->sInfo.dwStart;
2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203
    strHdr.dwLength              = pStream->sInfo.dwLength;
    strHdr.dwSuggestedBufferSize = pStream->sInfo.dwSuggestedBufferSize;
    strHdr.dwQuality             = pStream->sInfo.dwQuality;
    strHdr.dwSampleSize          = pStream->sInfo.dwSampleSize;
    strHdr.rcFrame.left          = pStream->sInfo.rcFrame.left;
    strHdr.rcFrame.top           = pStream->sInfo.rcFrame.top;
    strHdr.rcFrame.right         = pStream->sInfo.rcFrame.right;
    strHdr.rcFrame.bottom        = pStream->sInfo.rcFrame.bottom;

    /* now write the AVIStreamHeader */
    ck.ckid   = ckidSTREAMHEADER;
    ck.cksize = sizeof(strHdr);
    if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
      return AVIERR_FILEWRITE;
    if (mmioWrite(This->hmmio, (HPSTR)&strHdr, ck.cksize) != ck.cksize)
      return AVIERR_FILEWRITE;
    if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
      return AVIERR_FILEWRITE;

2204
    /* ... the hopefully ever present streamformat ... */
2205 2206 2207 2208 2209
    ck.ckid   = ckidSTREAMFORMAT;
    ck.cksize = pStream->cbFormat;
    if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
      return AVIERR_FILEWRITE;
    if (pStream->lpFormat != NULL && ck.cksize > 0) {
2210
      if (mmioWrite(This->hmmio, pStream->lpFormat, ck.cksize) != ck.cksize)
2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221
	return AVIERR_FILEWRITE;
    }
    if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
      return AVIERR_FILEWRITE;

    /* ... some optional existing handler data ... */
    if (pStream->lpHandlerData != NULL && pStream->cbHandlerData > 0) {
      ck.ckid   = ckidSTREAMHANDLERDATA;
      ck.cksize = pStream->cbHandlerData;
      if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
	return AVIERR_FILEWRITE;
2222
      if (mmioWrite(This->hmmio, pStream->lpHandlerData, ck.cksize) != ck.cksize)
2223 2224 2225 2226 2227 2228 2229
	return AVIERR_FILEWRITE;
      if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
	return AVIERR_FILEWRITE;
    }

    /* ... some optional additional extra chunk for this stream ... */
    if (pStream->extra.lp != NULL && pStream->extra.cb > 0) {
Austin English's avatar
Austin English committed
2230
      /* the chunk header(s) are already in the structure */
2231
      if (mmioWrite(This->hmmio, pStream->extra.lp, pStream->extra.cb) != pStream->extra.cb)
2232 2233 2234 2235
	return AVIERR_FILEWRITE;
    }

    /* ... an optional name for this stream ... */
2236
    if (pStream->sInfo.szName[0]) {
2237 2238 2239 2240 2241 2242 2243 2244 2245 2246
      LPSTR str;

      ck.ckid   = ckidSTREAMNAME;
      ck.cksize = lstrlenW(pStream->sInfo.szName) + 1;
      if (ck.cksize & 1) /* align */
	ck.cksize++;
      if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
	return AVIERR_FILEWRITE;

      /* the streamname must be saved in ASCII not Unicode */
2247
      str = HeapAlloc(GetProcessHeap(), 0, ck.cksize);
2248 2249 2250 2251 2252
      if (str == NULL)
	return AVIERR_MEMORY;
      WideCharToMultiByte(CP_ACP, 0, pStream->sInfo.szName, -1, str,
			  ck.cksize, NULL, NULL);

2253
      if (mmioWrite(This->hmmio, str, ck.cksize) != ck.cksize) {
2254
	HeapFree(GetProcessHeap(), 0, str);	
2255 2256 2257
	return AVIERR_FILEWRITE;
      }

2258
      HeapFree(GetProcessHeap(), 0, str);
2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305
      if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
	return AVIERR_FILEWRITE;
    }

    /* close streamheader list for this stream */
    if (mmioAscend(This->hmmio, &ckLIST2, 0) != S_OK)
      return AVIERR_FILEWRITE;
  } /* for (0 <= nStream < MainAVIHdr.dwStreams) */

  /* close the aviheader list */
  if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
    return AVIERR_FILEWRITE;

  /* check for padding to pre-guessed 'movi'-chunk position */
  dwPos = ckLIST1.dwDataOffset + ckLIST1.cksize;
  if (This->dwMoviChunkPos - 2 * sizeof(DWORD) > dwPos) {
    ck.ckid   = ckidAVIPADDING;
    ck.cksize = This->dwMoviChunkPos - dwPos - 4 * sizeof(DWORD);
    assert((LONG)ck.cksize >= 0);

    if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
      return AVIERR_FILEWRITE;
    if (mmioSeek(This->hmmio, ck.cksize, SEEK_CUR) == -1)
      return AVIERR_FILEWRITE;
    if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
      return AVIERR_FILEWRITE;
  }

  /* now write the 'movi' chunk */
  mmioSeek(This->hmmio, This->dwMoviChunkPos - 2 * sizeof(DWORD), SEEK_SET);
  ckLIST1.cksize  = 0;
  ckLIST1.fccType = listtypeAVIMOVIE;
  if (mmioCreateChunk(This->hmmio, &ckLIST1, MMIO_CREATELIST) != S_OK)
    return AVIERR_FILEWRITE;
  if (mmioSeek(This->hmmio, This->dwNextFramePos, SEEK_SET) == -1)
    return AVIERR_FILEWRITE;
  if (mmioAscend(This->hmmio, &ckLIST1, 0) != S_OK)
    return AVIERR_FILEWRITE;

  /* write 'idx1' chunk */
  hr = AVIFILE_SaveIndex(This);
  if (FAILED(hr))
    return hr;

  /* write optional extra file chunks */
  if (This->fileextra.lp != NULL && This->fileextra.cb > 0) {
    /* as for the streams, are the chunk header(s) in the structure */
2306
    if (mmioWrite(This->hmmio, This->fileextra.lp, This->fileextra.cb) != This->fileextra.cb)
2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321
      return AVIERR_FILEWRITE;
  }

  /* close RIFF chunk */
  if (mmioAscend(This->hmmio, &ckRIFF, 0) != S_OK)
    return AVIERR_FILEWRITE;

  /* add some JUNK at end for bad parsers */
  memset(&ckRIFF, 0, sizeof(ckRIFF));
  mmioWrite(This->hmmio, (HPSTR)&ckRIFF, sizeof(ckRIFF));
  mmioFlush(This->hmmio, 0);

  return AVIERR_OK;
}

2322
static HRESULT AVIFILE_SaveIndex(const IAVIFileImpl *This)
2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335
{
  IAVIStreamImpl *pStream;
  AVIINDEXENTRY   idx;
  MMCKINFO        ck;
  DWORD           nStream;
  LONG            n;

  ck.ckid   = ckidAVINEWINDEX;
  ck.cksize = 0;
  if (mmioCreateChunk(This->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEWRITE;

  if (This->fInfo.dwFlags & AVIFILEINFO_ISINTERLEAVED) {
Austin English's avatar
Austin English committed
2336
    /* is interleaved -- write block of corresponding frames */
2337 2338 2339 2340 2341 2342 2343
    LONG lInitialFrames = 0;
    LONG stepsize;
    LONG i;

    if (This->ppStreams[0]->sInfo.dwSampleSize == 0)
      stepsize = 1;
    else
2344
      stepsize = AVIStreamTimeToSample(&This->ppStreams[0]->IAVIStream_iface, 1000000);
2345

2346 2347
    assert(stepsize > 0);

2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366
    for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
      if (lInitialFrames < This->ppStreams[nStream]->sInfo.dwInitialFrames)
	lInitialFrames = This->ppStreams[nStream]->sInfo.dwInitialFrames;
    }

    for (i = -lInitialFrames; i < (LONG)This->fInfo.dwLength - lInitialFrames;
	 i += stepsize) {
      DWORD nFrame = lInitialFrames + i;

      assert(nFrame < This->nIdxRecords);

      idx.ckid          = listtypeAVIRECORD;
      idx.dwFlags       = AVIIF_LIST;
      idx.dwChunkLength = This->idxRecords[nFrame].dwChunkLength;
      idx.dwChunkOffset = This->idxRecords[nFrame].dwChunkOffset
	- This->dwMoviChunkPos;
      if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
	return AVIERR_FILEWRITE;

2367
      for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2368 2369 2370 2371 2372 2373 2374 2375 2376 2377
	pStream = This->ppStreams[nStream];

	/* heave we reached start of this stream? */
	if (-(LONG)pStream->sInfo.dwInitialFrames > i)
	  continue;

	if (pStream->sInfo.dwInitialFrames < lInitialFrames)
	  nFrame -= (lInitialFrames - pStream->sInfo.dwInitialFrames);

	/* reached end of this stream? */
2378
	if (pStream->lLastFrame <= nFrame)
2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411
	  continue;

	if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
	    pStream->sInfo.dwFormatChangeCount != 0 &&
	    pStream->idxFmtChanges != NULL) {
	  DWORD pos;

	  for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
	    if (pStream->idxFmtChanges[pos].ckid == nFrame) {
	      idx.dwFlags = AVIIF_NOTIME;
	      idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream);
	      idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
	      idx.dwChunkOffset = pStream->idxFmtChanges[pos].dwChunkOffset
		- This->dwMoviChunkPos;

	      if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
		return AVIERR_FILEWRITE;
	      break;
	    }
	  }
	} /* if have formatchanges */

	idx.ckid          = pStream->idxFrames[nFrame].ckid;
	idx.dwFlags       = pStream->idxFrames[nFrame].dwFlags;
	idx.dwChunkLength = pStream->idxFrames[nFrame].dwChunkLength;
	idx.dwChunkOffset = pStream->idxFrames[nFrame].dwChunkOffset
	  - This->dwMoviChunkPos;
	if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
	  return AVIERR_FILEWRITE;
      }
    }
  } else {
    /* not interleaved -- write index for each stream at once */
2412
    for (nStream = 0; nStream < This->fInfo.dwStreams; nStream++) {
2413 2414
      pStream = This->ppStreams[nStream];

2415
      for (n = 0; n <= pStream->lLastFrame; n++) {
2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451
	if ((pStream->sInfo.dwFlags & AVISTREAMINFO_FORMATCHANGES) &&
	    (pStream->sInfo.dwFormatChangeCount != 0)) {
	  DWORD pos;

	  for (pos = 0; pos < pStream->sInfo.dwFormatChangeCount; pos++) {
	    if (pStream->idxFmtChanges[pos].ckid == n) {
	      idx.dwFlags = AVIIF_NOTIME;
	      idx.ckid    = MAKEAVICKID(cktypePALchange, pStream->nStream);
	      idx.dwChunkLength = pStream->idxFmtChanges[pos].dwChunkLength;
	      idx.dwChunkOffset =
		pStream->idxFmtChanges[pos].dwChunkOffset - This->dwMoviChunkPos;
	      if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
		return AVIERR_FILEWRITE;
	      break;
	    }
	  }
	} /* if have formatchanges */

	idx.ckid          = pStream->idxFrames[n].ckid;
	idx.dwFlags       = pStream->idxFrames[n].dwFlags;
	idx.dwChunkLength = pStream->idxFrames[n].dwChunkLength;
	idx.dwChunkOffset = pStream->idxFrames[n].dwChunkOffset
	  - This->dwMoviChunkPos;

	if (mmioWrite(This->hmmio, (HPSTR)&idx, sizeof(idx)) != sizeof(idx))
	  return AVIERR_FILEWRITE;
      }
    }
  } /* if not interleaved */

  if (mmioAscend(This->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEWRITE;

  return AVIERR_OK;
}

2452
static ULONG  AVIFILE_SearchStream(const IAVIFileImpl *This, DWORD fcc, LONG lSkip)
2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494
{
  UINT i;
  UINT nStream;

  /* pre-condition */
  assert(lSkip >= 0);

  if (fcc != 0) {
    /* search the number of the specified stream */
    nStream = (ULONG)-1;
    for (i = 0; i < This->fInfo.dwStreams; i++) {
      assert(This->ppStreams[i] != NULL);

      if (This->ppStreams[i]->sInfo.fccType == fcc) {
	if (lSkip == 0) {
	  nStream = i;
	  break;
	} else
	  lSkip--;
      }
    }
  } else
    nStream = lSkip;

  return nStream;
}

static void    AVIFILE_UpdateInfo(IAVIFileImpl *This)
{
  UINT i;

  /* pre-conditions */
  assert(This != NULL);

  This->fInfo.dwMaxBytesPerSec      = 0;
  This->fInfo.dwCaps                = AVIFILECAPS_CANREAD|AVIFILECAPS_CANWRITE;
  This->fInfo.dwSuggestedBufferSize = 0;
  This->fInfo.dwWidth               = 0;
  This->fInfo.dwHeight              = 0;
  This->fInfo.dwScale               = 0;
  This->fInfo.dwRate                = 0;
  This->fInfo.dwLength              = 0;
2495
  This->dwInitialFrames             = 0;
2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513

  for (i = 0; i < This->fInfo.dwStreams; i++) {
    AVISTREAMINFOW *psi;
    DWORD           n;

    /* pre-conditions */
    assert(This->ppStreams[i] != NULL);

    psi = &This->ppStreams[i]->sInfo;
    assert(psi->dwScale != 0);
    assert(psi->dwRate != 0);

    if (i == 0) {
      /* use first stream timings as base */
      This->fInfo.dwScale  = psi->dwScale;
      This->fInfo.dwRate   = psi->dwRate;
      This->fInfo.dwLength = psi->dwLength;
    } else {
2514 2515
      n = AVIStreamSampleToSample(&This->ppStreams[0]->IAVIStream_iface,
                                  &This->ppStreams[i]->IAVIStream_iface, psi->dwLength);
2516 2517 2518 2519
      if (This->fInfo.dwLength < n)
	This->fInfo.dwLength = n;
    }

2520 2521 2522
    if (This->dwInitialFrames < psi->dwInitialFrames)
      This->dwInitialFrames = psi->dwInitialFrames;

2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546
    if (This->fInfo.dwSuggestedBufferSize < psi->dwSuggestedBufferSize)
      This->fInfo.dwSuggestedBufferSize = psi->dwSuggestedBufferSize;

    if (psi->dwSampleSize != 0) {
      /* fixed sample size -- exact computation */
      This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSampleSize, psi->dwRate,
					     psi->dwScale);
    } else {
      /* variable sample size -- only upper limit */
      This->fInfo.dwMaxBytesPerSec += MulDiv(psi->dwSuggestedBufferSize,
					     psi->dwRate, psi->dwScale);

      /* update dimensions */
      n = psi->rcFrame.right - psi->rcFrame.left;
      if (This->fInfo.dwWidth < n)
	This->fInfo.dwWidth = n;
      n = psi->rcFrame.bottom - psi->rcFrame.top;
      if (This->fInfo.dwHeight < n)
	This->fInfo.dwHeight = n;
    }
  }
}

static HRESULT AVIFILE_WriteBlock(IAVIStreamImpl *This, DWORD block,
2547
				  FOURCC ckid, DWORD flags, LPCVOID buffer,
2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565
				  LONG size)
{
  MMCKINFO ck;

  ck.ckid    = ckid;
  ck.cksize  = size;
  ck.fccType = 0;

  /* if no frame/block is already written, we must compute start of movi chunk */
  if (This->paf->dwMoviChunkPos == 0)
    AVIFILE_ComputeMoviStart(This->paf);

  if (mmioSeek(This->paf->hmmio, This->paf->dwNextFramePos, SEEK_SET) == -1)
    return AVIERR_FILEWRITE;

  if (mmioCreateChunk(This->paf->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEWRITE;
  if (buffer != NULL && size > 0) {
2566
    if (mmioWrite(This->paf->hmmio, buffer, size) != size)
2567 2568 2569 2570 2571 2572
      return AVIERR_FILEWRITE;
  }
  if (mmioAscend(This->paf->hmmio, &ck, 0) != S_OK)
    return AVIERR_FILEWRITE;

  This->paf->fDirty         = TRUE;
2573
  This->paf->dwNextFramePos = mmioSeek(This->paf->hmmio, 0, SEEK_CUR);
2574

2575 2576
  return AVIFILE_AddFrame(This, ckid, size,
			  ck.dwDataOffset - 2 * sizeof(DWORD), flags);
2577
}