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(&sectors);
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(&sectors);
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