Leaked source code of windows server 2003
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.

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