1 /*
2 * PortBurn
3 * Windows XP IMAPI v2 implementation
4 *
5 * Authors:
6 * Leland Lucius
7 *
8 * License: LGPL
9 */
10
11 #include "portburn.h"
12
13 #define _WIN32_WINNT 0x0400
14
15 #include <windows.h>
16 #include <tchar.h>
17 #include <malloc.h>
18 #include <imapi2.h>
19 #include <imapi2error.h>
20 #include <Objidl.h>
21 #include <ObjBase.h>
22 #include <stdio.h>
23
24 #define DEBUG(a) printf a
25
26 typedef struct {
27
28 // Used for duration of portburn open session
29
30 BSTR client; // Things that must be cleaned up at close
31
32 HRESULT hres;
33 bool verify;
34 bool test;
35 bool underrun;
36 bool eject;
37 bool gapless;
38 int speed;
39
40 // Used for duration of device open session
41
42 IStorage *pStorage; //
43 IStream *pMarshall; //
44 IStream *pStream; // Things that must be cleaned up at close
45 HANDLE hThread; //
46 BSTR diskid; //
47
48 float fraction;
49 int curtrack;
50 int threadres;
51 bool burning;
52 bool erasing;
53 bool cancel;
54 VARIANT_BOOL fullerase;
55 } PBHandlev2;
56
57 /// ==================================================================
58 /// ==================================================================
59 /// ==================================================================
60 /// ==================================================================
61 /// ==================================================================
62
63 class WriteEventsTAO:public DDiscFormat2TrackAtOnceEvents
64 {
65 public:
WriteEventsTAO(PBHandlev2 * h)66 WriteEventsTAO(PBHandlev2 *h)
67 {
68 LPTYPELIB pTypeLib;
69 HRESULT hres;
70
71 mH = h;
72 mRefs = 0;
73 mCookie = -1;
74 mConnectionPoint = NULL;
75 mConnectionPointContainer = NULL;
76 mTypeInfo = NULL;
77 mAdjust = 0;
78
79 hres = LoadRegTypeLib(LIBID_IMAPILib2,
80 IMAPILib2_MajorVersion,
81 IMAPILib2_MinorVersion,
82 LOCALE_SYSTEM_DEFAULT,
83 &pTypeLib);
84 if (FAILED(hres)) {
85 return;
86 }
87
88 hres = pTypeLib->GetTypeInfoOfGuid(__uuidof(DDiscFormat2TrackAtOnceEvents), &mTypeInfo);
89
90 pTypeLib->Release();
91
92 if (FAILED(hres)) {
93 return;
94 }
95
96 AddRef();
97 }
98
~WriteEventsTAO()99 ~WriteEventsTAO()
100 {
101 Disconnect();
102
103 if (mTypeInfo != NULL) {
104 mTypeInfo->Release();
105 }
106 }
107
Connect(IUnknown * pConnectTo)108 void Connect(IUnknown *pConnectTo)
109 {
110 HRESULT hres;
111
112 hres = pConnectTo->QueryInterface(__uuidof(IConnectionPointContainer),
113 (void **)&mConnectionPointContainer);
114 if (FAILED(hres)) {
115 return;
116 }
117
118 hres = mConnectionPointContainer->FindConnectionPoint(__uuidof(DDiscFormat2TrackAtOnceEvents),
119 &mConnectionPoint);
120 if (FAILED(hres)) {
121 Disconnect();
122 return;
123 }
124
125 hres = mConnectionPoint->Advise((IUnknown*)this, &mCookie);
126 if (FAILED(hres)) {
127 Disconnect();
128 return;
129 }
130 }
131
Disconnect()132 void Disconnect()
133 {
134 if (mCookie != -1) {
135 mConnectionPoint->Unadvise(mCookie);
136 mCookie = -1;
137 }
138
139 if (mConnectionPoint != NULL) {
140 mConnectionPoint->Release();
141 mConnectionPoint = NULL;
142 }
143
144 if (mConnectionPointContainer != NULL) {
145 mConnectionPointContainer->Release();
146 mConnectionPointContainer = NULL;
147 }
148 }
149
150 // IUnknown
151
QueryInterface(REFIID riid,LPVOID * ppv)152 STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
153 {
154 if (ppv == NULL) {
155 return E_POINTER;
156 }
157
158 *ppv = NULL;
159
160 if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DDiscFormat2TrackAtOnceEvents) {
161 *ppv = this;
162 AddRef();
163 return S_OK;
164 }
165
166 return E_NOINTERFACE;
167 }
168
AddRef(VOID)169 STDMETHODIMP_(ULONG) AddRef(VOID)
170 {
171 return InterlockedIncrement((LONG*) &mRefs);
172 }
173
Release(VOID)174 STDMETHODIMP_(ULONG) Release(VOID)
175 {
176 ULONG ref = InterlockedDecrement((LONG*) &mRefs);
177
178 if (ref == 0) {
179 delete this;
180 }
181
182 return ref;
183 }
184
185 // IDispatch
186
GetTypeInfoCount(UINT * pctinfo)187 STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
188 {
189 *pctinfo = 1;
190 return S_OK;
191 }
192
GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)193 STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
194 {
195 if (ppTInfo == NULL) {
196 return E_POINTER;
197 }
198
199 if (iTInfo != 0) {
200 return DISP_E_BADINDEX;
201 }
202
203 mTypeInfo->AddRef();
204
205 *ppTInfo = mTypeInfo;
206
207 return S_OK;
208 }
209
GetIDsOfNames(REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)210 STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
211 {
212 return DispGetIDsOfNames(mTypeInfo, rgszNames, cNames, rgDispId);
213 }
214
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)215 STDMETHODIMP Invoke(DISPID dispIdMember,
216 REFIID riid,
217 LCID lcid,
218 WORD wFlags,
219 DISPPARAMS *pDispParams,
220 VARIANT *pVarResult,
221 EXCEPINFO *pExcepInfo,
222 UINT *puArgErr)
223 {
224 return DispInvoke(this,
225 mTypeInfo,
226 dispIdMember,
227 wFlags,
228 pDispParams,
229 pVarResult,
230 pExcepInfo,
231 puArgErr);
232 }
233
234 // DDiscFormat2WriteEvents
235
Update(IDispatch * object,IDispatch * progress)236 STDMETHODIMP Update(IDispatch *object, IDispatch *progress)
237 {
238 IDiscFormat2TrackAtOnceEventArgs *pEventArgs;
239 IMAPI_FORMAT2_TAO_WRITE_ACTION action;
240 LONG track = -1;
241 LONG elapsed = -1;
242 LONG remaining = -1;
243 LONG sectors = -1;
244 LONG start = -1;
245 LONG last = -1;
246 HRESULT hres;
247
248 hres = progress->QueryInterface(IID_PPV_ARGS(&pEventArgs));
249 if (SUCCEEDED(hres)) {
250 float fraction;
251
252 hres = pEventArgs->get_CurrentTrackNumber(&track);
253 hres = pEventArgs->get_CurrentAction(&action);
254 hres = pEventArgs->get_ElapsedTime(&elapsed);
255 hres = pEventArgs->get_RemainingTime(&remaining);
256 // need to figure out best progress indicators
257 hres = pEventArgs->get_SectorCount(§ors);
258 hres = pEventArgs->get_StartLba(&start);
259 hres = pEventArgs->get_LastWrittenLba(&last);
260
261 fraction = (float) (last - start) / (float) sectors;
262
263 printf("track %d action = %d elapsed %d remaining %d\n", track, action, elapsed, remaining);
264
265 // fraction = (elapsed + mAdjust) / (float) (remaining + elapsed + mAdjust);
266
267 // Never set fraction to 1.0. Screws up synchro between thread and user.
268 if (fraction >= 1.0) {
269 fraction = (float) 0.99;
270 }
271
272 mH->fraction = fraction;
273
274 pEventArgs->Release();
275 }
276
277 if (mH->cancel) {
278 IDiscFormat2TrackAtOnce *pDiscFormat;
279
280 hres = object->QueryInterface(IID_PPV_ARGS(&pDiscFormat));
281 if (SUCCEEDED(hres)) {
282
283 hres = pDiscFormat->CancelAddTrack();
284 if (SUCCEEDED(hres)) {
285 mH->cancel = false;
286 }
287
288 pDiscFormat->Release();
289
290 return S_OK;
291 }
292 }
293
294 return S_OK;
295 }
296
297 private:
298 PBHandlev2 *mH;
299 LPTYPEINFO mTypeInfo;
300 ULONG mRefs;
301 DWORD mCookie;
302 IConnectionPoint *mConnectionPoint;
303 IConnectionPointContainer *mConnectionPointContainer;
304 int mAdjust;
305 };
306
307 /* Track At Once burning thread */
PortBurn_v2_RecordDiscTAO(LPVOID lpParam)308 DWORD WINAPI PortBurn_v2_RecordDiscTAO(LPVOID lpParam)
309 {
310 PBHandlev2 *h = (PBHandlev2 *) lpParam;
311 IDiscRecorder2 *pDiscRecorder = NULL;
312 IDiscFormat2TrackAtOnce *pDiscFormat = NULL;
313 WriteEventsTAO *pWriteEvents = NULL;
314 IStorage *pStorage = NULL;
315 IMalloc *pMalloc = NULL;
316 IEnumSTATSTG *pEnum = NULL;
317 bool prepared = false;
318
319 h->cancel = false;
320 h->fraction = 0.0;
321
322 h->hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
323 if (FAILED(h->hres)) {
324 h->threadres = pbErrBurnFailed;
325 h->fraction = 1.0;
326 return S_OK;
327 }
328
329 h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
330 NULL,
331 CLSCTX_ALL,
332 IID_PPV_ARGS(&pDiscRecorder));
333 if (FAILED(h->hres)) {
334 goto done;
335 }
336
337 h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
338 if (FAILED(h->hres)) {
339 goto done;
340 }
341
342 h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2TrackAtOnce),
343 NULL,
344 CLSCTX_ALL,
345 IID_PPV_ARGS(&pDiscFormat));
346 if (FAILED(h->hres)) {
347 goto done;
348 }
349
350 h->hres = pDiscFormat->put_ClientName(h->client);
351 if (FAILED(h->hres)) {
352 goto done;
353 }
354
355 h->hres = pDiscFormat->put_Recorder(pDiscRecorder);
356 if (FAILED(h->hres)) {
357 goto done;
358 }
359
360 pWriteEvents = new WriteEventsTAO(h);
361 if (pWriteEvents == NULL) {
362 goto done;
363 }
364
365 pWriteEvents->Connect(pDiscFormat);
366
367 h->hres = pDiscFormat->PrepareMedia();
368 if (FAILED(h->hres)) {
369 goto done;
370 }
371 prepared = true;
372
373 VARIANT_BOOL underrun = (h->underrun ? VARIANT_FALSE : VARIANT_TRUE);
374 h->hres = pDiscFormat->put_BufferUnderrunFreeDisabled(underrun);
375 if (FAILED(h->hres)) {
376 goto done;
377 }
378
379 h->hres = CoGetMalloc(1, &pMalloc);
380 if (FAILED(h->hres)) {
381 goto done;
382 }
383
384 h->hres = CoGetInterfaceAndReleaseStream(h->pMarshall,
385 __uuidof(IStorage),
386 (void **) &pStorage);
387 h->pMarshall = NULL; // stream is always released regardless of failure
388 if (FAILED(h->hres)) {
389 goto done;
390 }
391
392 h->hres = pStorage->EnumElements(0, NULL, NULL, &pEnum);
393 if (FAILED(h->hres)) {
394 goto done;
395 }
396
397 STATSTG stat;
398 ULONG fetched;
399 h->hres = pEnum->Next(1, &stat, &fetched);
400
401 while (h->hres == S_OK) {
402 IStream *pStream;
403
404 if (stat.pwcsName == NULL) {
405 h->hres = E_POINTER;
406 goto done;
407 }
408
409 _tprintf(L"stat name = %s\n", stat.pwcsName);
410 h->hres = pStorage->OpenStream(stat.pwcsName,
411 NULL,
412 STGM_SHARE_EXCLUSIVE |
413 STGM_READ,
414 NULL,
415 &pStream);
416
417 pMalloc->Free(stat.pwcsName);
418
419 if (FAILED(h->hres)) {
420 goto done;
421 }
422
423 h->hres = pDiscFormat->AddAudioTrack(pStream);
424
425 pStream->Release();
426 pStream = NULL;
427
428 if (FAILED(h->hres)) {
429 goto done;
430 }
431
432 h->hres = pEnum->Next(1, &stat, &fetched);
433 }
434
435 h->hres = pDiscFormat->ReleaseMedia();
436 prepared = false;
437
438 done:
439
440 if (FAILED(h->hres)) {
441 h->threadres = pbErrBurnFailed;
442 }
443 else {
444 h->threadres = pbSuccess;
445 }
446
447 if (prepared) {
448 pDiscFormat->ReleaseMedia();
449 }
450
451 if (pMalloc != NULL) {
452 pMalloc->Release();
453 }
454
455 if (pEnum != NULL) {
456 pEnum->Release();
457 }
458
459 if (pStorage != NULL) {
460 pStorage->Release();
461 }
462
463 if (pWriteEvents != NULL) {
464 pWriteEvents->Disconnect();
465 pWriteEvents->Release();
466 }
467
468 if (pDiscFormat != NULL) {
469 pDiscFormat->put_Recorder(NULL);
470 pDiscFormat->put_ClientName(NULL);
471 pDiscFormat->Release();
472 }
473
474 if (pDiscRecorder != NULL) {
475 if (h->eject) {
476 pDiscRecorder->EjectMedia();
477 }
478
479 pDiscRecorder->Release();
480 }
481
482 CoUninitialize();
483
484 h->fraction = 1.0;
485
486 return S_OK;
487 }
488
489 /// ==================================================================
490 /// ==================================================================
491 /// ==================================================================
492 /// ==================================================================
493 /// ==================================================================
494
495 class WriteEventsDAO:public DDiscFormat2RawCDEvents
496 {
497 public:
WriteEventsDAO(PBHandlev2 * h)498 WriteEventsDAO(PBHandlev2 *h)
499 {
500 LPTYPELIB pTypeLib;
501 HRESULT hres;
502
503 mH = h;
504 mRefs = 0;
505 mCookie = -1;
506 mConnectionPoint = NULL;
507 mConnectionPointContainer = NULL;
508 mTypeInfo = NULL;
509 mAdjust = 0;
510
511 hres = LoadRegTypeLib(LIBID_IMAPILib2,
512 IMAPILib2_MajorVersion,
513 IMAPILib2_MinorVersion,
514 LOCALE_SYSTEM_DEFAULT,
515 &pTypeLib);
516 if (FAILED(hres)) {
517 return;
518 }
519
520 hres = pTypeLib->GetTypeInfoOfGuid(__uuidof(DDiscFormat2RawCDEvents), &mTypeInfo);
521
522 pTypeLib->Release();
523
524 if (FAILED(hres)) {
525 return;
526 }
527
528 AddRef();
529 }
530
~WriteEventsDAO()531 ~WriteEventsDAO()
532 {
533 Disconnect();
534
535 if (mTypeInfo != NULL) {
536 mTypeInfo->Release();
537 }
538 }
539
Connect(IUnknown * pConnectTo)540 void Connect(IUnknown *pConnectTo)
541 {
542 HRESULT hres;
543
544 hres = pConnectTo->QueryInterface(__uuidof(IConnectionPointContainer),
545 (void **)&mConnectionPointContainer);
546 if (FAILED(hres)) {
547 return;
548 }
549
550 hres = mConnectionPointContainer->FindConnectionPoint(__uuidof(DDiscFormat2RawCDEvents),
551 &mConnectionPoint);
552 if (FAILED(hres)) {
553 Disconnect();
554 return;
555 }
556
557 hres = mConnectionPoint->Advise((IUnknown*)this, &mCookie);
558 if (FAILED(hres)) {
559 Disconnect();
560 return;
561 }
562 }
563
Disconnect()564 void Disconnect()
565 {
566 if (mCookie != -1) {
567 mConnectionPoint->Unadvise(mCookie);
568 mCookie = -1;
569 }
570
571 if (mConnectionPoint != NULL) {
572 mConnectionPoint->Release();
573 mConnectionPoint = NULL;
574 }
575
576 if (mConnectionPointContainer != NULL) {
577 mConnectionPointContainer->Release();
578 mConnectionPointContainer = NULL;
579 }
580 }
581
582 // IUnknown
583
QueryInterface(REFIID riid,LPVOID * ppv)584 STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
585 {
586 if (ppv == NULL) {
587 return E_POINTER;
588 }
589
590 *ppv = NULL;
591
592 if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DDiscFormat2RawCDEvents) {
593 *ppv = this;
594 AddRef();
595 return S_OK;
596 }
597
598 return E_NOINTERFACE;
599 }
600
AddRef(VOID)601 STDMETHODIMP_(ULONG) AddRef(VOID)
602 {
603 return InterlockedIncrement((LONG*) &mRefs);
604 }
605
Release(VOID)606 STDMETHODIMP_(ULONG) Release(VOID)
607 {
608 ULONG ref = InterlockedDecrement((LONG*) &mRefs);
609
610 if (ref == 0) {
611 delete this;
612 }
613
614 return ref;
615 }
616
617 // IDispatch
618
GetTypeInfoCount(UINT * pctinfo)619 STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
620 {
621 *pctinfo = 1;
622 return S_OK;
623 }
624
GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)625 STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
626 {
627 if (ppTInfo == NULL) {
628 return E_POINTER;
629 }
630
631 if (iTInfo != 0) {
632 return DISP_E_BADINDEX;
633 }
634
635 mTypeInfo->AddRef();
636
637 *ppTInfo = mTypeInfo;
638
639 return S_OK;
640 }
641
GetIDsOfNames(REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)642 STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
643 {
644 return DispGetIDsOfNames(mTypeInfo, rgszNames, cNames, rgDispId);
645 }
646
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)647 STDMETHODIMP Invoke(DISPID dispIdMember,
648 REFIID riid,
649 LCID lcid,
650 WORD wFlags,
651 DISPPARAMS *pDispParams,
652 VARIANT *pVarResult,
653 EXCEPINFO *pExcepInfo,
654 UINT *puArgErr)
655 {
656 return DispInvoke(this,
657 mTypeInfo,
658 dispIdMember,
659 wFlags,
660 pDispParams,
661 pVarResult,
662 pExcepInfo,
663 puArgErr);
664 }
665
666 // DDiscFormat2RawCDEvents
667
Update(IDispatch * object,IDispatch * progress)668 STDMETHODIMP Update(IDispatch *object, IDispatch *progress)
669 {
670 IDiscFormat2RawCDEventArgs *pEventArgs;
671 LONG sectors = -1;
672 LONG start = -1;
673 LONG last = -1;
674 HRESULT hres;
675
676 hres = progress->QueryInterface(IID_PPV_ARGS(&pEventArgs));
677 if (SUCCEEDED(hres)) {
678 float fraction;
679
680 hres = pEventArgs->get_SectorCount(§ors);
681 hres = pEventArgs->get_StartLba(&start);
682 hres = pEventArgs->get_LastWrittenLba(&last);
683
684 fraction = (float) (last - start) / (float) sectors;
685
686 // printf("sectors %d startlba %d lastlba %d frac %f\n", sectors, start, last, fraction);
687
688 // Never set fraction to 1.0. Screws up synchro between thread and user.
689 if (fraction >= 1.0) {
690 fraction = (float) 0.99;
691 }
692
693 mH->fraction = fraction;
694
695 pEventArgs->Release();
696 }
697
698 if (mH->cancel) {
699 IDiscFormat2RawCD *pDiscFormat;
700
701 hres = object->QueryInterface(IID_PPV_ARGS(&pDiscFormat));
702 if (SUCCEEDED(hres)) {
703
704 hres = pDiscFormat->CancelWrite();
705 if (SUCCEEDED(hres)) {
706 mH->cancel = false;
707 }
708
709 pDiscFormat->Release();
710
711 return S_OK;
712 }
713 }
714
715 return S_OK;
716 }
717
718 private:
719 PBHandlev2 *mH;
720 LPTYPEINFO mTypeInfo;
721 ULONG mRefs;
722 DWORD mCookie;
723 IConnectionPoint *mConnectionPoint;
724 IConnectionPointContainer *mConnectionPointContainer;
725 int mAdjust;
726 };
727
728 /* Track At Once burning thread */
PortBurn_v2_RecordDiscDAO(LPVOID lpParam)729 DWORD WINAPI PortBurn_v2_RecordDiscDAO(LPVOID lpParam)
730 {
731 PBHandlev2 *h = (PBHandlev2 *) lpParam;
732 IDiscRecorder2 *pDiscRecorder = NULL;
733 IDiscFormat2RawCD *pDiscFormat = NULL;
734 IRawCDImageCreator *pDiscImage = NULL;
735 WriteEventsDAO *pWriteEvents = NULL;
736 IStorage *pStorage = NULL;
737 IMalloc *pMalloc = NULL;
738 IEnumSTATSTG *pEnum = NULL;
739 bool prepared = false;
740
741 h->cancel = false;
742 h->fraction = 0.0;
743
744 h->hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
745 if (FAILED(h->hres)) {
746 h->threadres = pbErrBurnFailed;
747 h->fraction = 1.0;
748 return S_OK;
749 }
750
751 h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
752 NULL,
753 CLSCTX_ALL,
754 IID_PPV_ARGS(&pDiscRecorder));
755 if (FAILED(h->hres)) {
756 goto done;
757 }
758
759 h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
760 if (FAILED(h->hres)) {
761 goto done;
762 }
763
764 h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2RawCD),
765 NULL,
766 CLSCTX_ALL,
767 IID_PPV_ARGS(&pDiscFormat));
768 if (FAILED(h->hres)) {
769 goto done;
770 }
771
772 h->hres = pDiscFormat->put_ClientName(h->client);
773 if (FAILED(h->hres)) {
774 goto done;
775 }
776
777 h->hres = pDiscFormat->put_Recorder(pDiscRecorder);
778 if (FAILED(h->hres)) {
779 goto done;
780 }
781
782 pWriteEvents = new WriteEventsDAO(h);
783 if (pWriteEvents == NULL) {
784 goto done;
785 }
786
787 pWriteEvents->Connect(pDiscFormat);
788
789 h->hres = pDiscFormat->PrepareMedia();
790 if (FAILED(h->hres)) {
791 goto done;
792 }
793 prepared = true;
794
795 VARIANT_BOOL underrun = (h->underrun ? VARIANT_FALSE : VARIANT_TRUE);
796 h->hres = pDiscFormat->put_BufferUnderrunFreeDisabled(underrun);
797 if (FAILED(h->hres)) {
798 goto done;
799 }
800
801 h->hres = CoCreateInstance(__uuidof(MsftRawCDImageCreator),
802 NULL,
803 CLSCTX_ALL,
804 IID_PPV_ARGS(&pDiscImage));
805 if (FAILED(h->hres)) {
806 goto done;
807 }
808
809 VARIANT_BOOL gapless = (h->gapless ? VARIANT_FALSE : VARIANT_TRUE);
810 h->hres = pDiscImage->put_DisableGaplessAudio(gapless);
811 if (FAILED(h->hres)) {
812 goto done;
813 }
814
815 h->hres = CoGetMalloc(1, &pMalloc);
816 if (FAILED(h->hres)) {
817 goto done;
818 }
819
820 h->hres = CoGetInterfaceAndReleaseStream(h->pMarshall,
821 __uuidof(IStorage),
822 (void **) &pStorage);
823 h->pMarshall = NULL; // stream is always released regardless of failure
824 if (FAILED(h->hres)) {
825 goto done;
826 }
827
828 h->hres = pStorage->EnumElements(0, NULL, NULL, &pEnum);
829 if (FAILED(h->hres)) {
830 goto done;
831 }
832
833 STATSTG stat;
834 ULONG fetched;
835 h->hres = pEnum->Next(1, &stat, &fetched);
836
837 while (h->hres == S_OK) {
838 IStream *pStream;
839
840 if (stat.pwcsName == NULL) {
841 h->hres = E_POINTER;
842 goto done;
843 }
844
845 _tprintf(L"stat name = %s\n", stat.pwcsName);
846 h->hres = pStorage->OpenStream(stat.pwcsName,
847 NULL,
848 STGM_SHARE_EXCLUSIVE |
849 STGM_READ,
850 NULL,
851 &pStream);
852
853 pMalloc->Free(stat.pwcsName);
854
855 if (FAILED(h->hres)) {
856 goto done;
857 }
858
859 LONG tindex = 0;
860 h->hres = pDiscImage->AddTrack(IMAPI_CD_SECTOR_AUDIO, pStream, &tindex);
861
862 pStream->Release();
863 pStream = NULL;
864
865 if (FAILED(h->hres)) {
866 goto done;
867 }
868
869 h->hres = pEnum->Next(1, &stat, &fetched);
870 }
871
872 IStream *pStream;
873 h->hres = pDiscImage->CreateResultImage(&pStream);
874 if (SUCCEEDED(h->hres)) {
875 h->hres = pDiscFormat->WriteMedia(pStream);
876
877 pStream->Release();
878 }
879
880 if (FAILED(h->hres)) {
881 goto done;
882 }
883
884 h->hres = pDiscFormat->ReleaseMedia();
885 prepared = false;
886
887 done:
888
889 if (FAILED(h->hres)) {
890 h->threadres = pbErrBurnFailed;
891 }
892 else {
893 h->threadres = pbSuccess;
894 }
895
896 if (prepared) {
897 pDiscFormat->ReleaseMedia();
898 }
899
900 if (pMalloc != NULL) {
901 pMalloc->Release();
902 }
903
904 if (pEnum != NULL) {
905 pEnum->Release();
906 }
907
908 if (pStorage != NULL) {
909 pStorage->Release();
910 }
911
912 if (pDiscImage != NULL) {
913 pDiscImage->Release();
914 }
915
916 if (pWriteEvents != NULL) {
917 pWriteEvents->Disconnect();
918 pWriteEvents->Release();
919 }
920
921 if (pDiscFormat != NULL) {
922 pDiscFormat->put_Recorder(NULL);
923 pDiscFormat->put_ClientName(NULL);
924 pDiscFormat->Release();
925 }
926
927 if (pDiscRecorder != NULL) {
928 if (h->eject) {
929 pDiscRecorder->EjectMedia();
930 }
931
932 pDiscRecorder->Release();
933 }
934
935 CoUninitialize();
936
937 h->fraction = 1.0;
938
939 return S_OK;
940 }
941
942 /// ==================================================================
943 /// ==================================================================
944 /// ==================================================================
945 /// ==================================================================
946 /// ==================================================================
947
948 class EraseEvents:public DDiscFormat2EraseEvents
949 {
950 public:
EraseEvents(PBHandlev2 * h)951 EraseEvents(PBHandlev2 *h)
952 {
953 LPTYPELIB pTypeLib;
954 HRESULT hres;
955
956 mH = h;
957 mRefs = 0;
958 mCookie = -1;
959 mConnectionPoint = NULL;
960 mConnectionPointContainer = NULL;
961
962 hres = LoadRegTypeLib(LIBID_IMAPILib2,
963 IMAPILib2_MajorVersion,
964 IMAPILib2_MinorVersion,
965 LOCALE_SYSTEM_DEFAULT,
966 &pTypeLib);
967 if (FAILED(hres)) {
968 return;
969 }
970
971 hres = pTypeLib->GetTypeInfoOfGuid(IID_DDiscFormat2EraseEvents, &mTypeInfo);
972
973 pTypeLib->Release();
974
975 if (FAILED(hres)) {
976 return;
977 }
978
979 AddRef();
980 }
981
~EraseEvents()982 ~EraseEvents()
983 {
984 Disconnect();
985
986 if (mTypeInfo != NULL) {
987 mTypeInfo->Release();
988 }
989 }
990
Connect(IUnknown * pConnectTo)991 void Connect(IUnknown *pConnectTo)
992 {
993 HRESULT hres;
994
995 hres = pConnectTo->QueryInterface(IID_IConnectionPointContainer,
996 (void **)&mConnectionPointContainer);
997 if (FAILED(hres)) {
998 return;
999 }
1000
1001 hres = mConnectionPointContainer->FindConnectionPoint(IID_DDiscFormat2EraseEvents,
1002 &mConnectionPoint);
1003 if (FAILED(hres)) {
1004 Disconnect();
1005 return;
1006 }
1007
1008 hres = mConnectionPoint->Advise((IUnknown*)this, &mCookie);
1009 if (FAILED(hres)) {
1010 Disconnect();
1011 return;
1012 }
1013 }
1014
Disconnect()1015 void Disconnect()
1016 {
1017 if (mCookie != -1) {
1018 mConnectionPoint->Unadvise(mCookie);
1019 mCookie = -1;
1020 }
1021
1022 if (mConnectionPoint != NULL) {
1023 mConnectionPoint->Release();
1024 mConnectionPoint = NULL;
1025 }
1026
1027 if (mConnectionPointContainer != NULL) {
1028 mConnectionPointContainer->Release();
1029 mConnectionPointContainer = NULL;
1030 }
1031 }
1032
1033 // IUnknown
1034
QueryInterface(REFIID riid,LPVOID * ppv)1035 STDMETHODIMP QueryInterface(REFIID riid, LPVOID* ppv)
1036 {
1037 if (ppv == NULL) {
1038 return E_POINTER;
1039 }
1040
1041 *ppv = NULL;
1042
1043 if (riid == IID_IUnknown || riid == IID_IDispatch || riid == IID_DDiscFormat2EraseEvents) {
1044 *ppv = this;
1045 AddRef();
1046 return S_OK;
1047 }
1048
1049 return E_NOINTERFACE;
1050 }
1051
AddRef(VOID)1052 STDMETHODIMP_(ULONG) AddRef(VOID)
1053 {
1054 return InterlockedIncrement((LONG*) &mRefs);
1055 }
1056
Release(VOID)1057 STDMETHODIMP_(ULONG) Release(VOID)
1058 {
1059 ULONG ref = InterlockedDecrement((LONG*) &mRefs);
1060
1061 if (ref == 0) {
1062 delete this;
1063 }
1064
1065 return ref;
1066 }
1067
1068 // IDispatch
1069
GetTypeInfoCount(UINT * pctinfo)1070 STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
1071 {
1072 *pctinfo = 1;
1073 return S_OK;
1074 }
1075
GetTypeInfo(UINT iTInfo,LCID lcid,ITypeInfo ** ppTInfo)1076 STDMETHODIMP GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
1077 {
1078 if (ppTInfo == NULL) {
1079 return E_POINTER;
1080 }
1081
1082 if (iTInfo != 0) {
1083 return DISP_E_BADINDEX;
1084 }
1085
1086 mTypeInfo->AddRef();
1087
1088 *ppTInfo = mTypeInfo;
1089
1090 return S_OK;
1091 }
1092
GetIDsOfNames(REFIID riid,LPOLESTR * rgszNames,UINT cNames,LCID lcid,DISPID * rgDispId)1093 STDMETHODIMP GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
1094 {
1095 return DispGetIDsOfNames(mTypeInfo, rgszNames, cNames, rgDispId);
1096 }
1097
Invoke(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,DISPPARAMS * pDispParams,VARIANT * pVarResult,EXCEPINFO * pExcepInfo,UINT * puArgErr)1098 STDMETHODIMP Invoke(DISPID dispIdMember,
1099 REFIID riid,
1100 LCID lcid,
1101 WORD wFlags,
1102 DISPPARAMS *pDispParams,
1103 VARIANT *pVarResult,
1104 EXCEPINFO *pExcepInfo,
1105 UINT *puArgErr)
1106 {
1107 return DispInvoke(this,
1108 mTypeInfo,
1109 dispIdMember,
1110 wFlags,
1111 pDispParams,
1112 pVarResult,
1113 pExcepInfo,
1114 puArgErr);
1115 }
1116
1117 // DDiscFormat2EraseEvents
1118
Update(IDispatch * object,LONG elapsedSeconds,LONG estimatedTotalSeconds)1119 STDMETHODIMP Update(IDispatch *object,
1120 LONG elapsedSeconds,
1121 LONG estimatedTotalSeconds)
1122 {
1123 float fraction = elapsedSeconds / (float) estimatedTotalSeconds;
1124
1125 // Never set fraction to 1.0. Screws up synchro between thread and user.
1126 if (fraction >= 1.0) {
1127 fraction = (float) 0.99;
1128 }
1129
1130 mH->fraction = fraction;
1131
1132 return S_OK;
1133 }
1134
1135 private:
1136 PBHandlev2 *mH;
1137 LPTYPEINFO mTypeInfo;
1138 ULONG mRefs;
1139 DWORD mCookie;
1140 IConnectionPoint *mConnectionPoint;
1141 IConnectionPointContainer *mConnectionPointContainer;
1142 };
1143
1144 /* Erasing Thread */
PortBurn_v2_EraseDisc(LPVOID lpParam)1145 DWORD WINAPI PortBurn_v2_EraseDisc(LPVOID lpParam)
1146 {
1147 PBHandlev2 *h = (PBHandlev2 *) lpParam;
1148 IDiscRecorder2 *pDiscRecorder = NULL;
1149 IDiscFormat2Erase *pDiscErase = NULL;
1150 EraseEvents *pEraseEvents = NULL;
1151 bool mcndisabled = false;
1152
1153 h->hres = CoInitializeEx(0, COINIT_APARTMENTTHREADED);
1154 if (FAILED(h->hres)) {
1155 h->threadres = pbErrEraseFailed;
1156 h->fraction = 1.0;
1157 return S_OK;
1158 }
1159
1160 h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
1161 NULL,
1162 CLSCTX_ALL,
1163 IID_PPV_ARGS(&pDiscRecorder));
1164 if (FAILED(h->hres)) {
1165 goto done;
1166 }
1167
1168 h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
1169 if (FAILED(h->hres)) {
1170 goto done;
1171 }
1172
1173 h->hres = pDiscRecorder->DisableMcn();
1174 if (FAILED(h->hres)) {
1175 goto done;
1176 }
1177 mcndisabled = true;
1178
1179 h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2Erase),
1180 NULL,
1181 CLSCTX_ALL,
1182 IID_PPV_ARGS(&pDiscErase));
1183 if (FAILED(h->hres)) {
1184 goto done;
1185 }
1186
1187 h->hres = pDiscErase->put_ClientName(h->client);
1188 if (FAILED(h->hres)) {
1189 goto done;
1190 }
1191
1192 h->hres = pDiscErase->put_Recorder(pDiscRecorder);
1193 if (FAILED(h->hres)) {
1194 goto done;
1195 }
1196
1197 h->hres = pDiscErase->put_FullErase(h->fullerase);
1198 if (FAILED(h->hres)) {
1199 goto done;
1200 }
1201
1202 pEraseEvents = new EraseEvents(h);
1203 if (pEraseEvents == NULL) {
1204 goto done;
1205 }
1206
1207 pEraseEvents->Connect(pDiscErase);
1208
1209 h->cancel = false;
1210 h->fraction = 0.0;
1211
1212 h->hres = pDiscErase->EraseMedia();
1213
1214 if (SUCCEEDED(h->hres)) {
1215 IDiscRecorder2Ex *pDiscRecorderEx;
1216 int cnt = 50;
1217 bool blank = false;
1218
1219 h->hres = pDiscRecorder->QueryInterface(__uuidof(IDiscRecorder2Ex),
1220 (void **) &pDiscRecorderEx);
1221 if (SUCCEEDED(h->hres)) {
1222 while (!blank && cnt--) {
1223 BYTE *info;
1224 ULONG_IMAPI2_DISC_INFORMATION size;
1225
1226 SleepEx(100, true);
1227
1228 h->hres = pDiscRecorderEx->GetDiscInformation(&info, &size);
1229 if (FAILED(h->hres)) {
1230 break;
1231 }
1232
1233 blank = ((info[2] & 0x03) == 0);
1234
1235 CoTaskMemFree(info);
1236 }
1237
1238 pDiscRecorderEx->Release();
1239 }
1240 }
1241
1242 done:
1243
1244 if (FAILED(h->hres)) {
1245 h->threadres = pbErrEraseFailed;
1246 }
1247 else {
1248 h->threadres = pbSuccess;
1249 }
1250
1251 if (pEraseEvents != NULL) {
1252 pEraseEvents->Disconnect();
1253 pEraseEvents->Release();
1254 }
1255
1256 if (pDiscErase != NULL) {
1257 pDiscErase->put_ClientName(NULL);
1258 pDiscErase->put_Recorder(NULL);
1259 pDiscErase->Release();
1260 }
1261
1262 if (mcndisabled) {
1263 pDiscRecorder->EnableMcn();
1264 }
1265
1266 if (pDiscRecorder != NULL) {
1267 pDiscRecorder->Release();
1268 }
1269
1270 CoUninitialize();
1271
1272 h->fraction = 1.0;
1273
1274 return S_OK;
1275 }
1276
1277 /// ==================================================================
1278 /// ==================================================================
1279 /// ==================================================================
1280 /// ==================================================================
1281 /// ==================================================================
1282
get_diskid(void * handle,int index)1283 static BSTR get_diskid(void *handle, int index)
1284 {
1285 PBHandlev2 *h = (PBHandlev2 *) handle;
1286 IDiscMaster2 *pDiscMaster = NULL;
1287 BSTR bID = NULL;
1288 LONG count;
1289
1290 if (h == NULL) {
1291 return NULL;
1292 }
1293
1294 h->hres = S_OK;
1295
1296 h->hres = CoCreateInstance(__uuidof(MsftDiscMaster2),
1297 NULL,
1298 CLSCTX_ALL,
1299 IID_PPV_ARGS(&pDiscMaster));
1300 if (FAILED(h->hres)) {
1301 goto done;
1302 }
1303
1304 h->hres = pDiscMaster->get_Count(&count);
1305 if (FAILED(h->hres)) {
1306 goto done;
1307 }
1308
1309 if (index < 0 || index >= count) {
1310 h->hres = E_INVALIDARG;
1311 goto done;
1312 }
1313
1314 h->hres = pDiscMaster->get_Item(index, &bID);
1315 if (FAILED(h->hres)) {
1316 goto done;
1317 }
1318
1319 done:
1320
1321 if (pDiscMaster != NULL) {
1322 pDiscMaster->Release();
1323 }
1324
1325 return bID;
1326 }
1327
get_recorder(void * handle,int index)1328 static IDiscRecorder2 *get_recorder(void *handle, int index)
1329 {
1330 PBHandlev2 *h = (PBHandlev2 *) handle;
1331 IDiscRecorder2 *pDiscRecorder = NULL;
1332 BSTR bID = NULL;
1333
1334 if (h == NULL) {
1335 return NULL;
1336 }
1337
1338 h->hres = S_OK;
1339
1340 bID = get_diskid(handle, index);
1341 if (bID == NULL) {
1342 goto done;
1343 }
1344
1345 h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
1346 NULL,
1347 CLSCTX_ALL,
1348 IID_PPV_ARGS(&pDiscRecorder));
1349 if (FAILED(h->hres)) {
1350 goto done;
1351 }
1352
1353 h->hres = pDiscRecorder->InitializeDiscRecorder(bID);
1354 if (FAILED(h->hres)) {
1355 pDiscRecorder->Release();
1356 pDiscRecorder = NULL;
1357 }
1358
1359 done:
1360
1361 if (bID != NULL) {
1362 SysFreeString(bID);
1363 }
1364
1365 return pDiscRecorder;
1366 }
1367
1368 // ----------------------------------------------------------------------------
1369
PortBurn_v2_Open()1370 void *PortBurn_v2_Open()
1371 {
1372 PBHandlev2 *h;
1373 WCHAR name[255];
1374
1375 #if defined(_DEBUG)
1376 AllocConsole();
1377 freopen("CONOUT$", "w", stdout);
1378 #endif
1379
1380 h = (PBHandlev2 *) HeapAlloc(GetProcessHeap(),
1381 HEAP_ZERO_MEMORY,
1382 sizeof(PBHandlev2));
1383 if (h == NULL) {
1384 return NULL;
1385 }
1386
1387 h->test = pbTestDefault;
1388 h->verify = pbVerifyDefault;
1389 h->underrun = pbUnderrunDefault;
1390 h->eject = pbEjectDefault;
1391 h->gapless = pbGaplessDefault;
1392 h->speed = pbSpeedDefault;
1393
1394 _stprintf_s(name, 255, L"portburn_client_%d", GetTickCount());
1395
1396 h->client = SysAllocString(name);
1397 if (h->client == NULL) {
1398 HeapFree(GetProcessHeap(), 0, h);
1399 return NULL;
1400 }
1401
1402 return h;
1403 }
1404
1405 /* Cleanup */
1406 int PortBurn_v2_CloseDevice(void *handle);
PortBurn_v2_Close(void * handle)1407 void PortBurn_v2_Close(void *handle)
1408 {
1409 PBHandlev2 *h = (PBHandlev2 *) handle;
1410
1411 if (h == NULL) {
1412 return;
1413 }
1414
1415 PortBurn_v2_CloseDevice(h);
1416
1417 if (h->client != NULL) {
1418 SysFreeString(h->client);
1419 h->client = NULL;
1420 }
1421
1422 HeapFree(GetProcessHeap(), 0, h);
1423 }
1424
1425 /* Return a human-readable error string for the last operating system
1426 specific error (NOT human readable strings for the PortBurn error
1427 codes). Caller should dispose of the returned string using free(). */
PortBurn_v2_LastError(void * handle)1428 char *PortBurn_v2_LastError(void *handle)
1429 {
1430 PBHandlev2 *h = (PBHandlev2 *) handle;
1431 HRESULT hr;
1432 LPTSTR windowsErrorString = NULL;
1433 char *errorString = NULL;
1434 int len;
1435
1436 if (h == NULL) {
1437 return NULL;
1438 }
1439
1440 /* Have Windows allocate a buffer for us and format the error
1441 message in windowsErrorString */
1442 hr = h->hres;
1443 if (HRESULT_FACILITY(hr) == FACILITY_WINDOWS) {
1444 hr = HRESULT_CODE(hr);
1445 }
1446
1447 len = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
1448 NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
1449 (LPTSTR) &windowsErrorString, 0, NULL);
1450 if (windowsErrorString == NULL) {
1451 windowsErrorString = (LPTSTR) LocalAlloc(LPTR, 128 * sizeof(TCHAR));
1452 if (windowsErrorString == NULL) {
1453 return NULL;
1454 }
1455
1456 len = _stprintf_s(windowsErrorString, 128, L"HRESULT = %08x", hr);
1457 }
1458
1459 /* Convert the string */
1460 errorString = (char *) malloc(len + 1);
1461 if (errorString != NULL) {
1462 errorString[0] = '\0';
1463 WideCharToMultiByte(CP_ACP, 0, windowsErrorString, len, errorString, len, NULL, NULL);
1464 errorString[len] = '\0';
1465 }
1466
1467 LocalFree(windowsErrorString);
1468
1469 return errorString;
1470 }
1471
1472 /* Get the number of devices capable of burning audio CDs.
1473 If the result is N, then calls to GetDeviceName and OpenDevice
1474 are guaranteed to be valid for indices from 0 up to N-1, until
1475 the next time you call GetNumDevices. At that point, the list of
1476 devices will be rescanned, and may be different. */
PortBurn_v2_GetNumDevices(void * handle)1477 int PortBurn_v2_GetNumDevices(void *handle)
1478 {
1479 PBHandlev2 *h = (PBHandlev2 *) handle;
1480 IDiscMaster2 *pDiscMaster;
1481 LONG count = 0;
1482
1483 if (h == NULL) {
1484 return NULL;
1485 }
1486
1487 h->hres = CoCreateInstance(__uuidof(MsftDiscMaster2),
1488 NULL,
1489 CLSCTX_ALL,
1490 IID_PPV_ARGS(&pDiscMaster));
1491 if (SUCCEEDED(h->hres)) {
1492 h->hres = pDiscMaster->get_Count(&count);
1493
1494 pDiscMaster->Release();
1495 }
1496
1497 return count;
1498 }
1499
1500 /* Get the name of the device with a given index. Only valid
1501 after a call to GetNumDevices. */
PortBurn_v2_GetDeviceName(void * handle,int index)1502 char *PortBurn_v2_GetDeviceName(void *handle, int index)
1503 {
1504 PBHandlev2 *h = (PBHandlev2 *) handle;
1505 IDiscRecorder2 *pDiscRecorder = NULL;
1506 BSTR bVendor = NULL;
1507 BSTR bProduct = NULL;
1508 BSTR bRevision = NULL;
1509 char *name = NULL;
1510 TCHAR *wname;
1511 int len;
1512
1513 if (h == NULL) {
1514 return NULL;
1515 }
1516
1517 h->hres = S_OK;
1518
1519 pDiscRecorder = get_recorder(h, index);
1520 if (pDiscRecorder == NULL) {
1521 goto done;
1522 }
1523
1524 h->hres = pDiscRecorder->get_VendorId(&bVendor);
1525 if (FAILED(h->hres)) {
1526 goto done;
1527 }
1528
1529 h->hres = pDiscRecorder->get_ProductId(&bProduct);
1530 if (FAILED(h->hres)) {
1531 goto done;
1532 }
1533
1534 h->hres = pDiscRecorder->get_ProductRevision(&bRevision);
1535 if (FAILED(h->hres)) {
1536 goto done;
1537 }
1538
1539 len = SysStringLen(bVendor) + 1 +
1540 SysStringLen(bProduct) + 1 +
1541 SysStringLen(bRevision);
1542
1543 name = (char *) malloc(len + 1);
1544 if (name == NULL) {
1545 h->hres = E_OUTOFMEMORY;
1546 goto done;
1547 }
1548
1549 wname = (LPWSTR) alloca((len + 1) * sizeof(wchar_t));
1550 _stprintf_s(wname,
1551 (len + 1),
1552 _T("%s %s %s"),
1553 (LPCWSTR) bVendor,
1554 (LPCWSTR) bProduct,
1555 (LPCWSTR) bRevision);
1556
1557 name[0] = '\0';
1558 WideCharToMultiByte(CP_ACP, 0, wname, len, name, len, NULL, NULL);
1559 name[len] = '\0';
1560
1561 done:
1562
1563 if (bRevision != NULL) {
1564 SysFreeString(bRevision);
1565 }
1566
1567 if (bProduct != NULL) {
1568 SysFreeString(bProduct);
1569 }
1570
1571 if (bVendor != NULL) {
1572 SysFreeString(bVendor);
1573 }
1574
1575 if (pDiscRecorder != NULL) {
1576 pDiscRecorder->Release();
1577 }
1578
1579 return name;
1580 }
1581
1582 /* Open a particular device by index number. Returns 0 on success;
1583 any nonzero value indicates an error, for example if the device is
1584 already open by some other program. */
PortBurn_v2_OpenDevice(void * handle,int index)1585 int PortBurn_v2_OpenDevice(void *handle, int index)
1586 {
1587 PBHandlev2 *h = (PBHandlev2 *) handle;
1588
1589 if (h == NULL) {
1590 return pbErrNoHandle;
1591 }
1592
1593 h->hres = S_OK;
1594
1595 if (h->diskid != NULL) {
1596 return pbErrDeviceAlreadyOpen;
1597 }
1598
1599 h->diskid = get_diskid(handle, index);
1600 if (h->diskid == NULL) {
1601 return pbErrCannotReserveDevice;
1602 }
1603
1604 return pbSuccess;
1605 }
1606
1607 /* Close a device */
PortBurn_v2_CloseDevice(void * handle)1608 int PortBurn_v2_CloseDevice(void *handle)
1609 {
1610 PBHandlev2 *h = (PBHandlev2 *) handle;
1611
1612 if (h == NULL) {
1613 return pbErrNoHandle;
1614 }
1615
1616 h->hres = S_OK;
1617
1618 if (h->hThread != NULL) {
1619 h->cancel = true;
1620 WaitForSingleObject(h->hThread, INFINITE);
1621 CloseHandle(h->hThread);
1622 h->hThread = NULL;
1623 }
1624
1625 if (h->pStream != NULL) {
1626 h->pStream->Release();
1627 h->pStream = NULL;
1628 }
1629
1630 if (h->pMarshall != NULL) {
1631 IStorage *pStorage;
1632 h->hres = CoGetInterfaceAndReleaseStream(h->pMarshall,
1633 __uuidof(IStorage),
1634 (void **) &pStorage);
1635 h->pMarshall = NULL;
1636 }
1637
1638 if (h->pStorage != NULL) {
1639 h->pStorage->Release();
1640 h->pStorage = NULL;
1641 }
1642
1643 if (h->diskid != NULL) {
1644 SysFreeString(h->diskid);
1645 h->diskid = NULL;
1646 }
1647
1648 h->burning = false;
1649 h->erasing = false;
1650 h->cancel = false;
1651 h->fraction = 0.0;
1652
1653 h->test = pbTestDefault;
1654 h->verify = pbVerifyDefault;
1655 h->underrun = pbUnderrunDefault;
1656 h->eject = pbEjectDefault;
1657 h->gapless = pbGaplessDefault;
1658 h->speed = pbSpeedDefault;
1659
1660 return pbSuccess;
1661 }
1662
1663 /* Eject the media in the currently opened device */
PortBurn_v2_EjectDevice(void * handle)1664 int PortBurn_v2_EjectDevice(void *handle)
1665 {
1666 PBHandlev2 *h = (PBHandlev2 *) handle;
1667 IDiscRecorder2 *pDiscRecorder = NULL;
1668
1669 if (h == NULL) {
1670 return pbErrNoHandle;
1671 }
1672
1673 h->hres = S_OK;
1674
1675 if (h->diskid == NULL) {
1676 return pbErrDeviceNotOpen;
1677 }
1678
1679 h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
1680 NULL,
1681 CLSCTX_ALL,
1682 IID_PPV_ARGS(&pDiscRecorder));
1683 if (FAILED(h->hres)) {
1684 return pbErrCannotEject;
1685 }
1686
1687 h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
1688 if (FAILED(h->hres)) {
1689 pDiscRecorder->Release();
1690 return pbErrCannotEject;
1691 }
1692
1693 h->hres = pDiscRecorder->EjectMedia();
1694
1695 pDiscRecorder->Release();
1696
1697 if (FAILED(h->hres)) {
1698 return pbErrCannotEject;
1699 }
1700
1701 return pbSuccess;
1702 }
1703
1704 /* This indicates you're ready to start staging audio data for the
1705 currently opened device. At this point you are committing to
1706 exclusive access to the CD burner, and this is the function that
1707 will fail if another program is using the device, or if there is
1708 no writable CD media in the device at this point. */
PortBurn_v2_StartStaging(void * handle,const char * tmpdir)1709 int PortBurn_v2_StartStaging(void *handle, const char *tmpdir)
1710 {
1711 PBHandlev2 *h = (PBHandlev2 *) handle;
1712
1713 if (h == NULL) {
1714 return pbErrNoHandle;
1715 }
1716
1717 h->hres = S_OK;
1718
1719 if (h->diskid == NULL) {
1720 return pbErrDeviceNotOpen;
1721 }
1722
1723 if (h->pStorage != NULL) {
1724 return pbErrAlreadyStagingOrBurning;
1725 }
1726
1727 h->curtrack = -1;
1728
1729 h->hres = StgCreateStorageEx(h->client, // NULL,
1730 STGM_CREATE |
1731 STGM_SHARE_EXCLUSIVE |
1732 STGM_READWRITE |
1733 STGM_DELETEONRELEASE,
1734 STGFMT_STORAGE,
1735 0,
1736 NULL,
1737 NULL,
1738 IID_IStorage,
1739 (void **) &h->pStorage);
1740 if (FAILED(h->hres)) {
1741 return pbErrCannotCreateStagingDirectory;
1742 }
1743
1744 h->hres = CoMarshalInterThreadInterfaceInStream(__uuidof(IStorage),
1745 h->pStorage,
1746 &h->pMarshall);
1747 if (FAILED(h->hres)) {
1748 h->pStorage->Release();
1749 h->pStorage = NULL;
1750 return pbErrCannotCreateStagingDirectory;
1751 }
1752
1753 return pbSuccess;
1754 }
1755
1756 /* Start a new audio track. Pass the name of the track, and the
1757 length in CD Audio frames (each frame is 1/75.0 of a second, exactly). */
PortBurn_v2_StartTrack(void * handle,const char * name)1758 int PortBurn_v2_StartTrack(void *handle, const char *name)
1759 {
1760 PBHandlev2 *h = (PBHandlev2 *) handle;
1761 WCHAR wname[255];
1762 int curtrack;
1763 printf("start track\n");
1764 if (h == NULL) {
1765 return pbErrNoHandle;
1766 }
1767
1768 h->hres = S_OK;
1769
1770 if (h->pStorage == NULL) {
1771 return pbErrMustCallStartStaging;
1772 }
1773
1774 curtrack = h->curtrack + 1;
1775 if (curtrack == 99) {
1776 return pbErrCannotStageTrack;
1777 }
1778
1779 _stprintf_s(wname, 255, L"track #%d", curtrack);
1780
1781 h->hres = h->pStorage->CreateStream(wname,
1782 STGM_CREATE |
1783 STGM_SHARE_EXCLUSIVE |
1784 STGM_READWRITE,
1785 0,
1786 0,
1787 &h->pStream);
1788 if (FAILED(h->hres)) {
1789 return pbErrCannotCreateStagingFile;
1790 }
1791
1792 h->curtrack = curtrack;
1793
1794 return pbSuccess;
1795 }
1796
1797 /* Add one frame of audio to the current track. The buffer must be exactly
1798 1176 elements long, containing interleaved left and right audio samples.
1799 The values should be signed 16-bit numbers in the native endianness of
1800 this computer. */
PortBurn_v2_AddFrame(void * handle,short * buffer)1801 int PortBurn_v2_AddFrame(void *handle, short *buffer)
1802 {
1803 PBHandlev2 *h = (PBHandlev2 *) handle;
1804 int oneBlockByteCount = 1176 * 2;
1805
1806 if (h == NULL) {
1807 return pbErrNoHandle;
1808 }
1809
1810 h->hres = S_OK;
1811
1812 if (h->pStream == NULL) {
1813 return pbErrMustCallStartTrack;
1814 }
1815
1816 h->hres = h->pStream->Write(buffer, oneBlockByteCount, NULL);
1817 if (FAILED(h->hres)) {
1818 return pbErrCannotWriteToStagingFile;
1819 }
1820
1821 return pbSuccess;
1822 }
1823
1824 /* Finish the current audio track. */
PortBurn_v2_EndTrack(void * handle)1825 int PortBurn_v2_EndTrack(void *handle)
1826 {
1827 PBHandlev2 *h = (PBHandlev2 *) handle;
1828 LARGE_INTEGER zero = {0};
1829
1830 if (h == NULL) {
1831 return pbErrNoHandle;
1832 }
1833
1834 h->hres = S_OK;
1835
1836 if (h->pStream == NULL) {
1837 return pbErrMustCallStartTrack;
1838 }
1839
1840 h->pStream->Release();
1841 h->pStream = NULL;
1842
1843 return pbSuccess;
1844 }
1845
1846 /* Begin burning the disc. */
PortBurn_v2_StartBurning(void * handle)1847 int PortBurn_v2_StartBurning(void *handle)
1848 {
1849 PBHandlev2 *h = (PBHandlev2 *) handle;
1850 DWORD dwID;
1851
1852 if (h == NULL) {
1853 return pbErrNoHandle;
1854 }
1855 printf("starting burn\n");
1856 h->hres = S_OK;
1857
1858 if (h->curtrack < 0) {
1859 return pbErrMustCallStartTrack;
1860 }
1861
1862 if (h->hThread != NULL) {
1863 return pbErrAlreadyStagingOrBurning;
1864 }
1865
1866 h->burning = true;
1867
1868 LPTHREAD_START_ROUTINE start;
1869 #if 1
1870 IDiscFormat2RawCD *pDiscFormat;
1871 h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2RawCD),
1872 NULL,
1873 CLSCTX_ALL,
1874 IID_PPV_ARGS(&pDiscFormat));
1875 if (SUCCEEDED(h->hres)) {
1876 pDiscFormat->Release();
1877 start = PortBurn_v2_RecordDiscDAO;
1878 }
1879 else {
1880 start = PortBurn_v2_RecordDiscTAO;
1881 }
1882 #else
1883 start = PortBurn_v2_RecordDiscTAO;
1884 #endif
1885
1886 h->hThread = CreateThread(NULL,
1887 0,
1888 start,
1889 h,
1890 0,
1891 &dwID);
1892 if (h->hThread == NULL) {
1893 return pbErrCannotStartBurning;
1894 }
1895
1896 return pbSuccess;
1897 }
1898
1899 /* Cancel if burning was in progress. It might take a while for
1900 this to take effect; wait until GetStatus says 1.0 to close
1901 the device. */
PortBurn_v2_CancelBurning(void * handle)1902 int PortBurn_v2_CancelBurning(void *handle)
1903 {
1904 PBHandlev2 *h = (PBHandlev2 *) handle;
1905 float frac = 0.0;
1906
1907 if (h == NULL) {
1908 return pbErrNoHandle;
1909 }
1910
1911 h->hres = S_OK;
1912
1913 if (h->burning == false) {
1914 return pbErrNotCurrentlyBurning;
1915 }
1916
1917 h->cancel = true;
1918
1919 return pbSuccess;
1920 }
1921
1922 /* During burning, returns the fraction complete in the given
1923 float pointer, from 0.0 to 1.0. If this function returns
1924 nonzero, the disc burning has failed and should be aborted.
1925 If *out_fraction_complete is equal to 1.0, the burning is done;
1926 you can call PortBurn_CloseDevice.
1927 */
PortBurn_v2_GetStatus(void * handle,float * out_fraction_complete)1928 int PortBurn_v2_GetStatus(void *handle, float *out_fraction_complete)
1929 {
1930 PBHandlev2 *h = (PBHandlev2 *) handle;
1931
1932 if (h == NULL) {
1933 return pbErrNoHandle;
1934 }
1935
1936 h->hres = S_OK;
1937
1938 if (h->hThread == NULL) {
1939 return pbErrDeviceNotOpen;
1940 }
1941
1942 if (h->burning == false) {
1943 return pbErrNotCurrentlyBurning;
1944 }
1945
1946 MSG msg;
1947 if (PeekMessage(&msg, NULL, NULL, NULL, PM_REMOVE)) {
1948 TranslateMessage(&msg);
1949 DispatchMessage(&msg);
1950 }
1951
1952 WaitForSingleObject(h->hThread, 100);
1953
1954 *out_fraction_complete = h->fraction;
1955
1956 if (h->fraction == 1.0) {
1957 h->burning = false;
1958 WaitForSingleObject(h->hThread, INFINITE);
1959 CloseHandle(h->hThread);
1960 h->hThread = NULL;
1961 }
1962
1963 return pbSuccess;
1964 }
1965
1966 /* Get option value. */
PortBurn_v2_GetOption(void * handle,int option,int * value)1967 int PortBurn_v2_GetOption(void *handle, int option, int *value)
1968 {
1969 PBHandlev2 *h = (PBHandlev2 *) handle;
1970 int ret = pbSuccess;
1971
1972 if (h == NULL) {
1973 return pbErrNoHandle;
1974 }
1975
1976 switch (option)
1977 {
1978 case pbOptTest:
1979 {
1980 *value = h->test;
1981 }
1982 break;
1983
1984 case pbOptVerify:
1985 {
1986 *value = h->verify;
1987 }
1988 break;
1989
1990 case pbOptUnderrun:
1991 {
1992 *value = h->underrun;
1993 }
1994 break;
1995
1996 case pbOptEject:
1997 {
1998 *value = h->eject;
1999 }
2000 break;
2001
2002 case pbOptGapless:
2003 {
2004 *value = h->gapless;
2005 }
2006 break;
2007
2008 case pbOptSpeed:
2009 {
2010 }
2011 break;
2012
2013 default:
2014 {
2015 ret = pbErrInvalidOption;
2016 }
2017 break;
2018 }
2019
2020 return ret;
2021 }
2022
PortBurn_v2_SetOption(void * handle,int option,int value)2023 int PortBurn_v2_SetOption(void *handle, int option, int value)
2024 {
2025 PBHandlev2 *h = (PBHandlev2 *) handle;
2026 int ret = pbSuccess;
2027
2028 if (h == NULL) {
2029 return pbErrNoHandle;
2030 }
2031
2032 switch (option)
2033 {
2034 case pbOptTest:
2035 {
2036 h->test = value != 0;
2037 }
2038 break;
2039
2040 case pbOptVerify:
2041 {
2042 h->verify = value != 0;
2043 }
2044 break;
2045
2046 case pbOptUnderrun:
2047 {
2048 h->underrun = value != 0;
2049 }
2050 break;
2051
2052 case pbOptEject:
2053 {
2054 h->eject = value != 0;
2055 }
2056 break;
2057
2058 case pbOptGapless:
2059 {
2060 h->gapless = value != 0;
2061 }
2062 break;
2063
2064 case pbOptSpeed:
2065 {
2066 h->speed = value;
2067 }
2068 break;
2069
2070 default:
2071 {
2072 ret = pbErrInvalidOption;
2073 }
2074 break;
2075 }
2076
2077 return ret;
2078 }
2079
2080 /* Erase the media in the currently opened device */
PortBurn_v2_StartErasing(void * handle,int type)2081 int PortBurn_v2_StartErasing(void *handle, int type)
2082 {
2083 PBHandlev2 *h = (PBHandlev2 *) handle;
2084 DWORD dwID;
2085
2086 if (h == NULL) {
2087 return pbErrNoHandle;
2088 }
2089
2090 h->hres = S_OK;
2091
2092 if (h->hThread != NULL) {
2093 return pbErrCannotStartErasing;
2094 }
2095
2096 h->fullerase = (type == pbEraseQuick ? VARIANT_FALSE : VARIANT_TRUE);
2097
2098 h->erasing = true;
2099
2100 h->hThread = CreateThread(NULL,
2101 0,
2102 PortBurn_v2_EraseDisc,
2103 h,
2104 0,
2105 &dwID);
2106 if (h->hThread == NULL) {
2107 return pbErrCannotStartErasing;
2108 }
2109
2110 return pbSuccess;
2111 }
2112
PortBurn_v2_GetEraseStatus(void * handle,float * out_fraction_complete)2113 int PortBurn_v2_GetEraseStatus(void *handle, float *out_fraction_complete)
2114 {
2115 PBHandlev2 *h = (PBHandlev2 *) handle;
2116
2117 if (h == NULL) {
2118 return pbErrNoHandle;
2119 }
2120
2121 h->hres = S_OK;
2122
2123 if (h->diskid == NULL) {
2124 return pbErrDeviceNotOpen;
2125 }
2126
2127 if (h->erasing == false) {
2128 return pbErrNotCurrentlyErasing;
2129 }
2130
2131 WaitForSingleObject(h->hThread, 100);
2132
2133 *out_fraction_complete = h->fraction;
2134
2135 if (h->fraction == 1.0) {
2136 h->erasing = false;
2137 WaitForSingleObject(h->hThread, INFINITE);
2138 CloseHandle(h->hThread);
2139 h->hThread = NULL;
2140 }
2141
2142 return pbSuccess;
2143 }
2144
2145 /* */
PortBurn_v2_GetMediaState(void * handle,int * state)2146 int PortBurn_v2_GetMediaState(void *handle, int *state)
2147 {
2148 PBHandlev2 *h = (PBHandlev2 *) handle;
2149 IDiscRecorder2 *pDiscRecorder;
2150 IDiscFormat2Data *pDiscFormat;
2151 IMAPI_FORMAT2_DATA_MEDIA_STATE status;
2152 int mstate;
2153
2154 if (h == NULL) {
2155 return pbErrNoHandle;
2156 }
2157
2158 h->hres = S_OK;
2159 mstate = 0;
2160
2161 if (h->diskid == NULL) {
2162 return pbErrDeviceNotOpen;
2163 }
2164
2165 h->hres = CoCreateInstance(__uuidof(MsftDiscRecorder2),
2166 NULL,
2167 CLSCTX_ALL,
2168 IID_PPV_ARGS(&pDiscRecorder));
2169 if (FAILED(h->hres)) {
2170 return pbErrCannotAccessDevice;
2171 }
2172
2173 h->hres = pDiscRecorder->InitializeDiscRecorder(h->diskid);
2174 if (FAILED(h->hres)) {
2175 pDiscRecorder->Release();
2176 return pbErrCannotAccessDevice;
2177 }
2178
2179 h->hres = CoCreateInstance(__uuidof(MsftDiscFormat2Data),
2180 NULL,
2181 CLSCTX_ALL,
2182 IID_PPV_ARGS(&pDiscFormat));
2183 if (FAILED(h->hres)) {
2184 pDiscRecorder->Release();
2185 return pbErrCannotAccessDevice;
2186 }
2187
2188 h->hres = pDiscFormat->put_Recorder(pDiscRecorder);
2189 if (FAILED(h->hres)) {
2190 pDiscFormat->Release();
2191 pDiscRecorder->Release();
2192 return pbErrCannotAccessDevice;
2193 }
2194
2195 h->hres = pDiscFormat->get_CurrentMediaStatus(&status);
2196
2197 pDiscFormat->put_Recorder(NULL);
2198 pDiscFormat->Release();
2199 pDiscRecorder->Release();
2200
2201 if (h->hres == E_IMAPI_RECORDER_MEDIA_NO_MEDIA) {
2202 *state = pbMediaNone;
2203 return pbSuccess;
2204 }
2205
2206 if (FAILED(h->hres)) {
2207 return pbErrCannotAccessDevice;
2208 }
2209
2210 if (status & IMAPI_FORMAT2_DATA_MEDIA_STATE_BLANK) {
2211 mstate |= pbMediaBlank;
2212 }
2213
2214 if (!(status & IMAPI_FORMAT2_DATA_MEDIA_STATE_WRITE_PROTECTED)) {
2215 mstate |= pbMediaErasable;
2216 }
2217
2218 if (status & IMAPI_FORMAT2_DATA_MEDIA_STATE_APPENDABLE) {
2219 mstate |= pbMediaAppendable;
2220 }
2221
2222 if (status & IMAPI_FORMAT2_DATA_MEDIA_STATE_OVERWRITE_ONLY) {
2223 mstate |= pbMediaOverwritable;
2224 }
2225
2226 *state = mstate;
2227
2228 return pbSuccess;
2229 }
2230
PortBurn_v2_GetSupportedSpeeds(void * handle,int * cnt,int * speeds[])2231 int PortBurn_v2_GetSupportedSpeeds(void *handle, int *cnt, int *speeds[])
2232 {
2233 *cnt = 0;
2234 *speeds = NULL;
2235 return pbErrNoHandle;
2236 }
2237