Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1669 lines
46 KiB

  1. /* (C) Copyright Microsoft Corporation 1991-1994. All Rights Reserved */
  2. /* file.c
  3. *
  4. * File I/O and related functions.
  5. *
  6. * Revision history:
  7. * 4/2/91 LaurieGr (AKA LKG) Ported to WIN32 / WIN16 common code
  8. * 5/27/92 -jyg- Added more RIFF support to BOMBAY version
  9. * 22/Feb/94 LaurieGr merged Motown and Daytona version
  10. */
  11. #include "nocrap.h"
  12. #include <windows.h>
  13. #include <commdlg.h>
  14. #include <mmsystem.h>
  15. #include <mmreg.h>
  16. #include <windowsx.h>
  17. #define INCLUDE_OLESTUBS
  18. #include "SoundRec.h"
  19. #include "srecids.h"
  20. #ifdef CHICAGO
  21. # if WINVER >= 0x0400
  22. # include <shellapi.h>
  23. # else
  24. # include <shell2.h>
  25. # endif
  26. #endif
  27. #include "file.h"
  28. #include "convert.h"
  29. #include "reg.h"
  30. /* globals */
  31. PCKNODE gpcknHead = NULL; // ??? eugh. more globals!
  32. PCKNODE gpcknTail = NULL;
  33. static PFACT spFact = NULL;
  34. static long scbFact = 0;
  35. static void FreeAllChunks(PCKNODE *ppcknHead, PCKNODE *ppcknTail);
  36. static BOOL AddChunk(LPMMCKINFO lpCk, HPBYTE hb, PCKNODE * ppcknHead,
  37. PCKNODE * ppcknTail);
  38. static PCKNODE FreeHeadChunk(PCKNODE *ppcknHead);
  39. /*
  40. * Is the current document untitled?
  41. */
  42. BOOL IsDocUntitled()
  43. {
  44. return (lstrcmp(gachFileName, aszUntitled) == 0);
  45. }
  46. /*
  47. * Rename the current document.
  48. */
  49. void RenameDoc(LPTSTR aszNewFile)
  50. {
  51. lstrcpy(gachFileName, aszNewFile);
  52. lstrcpy(gachLinkFilename, gachFileName);
  53. if (gfLinked)
  54. AdviseRename(gachLinkFilename);
  55. }
  56. /* MarkWaveDirty: Mark the wave as dirty. */
  57. void FAR PASCAL
  58. EndWaveEdit(BOOL fDirty)
  59. {
  60. if (fDirty)
  61. {
  62. gfDirty = TRUE;
  63. AdviseDataChange();
  64. DoOleSave();
  65. AdviseSaved();
  66. }
  67. }
  68. void FAR PASCAL
  69. BeginWaveEdit(void)
  70. {
  71. FlushOleClipboard();
  72. }
  73. /* fOK = PromptToSave()
  74. *
  75. * If the file is dirty (modified), ask the user "Save before closing?".
  76. * Return TRUE if it's okay to continue, FALSE if the caller should cancel
  77. * whatever it's doing.
  78. */
  79. PROMPTRESULT FAR PASCAL
  80. PromptToSave(
  81. BOOL fMustClose,
  82. BOOL fSetForground)
  83. {
  84. WORD wID;
  85. DWORD dwMB = MB_ICONEXCLAMATION | MB_YESNOCANCEL;
  86. if (fSetForground)
  87. dwMB |= MB_SETFOREGROUND;
  88. /* stop playing/recording */
  89. StopWave();
  90. if (gfDirty && gfStandalone && gfDirty != -1) { // changed and possible to save
  91. wID = ErrorResBox( ghwndApp
  92. , ghInst
  93. , dwMB
  94. , IDS_APPTITLE
  95. , IDS_SAVECHANGES
  96. , (LPTSTR) gachFileName
  97. );
  98. if (wID == IDCANCEL)
  99. {
  100. return enumCancel;
  101. }
  102. else if (wID == IDYES)
  103. {
  104. if (!FileSave(FALSE))
  105. return enumCancel;
  106. }
  107. else
  108. return enumRevert;
  109. }
  110. #if 0
  111. // is this necessary?
  112. // This is bad. It notifies the container before we actually
  113. // DoOleClose. This will cause some containers (Excel 5.0c) to
  114. // get confused and nuke client sites on non-dirty objects.
  115. else if (fMustClose)
  116. {
  117. DebugBreak();
  118. AdviseClosed();
  119. }
  120. #endif
  121. return enumSaved;
  122. } /* PromptToSave */
  123. /* fOK = CheckIfFileExists(szFileName)
  124. *
  125. * The user specified <szFileName> as a file to write over -- check if
  126. * this file exists. Return TRUE if it's okay to continue (i.e. the
  127. * file doesn't exist, or the user OK'd overwriting it),
  128. * FALSE if the caller should cancel whatever it's doing.
  129. */
  130. static BOOL NEAR PASCAL
  131. CheckIfFileExists( LPTSTR szFileName) // file name to check
  132. {
  133. HANDLE hFile;
  134. hFile = CreateFile(szFileName,
  135. GENERIC_READ|GENERIC_WRITE,
  136. 0,
  137. NULL,
  138. OPEN_EXISTING,
  139. FILE_ATTRIBUTE_NORMAL,
  140. NULL);
  141. if (hFile == INVALID_HANDLE_VALUE)
  142. return TRUE; // doesn't exist
  143. CloseHandle(hFile);
  144. /* prompt user for permission to overwrite the file */
  145. return ErrorResBox(ghwndApp, ghInst, MB_ICONQUESTION | MB_OKCANCEL,
  146. IDS_APPTITLE, IDS_FILEEXISTS, szFileName) == IDOK;
  147. }
  148. #define SLASH(c) ((c) == TEXT('/') || (c) == TEXT('\\'))
  149. /* return a pointer to the filename part of the path
  150. i.e. scan back from end to \: or start
  151. e.g. "C:\FOO\BAR.XYZ" -> return pointer to "BAR.XYZ"
  152. */
  153. LPCTSTR FAR PASCAL
  154. FileName(LPCTSTR szPath)
  155. {
  156. LPCTSTR sz;
  157. if (!szPath)
  158. return NULL;
  159. for (sz=szPath; *sz; sz = CharNext(sz))
  160. ;
  161. for (; !SLASH(*sz) && *sz!=TEXT(':'); sz = CharPrev(szPath,sz))
  162. if (sz == szPath)
  163. return sz;
  164. return CharNext(sz);
  165. }
  166. /* UpdateCaption()
  167. *
  168. * Set the caption of the app window.
  169. */
  170. void FAR PASCAL
  171. UpdateCaption(void)
  172. {
  173. TCHAR ach[_MAX_PATH + _MAX_FNAME + _MAX_EXT - 2];
  174. static SZCODE aszTitleFormat[] = TEXT("%s - %s");
  175. #ifdef CHICAGO
  176. SHFILEINFO shfi;
  177. if (!IsDocUntitled() && SHGetFileInfo(gachFileName, 0, &shfi, sizeof(shfi), SHGFI_ICON|SHGFI_DISPLAYNAME ))
  178. {
  179. wsprintf(ach, aszTitleFormat, shfi.szDisplayName, (LPTSTR)gachAppTitle);
  180. SetWindowText(ghwndApp, ach);
  181. SetClassLongPtr(ghwndApp, GCLP_HICON, (DWORD_PTR)shfi.hIcon);
  182. return;
  183. }
  184. else
  185. {
  186. //
  187. // reset icon to app icon
  188. //
  189. extern HICON ghiconApp;
  190. SetClassLongPtr(ghwndApp, GCLP_HICON, (LONG_PTR)ghiconApp);
  191. }
  192. #endif
  193. wsprintf(ach, aszTitleFormat, FileName(gachFileName), (LPTSTR)gachAppTitle);
  194. SetWindowText(ghwndApp, ach);
  195. } /* UpdateCaption */
  196. //REVIEW: The functionality in FileOpen and FileNew should be more
  197. // safe for OLE. This means, we want to open a file, but
  198. // have no reason to revoke the server.
  199. /* FileNew(fmt, fUpdateDisplay, fNewDlg)
  200. *
  201. * Make a blank document.
  202. *
  203. * If <fUpdateDisplay> is TRUE, then update the display after creating a new file.
  204. */
  205. BOOL FAR PASCAL FileNew(
  206. WORD fmt,
  207. BOOL fUpdateDisplay,
  208. BOOL fNewDlg)
  209. {
  210. //
  211. // avoid reentrancy when called through OLE
  212. //
  213. // ??? Need to double check on this. Is this thread safe?
  214. // ??? Does it need to be thread safe? Or are we actually
  215. // ??? just trying to avoid recursion rather than reentrancy?
  216. if (gfInFileNew)
  217. return FALSE;
  218. //
  219. // stop playing/recording
  220. //
  221. StopWave();
  222. //
  223. // Commit all pending objects.
  224. //
  225. FlushOleClipboard();
  226. //
  227. // some client's (ie Excel 3.00 and PowerPoint 1.0) don't
  228. // handle saved notifications, they expect to get a
  229. // OLE_CLOSED message.
  230. //
  231. // if the user has chosen to update the object, but the client did
  232. // not then send a OLE_CLOSED message.
  233. //
  234. if (gfEmbeddedObject && gfDirty == -1)
  235. AdviseClosed();
  236. //
  237. // FileNew can be called either from FileOpen or from a menu
  238. // or from the server, etc... We should behave as FileOpen from the
  239. // server (i.e. the dialog can be canceled without toasting the buffer)
  240. //
  241. if (!NewWave(fmt,fNewDlg))
  242. return FALSE;
  243. //
  244. // update state variables
  245. //
  246. lstrcpy(gachFileName, aszUntitled);
  247. BuildUniqueLinkName();
  248. gfDirty = FALSE; // file was modified and not saved?
  249. if (fUpdateDisplay) {
  250. UpdateCaption();
  251. UpdateDisplay(TRUE);
  252. }
  253. FreeAllChunks(&gpcknHead, &gpcknTail); // free all old info
  254. return TRUE;
  255. } /* FileNew */
  256. /* REVIEW: The functionality in FileOpen and FileNew should be more
  257. * safe for OLE. This means, we want to open a file, but
  258. * have no reason to revoke the server.
  259. * */
  260. BOOL FileLoad(
  261. LPCTSTR szFileName)
  262. {
  263. TCHAR aszFile[_MAX_PATH];
  264. HCURSOR hcurPrev = NULL; // cursor before hourglass
  265. HMMIO hmmio;
  266. BOOL fOk = TRUE;
  267. StopWave();
  268. // qualify
  269. GetFullPathName(szFileName,SIZEOF(aszFile),aszFile,NULL);
  270. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  271. // read the WAVE file
  272. hmmio = mmioOpen(aszFile, NULL, MMIO_READ | MMIO_ALLOCBUF);
  273. if (hmmio != NULL)
  274. {
  275. MMRESULT mmr;
  276. LPWAVEFORMATEX pwfx;
  277. DWORD cbwfx;
  278. DWORD cbdata;
  279. LPBYTE pdata;
  280. PCKNODE pcknHead = gpcknHead;
  281. PCKNODE pcknTail = gpcknTail;
  282. PFACT pfct = spFact;
  283. LONG cbfact = scbFact;
  284. gpcknHead = NULL;
  285. gpcknTail = NULL;
  286. spFact = NULL;
  287. scbFact = 0L;
  288. mmr = ReadWaveFile(hmmio
  289. , &pwfx
  290. , &cbwfx
  291. , &pdata
  292. , &cbdata
  293. , aszFile
  294. , TRUE);
  295. mmioClose(hmmio, 0);
  296. if (mmr != MMSYSERR_NOERROR || pwfx == NULL)
  297. {
  298. //
  299. // restore the cache globals
  300. //
  301. gpcknHead = pcknHead;
  302. gpcknTail = pcknTail;
  303. spFact = pfct;
  304. scbFact = cbfact;
  305. if (pwfx == NULL)
  306. {
  307. if (pdata)
  308. GlobalFreePtr(pdata);
  309. }
  310. goto RETURN_ERROR;
  311. }
  312. DestroyWave();
  313. gpWaveFormat = pwfx;
  314. gcbWaveFormat = cbwfx;
  315. gpWaveSamples = pdata;
  316. glWaveSamples = cbdata;
  317. //
  318. // destroy the cache temps
  319. //
  320. FreeAllChunks(&pcknHead, &pcknTail);
  321. if (pfct)
  322. GlobalFreePtr((LPVOID)pfct);
  323. }
  324. else
  325. {
  326. ErrorResBox(ghwndApp
  327. , ghInst
  328. , MB_ICONEXCLAMATION | MB_OK
  329. , IDS_APPTITLE
  330. , IDS_ERROROPEN
  331. , (LPTSTR) aszFile);
  332. goto RETURN_ERROR;
  333. }
  334. //
  335. // update state variables
  336. //
  337. RenameDoc(aszFile);
  338. glWaveSamplesValid = glWaveSamples;
  339. glWavePosition = 0L;
  340. goto RETURN_SUCCESS;
  341. RETURN_ERROR:
  342. fOk = FALSE;
  343. #if 0
  344. FreeAllChunks(&gpcknHead, &gpcknTail); /* free all old info */
  345. #endif
  346. RETURN_SUCCESS:
  347. if (hcurPrev != NULL)
  348. SetCursor(hcurPrev);
  349. /* Only mark clean on success */
  350. if (fOk)
  351. gfDirty = FALSE;
  352. /* update the display */
  353. UpdateCaption();
  354. UpdateDisplay(TRUE);
  355. return fOk;
  356. }
  357. /* FileOpen(szFileName)
  358. *
  359. * If <szFileName> is NULL, do a File/Open command. Otherwise, open
  360. * <szFileName>. Return TRUE on success, FALSE otherwise.
  361. */
  362. BOOL FAR PASCAL
  363. FileOpen(
  364. LPCTSTR szFileName) // file to open (or NULL)
  365. {
  366. TCHAR ach[80]; // buffer for string loading
  367. TCHAR aszFile[_MAX_PATH];
  368. HCURSOR hcurPrev = NULL; // cursor before hourglass
  369. HMMIO hmmio;
  370. BOOL fOk = TRUE;
  371. //
  372. // stop playing/recording
  373. //
  374. StopWave();
  375. //
  376. // Commit all pending objects.
  377. //
  378. FlushOleClipboard();
  379. if (!PromptToSave(FALSE, FALSE))
  380. goto RETURN_ERRORNONEW;
  381. //
  382. // get the new file name into <ofs.szPathName>
  383. //
  384. if (szFileName == NULL)
  385. {
  386. OPENFILENAME ofn;
  387. BOOL f;
  388. //
  389. // prompt user for file to open
  390. //
  391. LoadString(ghInst, IDS_OPEN, ach, SIZEOF(ach));
  392. aszFile[0] = 0;
  393. ofn.lStructSize = sizeof(OPENFILENAME);
  394. ofn.hwndOwner = ghwndApp;
  395. ofn.hInstance = NULL;
  396. ofn.lpstrFilter = aszFilter;
  397. ofn.lpstrCustomFilter = NULL;
  398. ofn.nMaxCustFilter = 0;
  399. ofn.nFilterIndex = 1;
  400. ofn.lpstrFile = aszFile;
  401. ofn.nMaxFile = SIZEOF(aszFile);
  402. ofn.lpstrFileTitle = NULL;
  403. ofn.nMaxFileTitle = 0;
  404. ofn.lpstrInitialDir = NULL;
  405. ofn.lpstrTitle = ach;
  406. ofn.Flags = OFN_FILEMUSTEXIST
  407. | OFN_PATHMUSTEXIST
  408. #ifdef CHICAGO
  409. | OFN_EXPLORER
  410. #endif
  411. | OFN_HIDEREADONLY;
  412. ofn.lpfnHook = NULL;
  413. ofn.nFileOffset = 0;
  414. ofn.nFileExtension = 0;
  415. ofn.lpstrDefExt = gachDefFileExt;
  416. ofn.lCustData = 0;
  417. ofn.lpTemplateName = NULL;
  418. f = GetOpenFileName(&ofn);
  419. if (!f)
  420. goto RETURN_ERRORNONEW;
  421. }
  422. else
  423. {
  424. GetFullPathName(szFileName,SIZEOF(aszFile),aszFile,NULL);
  425. }
  426. UpdateWindow(ghwndApp);
  427. //
  428. // show hourglass cursor
  429. //
  430. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  431. //
  432. // read the WAVE file
  433. //
  434. hmmio = mmioOpen(aszFile, NULL, MMIO_READ | MMIO_ALLOCBUF);
  435. if (hmmio != NULL)
  436. {
  437. MMRESULT mmr;
  438. LPWAVEFORMATEX pwfx;
  439. DWORD cbwfx;
  440. DWORD cbdata;
  441. LPBYTE pdata;
  442. PCKNODE pcknHead = gpcknHead;
  443. PCKNODE pcknTail = gpcknTail;
  444. PFACT pfct = spFact;
  445. LONG cbfact = scbFact;
  446. gpcknHead = NULL;
  447. gpcknTail = NULL;
  448. spFact = NULL;
  449. scbFact = 0L;
  450. mmr = ReadWaveFile(hmmio
  451. , &pwfx
  452. , &cbwfx
  453. , &pdata
  454. , &cbdata
  455. , aszFile
  456. , TRUE);
  457. mmioClose(hmmio, 0);
  458. if (mmr != MMSYSERR_NOERROR || pwfx == NULL)
  459. {
  460. //
  461. // restore the cache globals
  462. //
  463. gpcknHead = pcknHead;
  464. gpcknTail = pcknTail;
  465. spFact = pfct;
  466. scbFact = cbfact;
  467. if (pwfx == NULL)
  468. {
  469. if (pdata)
  470. GlobalFreePtr(pdata);
  471. }
  472. goto RETURN_ERRORNONEW;
  473. }
  474. DestroyWave();
  475. gpWaveFormat = pwfx;
  476. gcbWaveFormat = cbwfx;
  477. gpWaveSamples = pdata;
  478. glWaveSamples = cbdata;
  479. //
  480. // destroy the cache temps
  481. //
  482. FreeAllChunks(&pcknHead, &pcknTail);
  483. if (pfct)
  484. GlobalFreePtr((LPVOID)pfct);
  485. }
  486. else
  487. {
  488. ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK,
  489. IDS_APPTITLE, IDS_ERROROPEN, (LPTSTR) aszFile);
  490. goto RETURN_ERRORNONEW;
  491. }
  492. //
  493. // update state variables
  494. //
  495. RenameDoc(aszFile);
  496. glWaveSamplesValid = glWaveSamples;
  497. glWavePosition = 0L;
  498. goto RETURN_SUCCESS;
  499. #if 0
  500. RETURN_ERROR: // do error exit without error message
  501. FileNew(FMT_DEFAULT, FALSE, FALSE);// revert to "(Untitled)" state
  502. /* fall through */
  503. #endif
  504. RETURN_ERRORNONEW: // same as above, but don't do "new"
  505. fOk = FALSE;
  506. /* fall through */
  507. RETURN_SUCCESS: // normal exit
  508. if (hcurPrev != NULL)
  509. SetCursor(hcurPrev);
  510. /* Only mark clean on success */
  511. if (fOk)
  512. gfDirty = FALSE;
  513. /* update the display */
  514. UpdateCaption();
  515. UpdateDisplay(TRUE);
  516. return fOk;
  517. } /* FileOpen */
  518. /* fOK = FileSave(fSaveAs)
  519. *
  520. * Do a File/Save operation (if <fSaveAs> is FALSE) or a File/SaveAs
  521. * operation (if <fSaveAs> is TRUE). Return TRUE unless the user cancelled
  522. * or an error occurred.
  523. */
  524. BOOL FAR PASCAL FileSave(
  525. BOOL fSaveAs) // do a "Save As" instead of "Save"?
  526. {
  527. BOOL fOK = TRUE; // function succeeded?
  528. TCHAR ach[80]; // buffer for string loading
  529. TCHAR aszFile[_MAX_PATH];
  530. BOOL fUntitled; // file is untitled?
  531. HCURSOR hcurPrev = NULL; // cursor before hourglass
  532. HMMIO hmmio;
  533. // temp arguments to WriteWaveFile if a conversion is requested
  534. PWAVEFORMATEX pwfxSaveAsFormat = NULL;
  535. /* stop playing/recording */
  536. StopWave();
  537. fUntitled = IsDocUntitled();
  538. if (fSaveAs || fUntitled)
  539. {
  540. OPENFILENAME ofn;
  541. BOOL f;
  542. // prompt user for file to save
  543. LoadString(ghInst, IDS_SAVE, ach, SIZEOF(ach));
  544. if (!gfEmbeddedObject && !fUntitled)
  545. lstrcpy(aszFile, gachFileName);
  546. else
  547. aszFile[0] = 0;
  548. ofn.lStructSize = sizeof(OPENFILENAME);
  549. ofn.hwndOwner = ghwndApp;
  550. ofn.hInstance = ghInst;
  551. ofn.lpstrFilter = aszFilter;
  552. ofn.lpstrCustomFilter = NULL;
  553. ofn.nMaxCustFilter = 0;
  554. ofn.nFilterIndex = 1;
  555. ofn.lpstrFile = aszFile;
  556. ofn.nMaxFile = SIZEOF(aszFile);
  557. ofn.lpstrFileTitle = NULL;
  558. ofn.nMaxFileTitle = 0;
  559. ofn.lpstrInitialDir = NULL;
  560. ofn.lpstrTitle = ach;
  561. ofn.Flags = OFN_PATHMUSTEXIST
  562. | OFN_HIDEREADONLY
  563. #ifdef CHICAGO
  564. | OFN_EXPLORER
  565. #endif
  566. | OFN_NOREADONLYRETURN;
  567. ofn.nFileOffset = 0;
  568. ofn.nFileExtension = 0;
  569. ofn.lpstrDefExt = gachDefFileExt;
  570. //
  571. // We need to present a new Save As dialog template to add a convert
  572. // button. Adding a convert button requires us to also hook and
  573. // handle the button message ourselves.
  574. //
  575. if (fSaveAs)
  576. {
  577. // pwfxSaveAsFormat will point to a new format if the user
  578. // requested it
  579. ofn.lCustData = (LPARAM)(LPVOID)&pwfxSaveAsFormat;
  580. ofn.Flags |= OFN_ENABLETEMPLATE | OFN_ENABLEHOOK;
  581. ofn.lpTemplateName = MAKEINTRESOURCE(IDD_SAVEAS);
  582. ofn.lpfnHook = SaveAsHookProc;
  583. }
  584. else
  585. {
  586. ofn.lpfnHook = NULL;
  587. ofn.lpTemplateName = NULL;
  588. }
  589. f = GetSaveFileName(&ofn);
  590. if (!f)
  591. goto RETURN_CANCEL;
  592. {
  593. //
  594. // Add extension if none given
  595. //
  596. LPTSTR lp;
  597. for (lp = (LPTSTR)&aszFile[lstrlen(aszFile)] ; *lp != TEXT('.') ;)
  598. {
  599. if (SLASH(*lp) || *lp == TEXT(':') || lp == (LPTSTR)aszFile)
  600. {
  601. extern TCHAR FAR aszClassKey[];
  602. lstrcat(aszFile, aszClassKey);
  603. break;
  604. }
  605. lp = CharPrev(aszFile, lp);
  606. }
  607. }
  608. // prompt for permission to overwrite the file
  609. if (!CheckIfFileExists(aszFile))
  610. return FALSE; // user cancelled
  611. if (gfEmbeddedObject && gfDirty)
  612. {
  613. int id;
  614. // see if user wants to update first
  615. id = ErrorResBox( ghwndApp
  616. , ghInst
  617. , MB_ICONQUESTION | MB_YESNOCANCEL
  618. , IDS_APPTITLE
  619. , IDS_UPDATEBEFORESAVE);
  620. if (id == IDCANCEL)
  621. return FALSE;
  622. else if (id == IDYES)
  623. {
  624. DoOleSave();
  625. AdviseSaved();
  626. gfDirty = FALSE;
  627. }
  628. }
  629. }
  630. else
  631. {
  632. // Copy the current name to our temporary variable
  633. // We really should save to a different temporary file
  634. lstrcpy(aszFile, gachFileName);
  635. }
  636. // show hourglass cursor
  637. hcurPrev = SetCursor(LoadCursor(NULL, IDC_WAIT));
  638. // write the WAVE file
  639. // open the file -- if it already exists, truncate it to zero bytes
  640. hmmio = mmioOpen(aszFile
  641. , NULL
  642. , MMIO_CREATE | MMIO_WRITE | MMIO_ALLOCBUF);
  643. if (hmmio == NULL) {
  644. ErrorResBox(ghwndApp
  645. , ghInst
  646. , MB_ICONEXCLAMATION | MB_OK
  647. , IDS_APPTITLE
  648. , IDS_ERROROPEN
  649. , (LPTSTR) aszFile);
  650. goto RETURN_ERROR;
  651. }
  652. if (pwfxSaveAsFormat)
  653. {
  654. DWORD cbNew;
  655. DWORD cbOld;
  656. LPBYTE pbNew;
  657. cbOld = wfSamplesToBytes(gpWaveFormat, glWaveSamplesValid);
  658. if (ConvertFormatDialog(ghwndApp
  659. , gpWaveFormat
  660. , cbOld
  661. , gpWaveSamples
  662. , pwfxSaveAsFormat
  663. , &cbNew
  664. , &pbNew
  665. , 0
  666. , NULL) == MMSYSERR_NOERROR )
  667. {
  668. GlobalFreePtr(gpWaveFormat);
  669. GlobalFreePtr(gpWaveSamples);
  670. gpWaveFormat = pwfxSaveAsFormat;
  671. gcbWaveFormat = sizeof(WAVEFORMATEX);
  672. if (pwfxSaveAsFormat->wFormatTag != WAVE_FORMAT_PCM)
  673. gcbWaveFormat += pwfxSaveAsFormat->cbSize;
  674. gpWaveSamples = pbNew;
  675. glWaveSamples = wfBytesToSamples(gpWaveFormat, cbNew);
  676. glWaveSamplesValid = wfBytesToSamples(gpWaveFormat, cbNew);
  677. }
  678. else
  679. {
  680. ErrorResBox(ghwndApp
  681. , ghInst
  682. , MB_ICONEXCLAMATION | MB_OK
  683. , IDS_APPTITLE, IDS_ERR_CANTCONVERT);
  684. goto RETURN_ERROR;
  685. }
  686. }
  687. if (!WriteWaveFile(hmmio
  688. , gpWaveFormat
  689. , gcbWaveFormat
  690. , gpWaveSamples
  691. , glWaveSamplesValid))
  692. {
  693. mmioClose(hmmio,0);
  694. ErrorResBox(ghwndApp
  695. , ghInst
  696. , MB_ICONEXCLAMATION | MB_OK
  697. , IDS_APPTITLE, IDS_ERRORWRITE
  698. , (LPTSTR) aszFile );
  699. goto RETURN_ERROR;
  700. }
  701. mmioClose(hmmio,0);
  702. //
  703. // Only change file name if we succeed
  704. //
  705. RenameDoc(aszFile);
  706. UpdateCaption();
  707. if (fSaveAs || fUntitled)
  708. {
  709. AdviseRename(gachFileName);
  710. }
  711. else
  712. {
  713. DoOleSave();
  714. gfDirty = FALSE;
  715. }
  716. goto RETURN_SUCCESS;
  717. RETURN_ERROR: // do error exit without error message
  718. DeleteFile(aszFile);
  719. RETURN_CANCEL:
  720. fOK = FALSE;
  721. //
  722. // Clean up conversion selection
  723. //
  724. if (pwfxSaveAsFormat)
  725. GlobalFreePtr(pwfxSaveAsFormat);
  726. RETURN_SUCCESS: // normal exit
  727. if (hcurPrev != NULL)
  728. SetCursor(hcurPrev);
  729. if (fOK)
  730. gfDirty = FALSE;
  731. //
  732. // update the display
  733. //
  734. UpdateDisplay(TRUE);
  735. return fOK;
  736. } /* FileSave*/
  737. /* fOK = FileRevert()
  738. *
  739. * Do a File/Revert operation, i.e. let user revert to last-saved version.
  740. */
  741. BOOL FAR PASCAL
  742. FileRevert(void)
  743. {
  744. int id;
  745. TCHAR achFileName[_MAX_PATH];
  746. BOOL fOk;
  747. BOOL fDirtyOrig;
  748. /* "Revert..." menu is grayed unless file is dirty and file name
  749. * is not "(Untitled)" and this is not an embedded object
  750. */
  751. /* prompt user for permission to discard changes */
  752. id = ErrorResBox(ghwndApp, ghInst, MB_ICONQUESTION | MB_YESNO,
  753. IDS_APPTITLE, IDS_CONFIRMREVERT);
  754. if (id == IDNO)
  755. return FALSE;
  756. /* discard changes and re-open file */
  757. lstrcpy(achFileName, gachFileName); // FileNew nukes <gachFileName>
  758. /* Make file clean temporarily, so FileOpen() won't warn user */
  759. fDirtyOrig = gfDirty;
  760. gfDirty = FALSE;
  761. fOk = FileOpen(achFileName);
  762. if (!fOk)
  763. gfDirty = fDirtyOrig;
  764. return fOk;
  765. } /* FileRevert */
  766. /* ReadWaveFile
  767. *
  768. * Read a WAVE file from <hmmio>. Fill in <*pWaveFormat> with
  769. * the WAVE file format and <*plWaveSamples> with the number of samples in
  770. * the file. Return a pointer to the samples (stored in a GlobalAlloc'd
  771. * memory block) or NULL on error.
  772. *
  773. * <szFileName> is the name of the file that <hmmio> refers to.
  774. * <szFileName> is used only for displaying error messages.
  775. *
  776. * On failure, an error message is displayed.
  777. */
  778. MMRESULT ReadWaveFile(
  779. HMMIO hmmio, // handle to open file
  780. LPWAVEFORMATEX* ppWaveFormat, // fill in with the WAVE format
  781. DWORD * pcbWaveFormat, // fill in with WAVE format size
  782. LPBYTE * ppWaveSamples,
  783. DWORD * plWaveSamples, // number of samples
  784. LPTSTR szFileName, // file name (or NULL) for error msg.
  785. BOOL fCacheRIFF) // cache RIFF?
  786. {
  787. MMCKINFO ckRIFF; // chunk info. for RIFF chunk
  788. MMCKINFO ck; // info. for a chunk file
  789. HPBYTE pWaveSamples = NULL; // waveform samples
  790. UINT cbWaveFormat;
  791. WAVEFORMATEX* pWaveFormat = NULL;
  792. BOOL fHandled;
  793. DWORD dwBlkAlignSize = 0; // initialisation only to eliminate spurious warning
  794. MMRESULT mmr = MMSYSERR_NOERROR;
  795. //
  796. // added for robust RIFF checking
  797. //
  798. BOOL fFMT=FALSE, fDATA=FALSE, fFACT=FALSE;
  799. DWORD dwCkEnd,dwRiffEnd;
  800. if (ppWaveFormat == NULL
  801. || pcbWaveFormat == NULL
  802. || ppWaveSamples == NULL
  803. || plWaveSamples == NULL )
  804. return MMSYSERR_ERROR;
  805. *ppWaveFormat = NULL;
  806. *pcbWaveFormat = 0L;
  807. *ppWaveSamples = NULL;
  808. *plWaveSamples = 0L;
  809. //
  810. // descend the file into the RIFF chunk
  811. //
  812. if (mmioDescend(hmmio, &ckRIFF, NULL, 0) != 0)
  813. {
  814. //
  815. // Zero length files are OK.
  816. //
  817. if (mmioSeek(hmmio, 0L, SEEK_END) == 0L)
  818. {
  819. DWORD cbwfx;
  820. LPWAVEFORMATEX pwfx;
  821. //
  822. // Synthesize a wave header
  823. //
  824. if (!SoundRec_GetDefaultFormat(&pwfx, &cbwfx))
  825. {
  826. cbwfx = sizeof(WAVEFORMATEX);
  827. pwfx = (WAVEFORMATEX *)GlobalAllocPtr(GHND, sizeof(WAVEFORMATEX));
  828. if (pwfx == NULL)
  829. return MMSYSERR_NOMEM;
  830. CreateWaveFormat(pwfx,FMT_DEFAULT,(UINT)WAVE_MAPPER);
  831. }
  832. *ppWaveFormat = pwfx;
  833. *pcbWaveFormat = cbwfx;
  834. *plWaveSamples = 0L;
  835. *ppWaveSamples = NULL;
  836. return MMSYSERR_NOERROR;
  837. }
  838. else
  839. goto ERROR_NOTAWAVEFILE;
  840. }
  841. /* make sure the file is a WAVE file */
  842. if ((ckRIFF.ckid != FOURCC_RIFF) ||
  843. (ckRIFF.fccType != mmioFOURCC('W', 'A', 'V', 'E')))
  844. goto ERROR_NOTAWAVEFILE;
  845. /* We can preserve the order of chunks in memory
  846. * by parsing the entire file as we read it in.
  847. */
  848. /* Use AddChunk(&ck,NULL) to add a placeholder node
  849. * for a chunk being edited.
  850. * Else AddChunk(&ck,hpstrData)
  851. */
  852. dwRiffEnd = ckRIFF.cksize;
  853. dwRiffEnd += (dwRiffEnd % 2); /* must be even */
  854. while ( mmioDescend( hmmio, &ck, &ckRIFF, 0) == 0)
  855. {
  856. fHandled = FALSE;
  857. dwCkEnd = ck.cksize + (ck.dwDataOffset - ckRIFF.dwDataOffset);
  858. dwCkEnd += (dwCkEnd % 2); /* must be even */
  859. if (dwCkEnd > dwRiffEnd)
  860. {
  861. DPF(TEXT("Chunk End %lu> Riff End %lu\n"),dwCkEnd,dwRiffEnd);
  862. /* CORRUPTED RIFF, when we ascend we'll be past the
  863. * end of the RIFF
  864. */
  865. if (fFMT && fDATA)
  866. {
  867. /* We might have enough information to deal
  868. * with clipboard mixing/inserts, etc...
  869. * This is for the bug with BOOKSHELF '92
  870. * where they give us RIFF with a
  871. * RIFF.dwSize > sum(childchunks).
  872. * They *PROMISE* not to do this again.
  873. */
  874. mmioAscend( hmmio, &ck, 0 );
  875. goto RETURN_FINISH;
  876. }
  877. goto ERROR_READING;
  878. }
  879. switch ( ck.ckid )
  880. {
  881. case mmioFOURCC('f','m','t',' '):
  882. if (fFMT)
  883. break; /* we've been here before */
  884. /* expect the 'fmt' chunk to be at least as
  885. * large as <sizeof(WAVEFORMAT)>;
  886. * if there are extra parameters at the end,
  887. * we'll ignore them
  888. */
  889. // 'fmt' chunk too small?
  890. if (ck.cksize < sizeof(WAVEFORMAT))
  891. goto ERROR_NOTAWAVEFILE;
  892. /*
  893. * always force allocation to be AT LEAST
  894. * the size of WFX. this is required so all
  895. * code does not have to special case the
  896. * cbSize field. note that we alloc with zero
  897. * init so cbSize will be zero for plain
  898. * jane PCM
  899. */
  900. cbWaveFormat = max((WORD)ck.cksize,
  901. sizeof(WAVEFORMATEX));
  902. pWaveFormat = (WAVEFORMATEX*)GlobalAllocPtr(GHND, cbWaveFormat);
  903. if (pWaveFormat == NULL)
  904. goto ERROR_FILETOOLARGE;
  905. /*
  906. * set the size back to the actual size
  907. */
  908. cbWaveFormat = (WORD)ck.cksize;
  909. *ppWaveFormat = pWaveFormat;
  910. *pcbWaveFormat = cbWaveFormat;
  911. /* read the file format into <*pWaveFormat> */
  912. if (mmioRead(hmmio, (HPSTR)pWaveFormat, ck.cksize) != (long)ck.cksize)
  913. goto ERROR_READING; // truncated file, probably
  914. if (fCacheRIFF && !AddChunk(&ck,NULL,&gpcknHead,&gpcknTail))
  915. {
  916. goto ERROR_FILETOOLARGE;
  917. }
  918. //Sanity check for PCM Formats:
  919. if (pWaveFormat->wFormatTag == WAVE_FORMAT_PCM)
  920. {
  921. pWaveFormat->nBlockAlign = pWaveFormat->nChannels *
  922. ((pWaveFormat->wBitsPerSample + 7)/8);
  923. pWaveFormat->nAvgBytesPerSec = pWaveFormat->nBlockAlign *
  924. pWaveFormat->nSamplesPerSec;
  925. }
  926. fFMT = TRUE;
  927. fHandled = TRUE;
  928. break;
  929. case mmioFOURCC('d','a','t','a'):
  930. /* deal with the 'data' chunk */
  931. if (fDATA)
  932. break; /* we've been here before */
  933. if (!pWaveFormat)
  934. goto ERROR_READING;
  935. //*** is dwBlkAlignSize? Don't you want to use nBlkAlign
  936. //*** to determine this value?
  937. #if 0
  938. dwBlkAlignSize = ck.cksize;
  939. dwBlkAlignSize += (ck.cksize%pWaveFormat.nBlkAlign);
  940. *pcbWaveSamples = ck.cksize;
  941. #else
  942. dwBlkAlignSize = wfBytesToBytes(pWaveFormat, ck.cksize);
  943. #endif
  944. if ((pWaveSamples = GlobalAllocPtr(GHND | GMEM_SHARE
  945. , dwBlkAlignSize+4)) == NULL)
  946. goto ERROR_FILETOOLARGE;
  947. /* read the samples into the memory buffer */
  948. if (mmioRead(hmmio, (HPSTR)pWaveSamples, dwBlkAlignSize) !=
  949. (LONG)dwBlkAlignSize)
  950. goto ERROR_READING; // truncated file, probably
  951. if (fCacheRIFF && !AddChunk(&ck,NULL,&gpcknHead,&gpcknTail))
  952. {
  953. goto ERROR_FILETOOLARGE;
  954. }
  955. fDATA = TRUE;
  956. fHandled = TRUE;
  957. break;
  958. case mmioFOURCC('f','a','c','t'):
  959. /* deal with the 'fact' chunk */
  960. if (fFACT)
  961. break; /* we've been here before */
  962. #if 0
  963. //
  964. // There are some wave editors that are writing 'fact' chunks
  965. // after the data chunk, so we no longer make this assumption
  966. //
  967. if (fDATA)
  968. break; /* we describe some another 'data' chunk */
  969. #endif
  970. if (mmioRead(hmmio,(HPSTR)plWaveSamples, sizeof(DWORD))
  971. != sizeof(DWORD))
  972. goto ERROR_READING;
  973. if (fCacheRIFF && ck.cksize > sizeof(DWORD) &&
  974. ck.cksize < 0xffff)
  975. {
  976. spFact = (PFACT)GlobalAllocPtr(GHND,(UINT)(ck.cksize - sizeof(DWORD)));
  977. if (spFact == NULL)
  978. goto ERROR_FILETOOLARGE;
  979. scbFact = ck.cksize - sizeof(DWORD);
  980. if (mmioRead(hmmio,(HPSTR)spFact,scbFact) != scbFact)
  981. goto ERROR_READING;
  982. }
  983. /* we don't AddChunk() the 'fact' because we
  984. * write it out before we write our edit 'data'
  985. */
  986. fFACT = TRUE;
  987. fHandled = TRUE;
  988. break;
  989. #ifdef DISP
  990. case mmioFOURCC('d','i','s','p'):
  991. /* deal with the 'disp' chunk for clipboard transfer */
  992. // TODO:
  993. // DISP's are CF_DIB or CF_TEXT. Put 'em somewhere
  994. // global and pass them through as text or a BMP when
  995. // we copy to clipboard.
  996. //
  997. break;
  998. #endif /* DISP */
  999. case mmioFOURCC('L','I','S','T'):
  1000. if (fCacheRIFF)
  1001. {
  1002. /* seek back over the type field */
  1003. if (mmioSeek(hmmio,-4,SEEK_CUR) == -1)
  1004. goto ERROR_READING;
  1005. }
  1006. break;
  1007. default:
  1008. break;
  1009. }
  1010. /* the "default" case. */
  1011. if (fCacheRIFF && !fHandled)
  1012. {
  1013. HPBYTE hpData;
  1014. hpData = GlobalAllocPtr(GMEM_MOVEABLE, ck.cksize+4);
  1015. if (hpData == NULL)
  1016. {
  1017. goto ERROR_FILETOOLARGE;
  1018. }
  1019. /* read the data into the cache buffer */
  1020. if (mmioRead(hmmio, (HPSTR)hpData, ck.cksize) != (LONG) ck.cksize)
  1021. {
  1022. GlobalFreePtr(hpData);
  1023. goto ERROR_READING;// truncated file, probably
  1024. }
  1025. //
  1026. // Special case the copyright info. I'd rather do this than
  1027. // rewrite this whole app.
  1028. //
  1029. if (ck.ckid == mmioFOURCC('I','C','O','P'))
  1030. {
  1031. LPTSTR lpstr = GlobalAllocPtr(GHND, ck.cksize+4);
  1032. if (lpstr)
  1033. {
  1034. memcpy(lpstr, hpData, ck.cksize+4);
  1035. gpszInfo = lpstr;
  1036. }
  1037. }
  1038. if (!AddChunk(&ck,hpData,&gpcknHead, &gpcknTail))
  1039. {
  1040. goto ERROR_FILETOOLARGE;
  1041. }
  1042. }
  1043. mmioAscend( hmmio, &ck, 0 );
  1044. }
  1045. RETURN_FINISH:
  1046. if (fFMT && fDATA)
  1047. {
  1048. *plWaveSamples = wfBytesToSamples(pWaveFormat, dwBlkAlignSize);
  1049. *ppWaveSamples = pWaveSamples;
  1050. goto RETURN_SUCCESS;
  1051. }
  1052. /* goto ERROR_NOTAWAVEFILE; */
  1053. ERROR_NOTAWAVEFILE: // file is not a WAVE file
  1054. ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK,
  1055. IDS_APPTITLE, IDS_NOTAWAVEFILE, (LPTSTR) szFileName);
  1056. goto RETURN_ERROR;
  1057. ERROR_READING: // error reading from file
  1058. ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK,
  1059. IDS_APPTITLE, IDS_ERRORREAD, (LPTSTR) szFileName);
  1060. goto RETURN_ERROR;
  1061. ERROR_FILETOOLARGE: // out of memory
  1062. ErrorResBox(ghwndApp, ghInst, MB_ICONEXCLAMATION | MB_OK,
  1063. IDS_APPTITLE, IDS_FILETOOLARGE, (LPTSTR) szFileName);
  1064. goto RETURN_ERROR;
  1065. RETURN_ERROR:
  1066. if (pWaveSamples != NULL)
  1067. GlobalFreePtr(pWaveSamples), pWaveSamples = NULL;
  1068. if (fCacheRIFF)
  1069. FreeAllChunks(&gpcknHead, &gpcknTail);
  1070. mmr = MMSYSERR_ERROR;
  1071. RETURN_SUCCESS:
  1072. return mmr;
  1073. } /* ReadWaveFile */
  1074. /* fSuccess = AddChunk(lpCk, hpData)
  1075. *
  1076. * Adds to our linked list of chunk information.
  1077. *
  1078. * LPMMCKINFO lpCk | far pointer to the MMCKINFO describing the chunk.
  1079. * HPBYTE hpData | huge pointer to the data portion of the chunk, NULL if
  1080. * handled elsewhere.
  1081. *
  1082. * RETURNS: TRUE if added, FALSE if out of local heap.
  1083. */
  1084. static BOOL AddChunk(
  1085. LPMMCKINFO lpCk,
  1086. HPBYTE hpData,
  1087. PCKNODE * ppcknHead,
  1088. PCKNODE * ppcknTail)
  1089. {
  1090. PCKNODE pckn;
  1091. //
  1092. // create a node
  1093. //
  1094. pckn = (PCKNODE)GlobalAllocPtr(GHND,sizeof(CKNODE));
  1095. if (pckn == NULL)
  1096. {
  1097. DPF(TEXT("No Local Heap for Cache"));
  1098. return FALSE;
  1099. }
  1100. if (*ppcknHead == NULL)
  1101. {
  1102. *ppcknHead = pckn;
  1103. }
  1104. if (*ppcknTail != NULL)
  1105. {
  1106. (*ppcknTail)->psNext = pckn;
  1107. }
  1108. *ppcknTail = pckn;
  1109. pckn->ck.ckid = lpCk->ckid;
  1110. pckn->ck.fccType = lpCk->fccType;
  1111. pckn->ck.cksize = lpCk->cksize;
  1112. pckn->ck.dwDataOffset = lpCk->dwDataOffset;
  1113. pckn->hpData = hpData;
  1114. return TRUE;
  1115. } /* AddChunk() */
  1116. /* pckn = PCKNODE FreeHeadChunk(void)
  1117. *
  1118. * Frees up the Head chunk and return a pointer to the new Head.
  1119. * Uses global gpcknHead
  1120. *
  1121. * RETURNS: PCKNODE pointer to the Head chunk. NULL if no chunks in the list.
  1122. */
  1123. static PCKNODE FreeHeadChunk(
  1124. PCKNODE * ppcknHead)
  1125. {
  1126. PCKNODE pckn, pcknNext;
  1127. if (*ppcknHead == NULL)
  1128. {
  1129. goto SUCCESS;
  1130. }
  1131. pckn = *ppcknHead;
  1132. pcknNext = (*ppcknHead)->psNext;
  1133. if (pckn->hpData != NULL)
  1134. {
  1135. GlobalFreePtr(pckn->hpData);
  1136. }
  1137. GlobalFreePtr(pckn);
  1138. *ppcknHead = pcknNext;
  1139. SUCCESS:;
  1140. return *ppcknHead;
  1141. } /* FreeHeadChunk() */
  1142. /* void FreeAllChunks(void)
  1143. *
  1144. * Frees up the link list of chunk data.
  1145. *
  1146. * RETURNS: Nothing
  1147. */
  1148. static void FreeAllChunks(
  1149. PCKNODE * ppcknHead,
  1150. PCKNODE * ppcknTail)
  1151. {
  1152. PCKNODE pckn = *ppcknHead;
  1153. PCKNODE pcknNext = (*ppcknHead ? (*ppcknHead)->psNext : NULL);
  1154. DPF1(TEXT("Freeing All Chunks\n"));
  1155. while (FreeHeadChunk(ppcknHead));
  1156. if (scbFact > 0)
  1157. {
  1158. GlobalFreePtr(spFact);
  1159. scbFact = 0;
  1160. }
  1161. *ppcknHead = NULL;
  1162. *ppcknTail = NULL;
  1163. } /* FreeAllChunks() */
  1164. /* fSuccess = WriteWaveFile(hmmio, pWaveFormat, lWaveSamples)
  1165. *
  1166. * Write a WAVE file into <hmmio>. <*pWaveFormat> should be
  1167. * the WAVE file format and <lWaveSamples> should be the number of samples in
  1168. * the file. Return TRUE on success, FALSE on failure.
  1169. *
  1170. */
  1171. BOOL FAR PASCAL
  1172. WriteWaveFile(
  1173. HMMIO hmmio, // handle to open file
  1174. WAVEFORMATEX* pWaveFormat, // WAVE format
  1175. UINT cbWaveFormat, // size of WAVEFORMAT
  1176. HPBYTE pWaveSamples, // waveform samples
  1177. LONG lWaveSamples) // number of samples
  1178. {
  1179. MMCKINFO ckRIFF; // chunk info. for RIFF chunk
  1180. MMCKINFO ck; // info. for a chunk file
  1181. PCKNODE pckn = gpcknHead;
  1182. LONG cbWaveSamples;
  1183. MMRESULT mmr;
  1184. /* create the RIFF chunk of form type 'WAVE' */
  1185. ckRIFF.fccType = mmioFOURCC('W', 'A', 'V', 'E');
  1186. ckRIFF.cksize = 0L; // let MMIO figure out ck. size
  1187. mmr = mmioCreateChunk(hmmio, &ckRIFF, MMIO_CREATERIFF);
  1188. if (mmr != MMSYSERR_NOERROR)
  1189. goto wwferror;
  1190. if (pckn != NULL)
  1191. {
  1192. /* ForEach node in the linked list of chunks,
  1193. * Write out the corresponding data chunk OR
  1194. * the global edit data.
  1195. */
  1196. do {
  1197. ck.cksize = 0L;
  1198. ck.ckid = pckn->ck.ckid;
  1199. ck.fccType = pckn->ck.fccType;
  1200. if (pckn->hpData == NULL)
  1201. {
  1202. /* This must be a data-type we have in edit
  1203. * buffers. We should preserve the original
  1204. * order.
  1205. */
  1206. switch (pckn->ck.ckid)
  1207. {
  1208. case mmioFOURCC('f','m','t',' '):
  1209. mmr = mmioCreateChunk(hmmio, &ck, 0);
  1210. if (mmr != MMSYSERR_NOERROR)
  1211. goto wwferror;
  1212. if (mmioWrite(hmmio, (LPSTR) pWaveFormat, cbWaveFormat)
  1213. != (long)cbWaveFormat)
  1214. goto wwfwriteerror;
  1215. mmr = mmioAscend(hmmio, &ck, 0);
  1216. if (mmr != MMSYSERR_NOERROR)
  1217. goto wwferror;
  1218. break;
  1219. case mmioFOURCC('d','a','t','a'):
  1220. /* Insert a 'fact' chunk here */
  1221. /* 'fact' should always preceed the 'data' it
  1222. * describes.
  1223. */
  1224. ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
  1225. mmr = mmioCreateChunk(hmmio, &ck, 0);
  1226. if (mmr != MMSYSERR_NOERROR)
  1227. goto wwferror;
  1228. if (mmioWrite(hmmio, (LPSTR) &lWaveSamples,
  1229. sizeof(lWaveSamples)) != sizeof(lWaveSamples))
  1230. goto wwfwriteerror;
  1231. if (scbFact > 4)
  1232. {
  1233. if ( mmioWrite(hmmio, (LPSTR)spFact, scbFact)
  1234. != scbFact )
  1235. goto wwfwriteerror;
  1236. }
  1237. mmr = mmioAscend(hmmio, &ck, 0);
  1238. if (mmr != MMSYSERR_NOERROR)
  1239. goto wwferror;
  1240. ck.cksize = 0L;
  1241. ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  1242. mmr = mmioCreateChunk(hmmio, &ck, 0);
  1243. if (mmr != MMSYSERR_NOERROR)
  1244. goto wwferror;
  1245. cbWaveSamples = wfSamplesToBytes(pWaveFormat,
  1246. lWaveSamples);
  1247. if (cbWaveSamples)
  1248. {
  1249. /* write the waveform samples */
  1250. if (mmioWrite(hmmio, (LPSTR)pWaveSamples
  1251. , cbWaveSamples)
  1252. != cbWaveSamples)
  1253. return FALSE;
  1254. }
  1255. mmr = mmioAscend(hmmio, &ck, 0);
  1256. if (mmr != MMSYSERR_NOERROR)
  1257. goto wwferror;
  1258. break;
  1259. #ifdef DISP
  1260. case mmioFOURCC('d','i','s','p'):
  1261. /* deal with writing the 'disp' chunk */
  1262. break;
  1263. #endif /* DISP */
  1264. case mmioFOURCC('f','a','c','t'):
  1265. /* deal with the 'fact' chunk */
  1266. /* skip it. We always write it before the 'data' */
  1267. break;
  1268. default:
  1269. /* This should never happen.*/
  1270. return FALSE;
  1271. }
  1272. }
  1273. else
  1274. {
  1275. /* generic case */
  1276. mmr = mmioCreateChunk(hmmio,&ck,0);
  1277. if (mmr != MMSYSERR_NOERROR)
  1278. goto wwferror;
  1279. if (mmioWrite(hmmio,(LPSTR)pckn->hpData,pckn->ck.cksize)
  1280. != (long) pckn->ck.cksize)
  1281. goto wwfwriteerror;
  1282. mmr = mmioAscend(hmmio, &ck, 0);
  1283. if (mmr != MMSYSERR_NOERROR)
  1284. goto wwferror;
  1285. }
  1286. } while (pckn = pckn->psNext);
  1287. }
  1288. else
  1289. {
  1290. /* <hmmio> is now descended into the 'RIFF' chunk -- create the
  1291. * 'fmt' chunk and write <*pWaveFormat> into it
  1292. */
  1293. ck.ckid = mmioFOURCC('f', 'm', 't', ' ');
  1294. ck.cksize = cbWaveFormat;
  1295. mmr = mmioCreateChunk(hmmio, &ck, 0);
  1296. if (mmr != MMSYSERR_NOERROR)
  1297. goto wwferror;
  1298. if (mmioWrite(hmmio, (LPSTR) pWaveFormat, cbWaveFormat) !=
  1299. (long)cbWaveFormat)
  1300. goto wwfwriteerror;
  1301. /* ascend out of the 'fmt' chunk, back into 'RIFF' chunk */
  1302. mmr = mmioAscend(hmmio, &ck, 0);
  1303. if (mmr != MMSYSERR_NOERROR)
  1304. goto wwferror;
  1305. /* write out the number of samples in the 'FACT' chunk */
  1306. ck.ckid = mmioFOURCC('f', 'a', 'c', 't');
  1307. mmr = mmioCreateChunk(hmmio, &ck, 0);
  1308. if (mmr != MMSYSERR_NOERROR)
  1309. goto wwferror;
  1310. if (mmioWrite(hmmio, (LPSTR)&lWaveSamples, sizeof(lWaveSamples))
  1311. != sizeof(lWaveSamples))
  1312. return FALSE;
  1313. /* ascend out of the 'fact' chunk, back into 'RIFF' chunk */
  1314. mmr = mmioAscend(hmmio, &ck, 0);
  1315. if (mmr != MMSYSERR_NOERROR)
  1316. goto wwferror;
  1317. /* create the 'data' chunk that holds the waveform samples */
  1318. ck.ckid = mmioFOURCC('d', 'a', 't', 'a');
  1319. ck.cksize = 0L; // let MMIO figure out ck. size
  1320. mmr = mmioCreateChunk(hmmio, &ck, 0);
  1321. if (mmr != MMSYSERR_NOERROR)
  1322. goto wwferror;
  1323. cbWaveSamples = wfSamplesToBytes(pWaveFormat,lWaveSamples);
  1324. /* write the waveform samples */
  1325. if (cbWaveSamples)
  1326. {
  1327. if (mmioWrite(hmmio, (LPSTR)pWaveSamples, cbWaveSamples)
  1328. != cbWaveSamples)
  1329. goto wwfwriteerror;
  1330. }
  1331. /* ascend the file out of the 'data' chunk, back into
  1332. * the 'RIFF' chunk -- this will cause the chunk size of the 'data'
  1333. * chunk to be written
  1334. */
  1335. mmr = mmioAscend(hmmio, &ck, 0);
  1336. if (mmr != MMSYSERR_NOERROR)
  1337. goto wwferror;
  1338. }
  1339. /* ascend the file out of the 'RIFF' chunk */
  1340. mmr = mmioAscend(hmmio, &ckRIFF, 0);
  1341. if (mmr != MMSYSERR_NOERROR)
  1342. goto wwferror;
  1343. /* done */
  1344. return TRUE;
  1345. wwferror:
  1346. #if DBG
  1347. {
  1348. TCHAR sz[256];
  1349. wsprintf(sz, TEXT("WriteWaveFile: Error %lx\r\n"), mmr);
  1350. OutputDebugString(sz);
  1351. DebugBreak();
  1352. }
  1353. #endif
  1354. return FALSE;
  1355. wwfwriteerror:
  1356. #if DBG
  1357. {
  1358. TCHAR sz[256];
  1359. wsprintf(sz,TEXT("Write Error! ckid = %04x\r\n"), (DWORD)ck.ckid);
  1360. OutputDebugString(sz);
  1361. DebugBreak();
  1362. }
  1363. #endif
  1364. return FALSE;
  1365. } /* WriteWaveFile */