1*c2c66affSColin Finck /*
2*c2c66affSColin Finck  * COPYRIGHT:            See COPYING in the top level directory
3*c2c66affSColin Finck  * PROJECT:              ReactOS DirectX
4*c2c66affSColin Finck  * FILE:                 dll/directx/ddraw/Surface/createsurface.c
5*c2c66affSColin Finck  * PURPOSE:              IDirectDrawSurface Creation
6*c2c66affSColin Finck  * PROGRAMMER:           Magnus Olsen
7*c2c66affSColin Finck  *
8*c2c66affSColin Finck  */
9*c2c66affSColin Finck #include "rosdraw.h"
10*c2c66affSColin Finck 
11*c2c66affSColin Finck /*
12*c2c66affSColin Finck * All parameters must have been checked if they are valid before they are passed to Internal_CreateSurface.
13*c2c66affSColin Finck * If not please fix the code in the functions which call Internal_CreateSurface.
14*c2c66affSColin Finck * ppSurf,pDDSD,pDDraw are being validated in Internal_CreateSurface.
15*c2c66affSColin Finck  */
16*c2c66affSColin Finck 
17*c2c66affSColin Finck HRESULT
Internal_CreateSurface(LPDDRAWI_DIRECTDRAW_INT pDDraw,LPDDSURFACEDESC2 pDDSD,LPDDRAWI_DDRAWSURFACE_INT * ppSurf,IUnknown * pUnkOuter)18*c2c66affSColin Finck Internal_CreateSurface( LPDDRAWI_DIRECTDRAW_INT pDDraw, LPDDSURFACEDESC2 pDDSD,
19*c2c66affSColin Finck                         LPDDRAWI_DDRAWSURFACE_INT *ppSurf, IUnknown *pUnkOuter)
20*c2c66affSColin Finck {
21*c2c66affSColin Finck     DDHAL_CANCREATESURFACEDATA mDdCanCreateSurface = { 0 };
22*c2c66affSColin Finck     DDHAL_CREATESURFACEDATA mDdCreateSurface = { 0 };
23*c2c66affSColin Finck 
24*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_INT ThisSurfInt;
25*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_LCL ThisSurfLcl;
26*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_GBL ThisSurfGbl;
27*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_MORE ThisSurfMore;
28*c2c66affSColin Finck 
29*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_INT * slist_int = NULL;
30*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_LCL * slist_lcl = NULL;
31*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_GBL * slist_gbl = NULL;
32*c2c66affSColin Finck     LPDDRAWI_DDRAWSURFACE_MORE * slist_more = NULL;
33*c2c66affSColin Finck     DWORD num_of_surf=1;
34*c2c66affSColin Finck     DWORD count;
35*c2c66affSColin Finck     HRESULT ret;
36*c2c66affSColin Finck 
37*c2c66affSColin Finck     if((pDDraw->lpLcl->dwLocalFlags & DDRAWILCL_SETCOOPCALLED) != DDRAWILCL_SETCOOPCALLED)
38*c2c66affSColin Finck     {
39*c2c66affSColin Finck         return DDERR_NOCOOPERATIVELEVELSET;
40*c2c66affSColin Finck     }
41*c2c66affSColin Finck 
42*c2c66affSColin Finck     if(pUnkOuter)
43*c2c66affSColin Finck     {
44*c2c66affSColin Finck         return CLASS_E_NOAGGREGATION;
45*c2c66affSColin Finck     }
46*c2c66affSColin Finck 
47*c2c66affSColin Finck     if(!(pDDSD->dwFlags & DDSD_CAPS))
48*c2c66affSColin Finck     {
49*c2c66affSColin Finck         return DDERR_INVALIDPARAMS;
50*c2c66affSColin Finck     }
51*c2c66affSColin Finck     if (pDDraw->lpLcl->dwProcessId != GetCurrentProcessId() )
52*c2c66affSColin Finck     {
53*c2c66affSColin Finck         return DDERR_INVALIDOBJECT;
54*c2c66affSColin Finck     }
55*c2c66affSColin Finck 
56*c2c66affSColin Finck     if  ( ((pDDSD->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) == DDSCAPS_SYSTEMMEMORY) &&
57*c2c66affSColin Finck           ((pDDSD->ddsCaps.dwCaps & DDSCAPS_VIDEOMEMORY) == DDSCAPS_VIDEOMEMORY) )
58*c2c66affSColin Finck     {
59*c2c66affSColin Finck         return DDERR_INVALIDCAPS;
60*c2c66affSColin Finck     }
61*c2c66affSColin Finck 
62*c2c66affSColin Finck     if((!(pDDSD->dwFlags & DDSD_HEIGHT) || !(pDDSD->dwFlags & DDSD_WIDTH))
63*c2c66affSColin Finck        && !(pDDSD->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
64*c2c66affSColin Finck     {
65*c2c66affSColin Finck         return DDERR_INVALIDPARAMS;
66*c2c66affSColin Finck     }
67*c2c66affSColin Finck 
68*c2c66affSColin Finck     else if(((pDDSD->dwFlags & DDSD_HEIGHT) || (pDDSD->dwFlags & DDSD_WIDTH))
69*c2c66affSColin Finck             && (pDDSD->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE))
70*c2c66affSColin Finck     {
71*c2c66affSColin Finck         return DDERR_INVALIDPARAMS;
72*c2c66affSColin Finck     }
73*c2c66affSColin Finck 
74*c2c66affSColin Finck     /*
75*c2c66affSColin Finck      * program does not need set the DDSD_LPSURFACE,
76*c2c66affSColin Finck      * if they forget set it, the ddraw will automatic
77*c2c66affSColin Finck      * set it for system memory.
78*c2c66affSColin Finck      */
79*c2c66affSColin Finck     if ( ((pDDSD->ddsCaps.dwCaps & DDSCAPS_SYSTEMMEMORY) == DDSCAPS_SYSTEMMEMORY) &&
80*c2c66affSColin Finck          ((pDDSD->dwFlags & DDSD_LPSURFACE) != DDSD_LPSURFACE) )
81*c2c66affSColin Finck     {
82*c2c66affSColin Finck         pDDSD->dwFlags = pDDSD->dwFlags | DDSD_LPSURFACE;
83*c2c66affSColin Finck     }
84*c2c66affSColin Finck 
85*c2c66affSColin Finck     /* FIXME count how many surfaces we need */
86*c2c66affSColin Finck 
87*c2c66affSColin Finck     DxHeapMemAlloc(slist_int, num_of_surf * sizeof( LPDDRAWI_DDRAWSURFACE_INT ) );
88*c2c66affSColin Finck     if( slist_int == NULL)
89*c2c66affSColin Finck     {
90*c2c66affSColin Finck         ret = DDERR_OUTOFMEMORY;
91*c2c66affSColin Finck         goto cleanup;
92*c2c66affSColin Finck     }
93*c2c66affSColin Finck 
94*c2c66affSColin Finck     DxHeapMemAlloc(slist_lcl, num_of_surf * sizeof( LPDDRAWI_DDRAWSURFACE_LCL ) );
95*c2c66affSColin Finck     if( slist_lcl == NULL )
96*c2c66affSColin Finck     {
97*c2c66affSColin Finck         ret = DDERR_OUTOFMEMORY;
98*c2c66affSColin Finck         goto cleanup;
99*c2c66affSColin Finck     }
100*c2c66affSColin Finck 
101*c2c66affSColin Finck     /* keep pointers to all gbl surfs to be able to free them on error */
102*c2c66affSColin Finck     DxHeapMemAlloc(slist_gbl, num_of_surf * sizeof( LPDDRAWI_DDRAWSURFACE_GBL ) );
103*c2c66affSColin Finck     if( slist_gbl == NULL )
104*c2c66affSColin Finck     {
105*c2c66affSColin Finck         DxHeapMemFree(slist_int);
106*c2c66affSColin Finck         return DDERR_OUTOFMEMORY;
107*c2c66affSColin Finck     }
108*c2c66affSColin Finck 
109*c2c66affSColin Finck     /* keep pointers to all more surfs to be able to free them on error */
110*c2c66affSColin Finck     DxHeapMemAlloc(slist_more, num_of_surf * sizeof( LPDDRAWI_DDRAWSURFACE_MORE ) );
111*c2c66affSColin Finck     if( slist_more == NULL )
112*c2c66affSColin Finck     {
113*c2c66affSColin Finck         DxHeapMemFree(slist_int);
114*c2c66affSColin Finck         return DDERR_OUTOFMEMORY;
115*c2c66affSColin Finck     }
116*c2c66affSColin Finck 
117*c2c66affSColin Finck     for( count=0; count < num_of_surf; count++ )
118*c2c66affSColin Finck     {
119*c2c66affSColin Finck         /* Allocate the surface interface and needed members */
120*c2c66affSColin Finck         DxHeapMemAlloc(ThisSurfInt,  sizeof( DDRAWI_DDRAWSURFACE_INT ) );
121*c2c66affSColin Finck         if( ThisSurfInt == NULL )
122*c2c66affSColin Finck         {
123*c2c66affSColin Finck             ret = DDERR_OUTOFMEMORY;
124*c2c66affSColin Finck             goto cleanup;
125*c2c66affSColin Finck         }
126*c2c66affSColin Finck 
127*c2c66affSColin Finck         DxHeapMemAlloc(ThisSurfLcl,  sizeof( DDRAWI_DDRAWSURFACE_LCL ) );
128*c2c66affSColin Finck         if( ThisSurfLcl == NULL )
129*c2c66affSColin Finck         {
130*c2c66affSColin Finck             ret = DDERR_OUTOFMEMORY;
131*c2c66affSColin Finck             goto cleanup;
132*c2c66affSColin Finck         }
133*c2c66affSColin Finck 
134*c2c66affSColin Finck         DxHeapMemAlloc(ThisSurfGbl,  sizeof( DDRAWI_DDRAWSURFACE_GBL ) );
135*c2c66affSColin Finck         if( ThisSurfGbl == NULL )
136*c2c66affSColin Finck         {
137*c2c66affSColin Finck             ret = DDERR_OUTOFMEMORY;
138*c2c66affSColin Finck             goto cleanup;
139*c2c66affSColin Finck         }
140*c2c66affSColin Finck 
141*c2c66affSColin Finck         DxHeapMemAlloc(ThisSurfMore, sizeof( DDRAWI_DDRAWSURFACE_MORE ) );
142*c2c66affSColin Finck         if( ThisSurfMore == NULL )
143*c2c66affSColin Finck         {
144*c2c66affSColin Finck             ret = DDERR_OUTOFMEMORY;
145*c2c66affSColin Finck             goto cleanup;
146*c2c66affSColin Finck         }
147*c2c66affSColin Finck 
148*c2c66affSColin Finck         /* setup lists, really needed are slist_lcl, slist_int
149*c2c66affSColin Finck           other slists should be released on return */
150*c2c66affSColin Finck 
151*c2c66affSColin Finck         slist_int[count] = ThisSurfInt;
152*c2c66affSColin Finck         slist_lcl[count] = ThisSurfLcl;
153*c2c66affSColin Finck         slist_gbl[count] = ThisSurfGbl;
154*c2c66affSColin Finck         slist_more[count] = ThisSurfMore;
155*c2c66affSColin Finck 
156*c2c66affSColin Finck         /* Start now fill in the member as they shall look like before call to createsurface */
157*c2c66affSColin Finck 
158*c2c66affSColin Finck         ThisSurfInt->lpLcl = ThisSurfLcl;
159*c2c66affSColin Finck         ThisSurfLcl->lpGbl = ThisSurfGbl;
160*c2c66affSColin Finck 
161*c2c66affSColin Finck         ThisSurfLcl->ddsCaps.dwCaps = pDDSD->ddsCaps.dwCaps;
162*c2c66affSColin Finck 
163*c2c66affSColin Finck         ThisSurfGbl->lpDD = pDDraw->lpLcl->lpGbl;
164*c2c66affSColin Finck         ThisSurfGbl->lpDDHandle = pDDraw->lpLcl->lpGbl;
165*c2c66affSColin Finck 
166*c2c66affSColin Finck         /* FIXME ? */
167*c2c66affSColin Finck         ThisSurfGbl->dwGlobalFlags = DDRAWISURFGBL_ISGDISURFACE;
168*c2c66affSColin Finck 
169*c2c66affSColin Finck         if (pDDSD->ddsCaps.dwCaps & DDSCAPS_PRIMARYSURFACE)
170*c2c66affSColin Finck         {
171*c2c66affSColin Finck             ThisSurfGbl->wWidth  = pDDraw->lpLcl->lpGbl->vmiData.dwDisplayWidth;
172*c2c66affSColin Finck             ThisSurfGbl->wHeight = pDDraw->lpLcl->lpGbl->vmiData.dwDisplayHeight;
173*c2c66affSColin Finck             ThisSurfGbl->lPitch  = pDDraw->lpLcl->lpGbl->vmiData.lDisplayPitch;
174*c2c66affSColin Finck             ThisSurfGbl->dwLinearSize = pDDraw->lpLcl->lpGbl->vmiData.lDisplayPitch;
175*c2c66affSColin Finck 
176*c2c66affSColin Finck 
177*c2c66affSColin Finck             ThisSurfMore->dmiDDrawReserved7.wWidth = pDDraw->lpLcl->lpGbl->vmiData.dwDisplayWidth;
178*c2c66affSColin Finck             ThisSurfMore->dmiDDrawReserved7.wHeight = pDDraw->lpLcl->lpGbl->vmiData.dwDisplayHeight;
179*c2c66affSColin Finck             ThisSurfMore->dmiDDrawReserved7.wBPP    = pDDraw->lpLcl->lpGbl->dwMonitorFrequency;
180*c2c66affSColin Finck 
181*c2c66affSColin Finck             /* FIXME  ThisSurfaceMore->dmiDDrawReserved7.wMonitorsAttachedToDesktop */
182*c2c66affSColin Finck             ThisSurfMore->dmiDDrawReserved7.wMonitorsAttachedToDesktop = 1;
183*c2c66affSColin Finck             pDDraw->lpLcl->lpPrimary = ThisSurfInt;
184*c2c66affSColin Finck         }
185*c2c66affSColin Finck         else
186*c2c66affSColin Finck         {
187*c2c66affSColin Finck             ThisSurfGbl->wWidth  = (WORD)pDDSD->dwWidth;
188*c2c66affSColin Finck             ThisSurfGbl->wHeight = (WORD)pDDSD->dwHeight;
189*c2c66affSColin Finck             ThisSurfGbl->lPitch  = pDDSD->lPitch;
190*c2c66affSColin Finck             ThisSurfGbl->dwLinearSize = pDDSD->lPitch;
191*c2c66affSColin Finck         }
192*c2c66affSColin Finck 
193*c2c66affSColin Finck         if(pDDraw->lpVtbl == &DirectDraw7_Vtable)
194*c2c66affSColin Finck         {
195*c2c66affSColin Finck             ThisSurfInt->lpVtbl = &DirectDrawSurface7_Vtable;
196*c2c66affSColin Finck         }
197*c2c66affSColin Finck         else if(pDDraw->lpVtbl == &DirectDraw4_Vtable)
198*c2c66affSColin Finck         {
199*c2c66affSColin Finck             ThisSurfInt->lpVtbl = &DirectDrawSurface4_Vtable;
200*c2c66affSColin Finck         }
201*c2c66affSColin Finck         else if(pDDraw->lpVtbl == &DirectDraw2_Vtable)
202*c2c66affSColin Finck         {
203*c2c66affSColin Finck             ThisSurfInt->lpVtbl = &DirectDrawSurface2_Vtable;
204*c2c66affSColin Finck         }
205*c2c66affSColin Finck         else if(pDDraw->lpVtbl == &DirectDraw_Vtable)
206*c2c66affSColin Finck         {
207*c2c66affSColin Finck             ThisSurfInt->lpVtbl = &DirectDrawSurface_Vtable;
208*c2c66affSColin Finck         }
209*c2c66affSColin Finck         else
210*c2c66affSColin Finck         {
211*c2c66affSColin Finck             ret =  DDERR_NOTINITIALIZED;
212*c2c66affSColin Finck             goto cleanup;
213*c2c66affSColin Finck         }
214*c2c66affSColin Finck 
215*c2c66affSColin Finck         ThisSurfLcl->lpSurfMore = ThisSurfMore;
216*c2c66affSColin Finck         ThisSurfMore->dwSize = sizeof(DDRAWI_DDRAWSURFACE_MORE);
217*c2c66affSColin Finck         ThisSurfMore->lpDD_int = pDDraw;
218*c2c66affSColin Finck         ThisSurfMore->lpDD_lcl = pDDraw->lpLcl;
219*c2c66affSColin Finck         ThisSurfMore->slist = slist_lcl;
220*c2c66affSColin Finck 
221*c2c66affSColin Finck         ThisSurfLcl->dwProcessId = GetCurrentProcessId();
222*c2c66affSColin Finck 
223*c2c66affSColin Finck         /* FIXME the lpLnk */
224*c2c66affSColin Finck 
225*c2c66affSColin Finck         Main_DDrawSurface_AddRef(ThisSurfInt);
226*c2c66affSColin Finck     }
227*c2c66affSColin Finck 
228*c2c66affSColin Finck     pDDraw->lpLcl->lpGbl->dsList = (LPDDRAWI_DDRAWSURFACE_INT) slist_int;
229*c2c66affSColin Finck 
230*c2c66affSColin Finck     /* Fixme call on DdCanCreate then on DdCreateSurface createsurface data here */
231*c2c66affSColin Finck 
232*c2c66affSColin Finck     /* FIXME bIsDifferentPixelFormat being set to true or false with automatic detections */
233*c2c66affSColin Finck     mDdCanCreateSurface.bIsDifferentPixelFormat = FALSE;
234*c2c66affSColin Finck 
235*c2c66affSColin Finck     mDdCanCreateSurface.lpDD = pDDraw->lpLcl->lpGbl;
236*c2c66affSColin Finck     mDdCanCreateSurface.CanCreateSurface = pDDraw->lpLcl->lpDDCB->HALDD.CanCreateSurface;
237*c2c66affSColin Finck     mDdCanCreateSurface.lpDDSurfaceDesc = (LPDDSURFACEDESC) pDDSD;
238*c2c66affSColin Finck     mDdCanCreateSurface.ddRVal = DDERR_GENERIC;
239*c2c66affSColin Finck 
240*c2c66affSColin Finck     if (mDdCanCreateSurface.CanCreateSurface(&mDdCanCreateSurface) == DDHAL_DRIVER_NOTHANDLED)
241*c2c66affSColin Finck     {
242*c2c66affSColin Finck         DX_STUB_str("mDdCanCreateSurface failed with DDHAL_DRIVER_NOTHANDLED.");
243*c2c66affSColin Finck         ret = DDERR_NOTINITIALIZED;
244*c2c66affSColin Finck         goto cleanup;
245*c2c66affSColin Finck     }
246*c2c66affSColin Finck 
247*c2c66affSColin Finck     if (mDdCanCreateSurface.ddRVal != DD_OK)
248*c2c66affSColin Finck     {
249*c2c66affSColin Finck         DX_STUB_str("mDdCanCreateSurface failed.");
250*c2c66affSColin Finck         ret = mDdCanCreateSurface.ddRVal;
251*c2c66affSColin Finck         goto cleanup;
252*c2c66affSColin Finck     }
253*c2c66affSColin Finck 
254*c2c66affSColin Finck     mDdCreateSurface.lpDD = pDDraw->lpLcl->lpGbl;
255*c2c66affSColin Finck     mDdCreateSurface.CreateSurface = pDDraw->lpLcl->lpGbl->lpDDCBtmp->HALDD.CreateSurface;
256*c2c66affSColin Finck     mDdCreateSurface.ddRVal = DDERR_GENERIC;
257*c2c66affSColin Finck     mDdCreateSurface.dwSCnt = num_of_surf;
258*c2c66affSColin Finck     mDdCreateSurface.lpDDSurfaceDesc = (LPDDSURFACEDESC) pDDSD;
259*c2c66affSColin Finck     mDdCreateSurface.lplpSList = slist_lcl;
260*c2c66affSColin Finck 
261*c2c66affSColin Finck     if (mDdCreateSurface.CreateSurface(&mDdCreateSurface) == DDHAL_DRIVER_NOTHANDLED)
262*c2c66affSColin Finck     {
263*c2c66affSColin Finck         DX_STUB_str("mDdCreateSurface failed with DDHAL_DRIVER_NOTHANDLED.");
264*c2c66affSColin Finck         ret = DDERR_NOTINITIALIZED;
265*c2c66affSColin Finck         goto cleanup;
266*c2c66affSColin Finck     }
267*c2c66affSColin Finck 
268*c2c66affSColin Finck     if (mDdCreateSurface.ddRVal != DD_OK)
269*c2c66affSColin Finck     {
270*c2c66affSColin Finck         DX_STUB_str("mDdCreateSurface failed.");
271*c2c66affSColin Finck         ret = mDdCreateSurface.ddRVal;
272*c2c66affSColin Finck         goto cleanup;
273*c2c66affSColin Finck     }
274*c2c66affSColin Finck 
275*c2c66affSColin Finck     /* free unneeded slists */
276*c2c66affSColin Finck     if (slist_more != NULL)
277*c2c66affSColin Finck         DxHeapMemFree(slist_more);
278*c2c66affSColin Finck     if (slist_gbl != NULL)
279*c2c66affSColin Finck         DxHeapMemFree(slist_gbl);
280*c2c66affSColin Finck 
281*c2c66affSColin Finck     *ppSurf = (LPDDRAWI_DDRAWSURFACE_INT) &slist_int[0]->lpVtbl;
282*c2c66affSColin Finck 
283*c2c66affSColin Finck     return DD_OK;
284*c2c66affSColin Finck 
285*c2c66affSColin Finck cleanup:
286*c2c66affSColin Finck     for(count = 0; count < num_of_surf; count++)
287*c2c66affSColin Finck     {
288*c2c66affSColin Finck         if (slist_more[count] != NULL)
289*c2c66affSColin Finck             DxHeapMemFree(slist_more[count]);
290*c2c66affSColin Finck         if (slist_gbl[count] != NULL)
291*c2c66affSColin Finck             DxHeapMemFree(slist_gbl[count]);
292*c2c66affSColin Finck         if (slist_lcl[count] != NULL)
293*c2c66affSColin Finck             DxHeapMemFree(slist_lcl[count]);
294*c2c66affSColin Finck         if (slist_int[count] != NULL)
295*c2c66affSColin Finck             DxHeapMemFree(slist_int[count]);
296*c2c66affSColin Finck     }
297*c2c66affSColin Finck     if (slist_more != NULL)
298*c2c66affSColin Finck         DxHeapMemFree(slist_more);
299*c2c66affSColin Finck     if (slist_gbl != NULL)
300*c2c66affSColin Finck         DxHeapMemFree(slist_gbl);
301*c2c66affSColin Finck     if (slist_lcl != NULL)
302*c2c66affSColin Finck         DxHeapMemFree(slist_lcl);
303*c2c66affSColin Finck     if (slist_int != NULL)
304*c2c66affSColin Finck         DxHeapMemFree(slist_int);
305*c2c66affSColin Finck 
306*c2c66affSColin Finck     return ret;
307*c2c66affSColin Finck }
308*c2c66affSColin Finck 
309*c2c66affSColin Finck 
310*c2c66affSColin Finck 
CopyDDSurfDescToDDSurfDesc2(LPDDSURFACEDESC2 dst_pDesc,LPDDSURFACEDESC src_pDesc)311*c2c66affSColin Finck void CopyDDSurfDescToDDSurfDesc2(LPDDSURFACEDESC2 dst_pDesc, LPDDSURFACEDESC src_pDesc)
312*c2c66affSColin Finck {
313*c2c66affSColin Finck     RtlZeroMemory(dst_pDesc,sizeof(DDSURFACEDESC2));
314*c2c66affSColin Finck     RtlCopyMemory(dst_pDesc,src_pDesc,sizeof(DDSURFACEDESC));
315*c2c66affSColin Finck     dst_pDesc->dwSize =  sizeof(DDSURFACEDESC2);
316*c2c66affSColin Finck }
317*c2c66affSColin Finck 
318*c2c66affSColin Finck 
319*c2c66affSColin Finck 
320*c2c66affSColin Finck 
321*c2c66affSColin Finck 
322