1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 
5 module dfl.internal.com;
6 
7 private import dfl.internal.winapi, dfl.internal.wincom, dfl.internal.dlib;
8 
9 
10 version(DFL_TANGO_SEEK_COMPAT)
11 {
12 }
13 else
14 {
15 	version = DFL_TANGO_NO_SEEK_COMPAT;
16 }
17 
18 
19 // Importing dfl.application here causes the compiler to crash.
20 //import dfl.application;
21 private extern(C)
22 {
23 	size_t C_refCountInc(void* p);
24 	size_t C_refCountDec(void* p);
25 }
26 
27 
28 // Won't be killed by GC if not referenced in D and the refcount is > 0.
29 class DflComObject: ComObject // package
30 {
31 	extern(Windows):
32 	
33 	override ULONG AddRef()
34 	{
35 		//cprintf("AddRef `%.*s`\n", cast(int)toString().length, toString().ptr);
36 		return cast(ULONG)C_refCountInc(cast(void*)this);
37 	}
38 	
39 	override UINT Release()
40 	{
41 		//cprintf("Release `%.*s`\n", cast(int)toString().length, toString().ptr);
42 		return  cast(ULONG)C_refCountDec(cast(void*)this);
43 	}
44 }
45 
46 
47 class DStreamToIStream: DflComObject, dfl.internal.wincom.IStream
48 {
49 	this(DStream sourceDStream)
50 	{
51 		this.stm = sourceDStream;
52 	}
53 	
54 	
55 	extern(Windows):
56 	
57 	override HRESULT QueryInterface(IID* riid, void** ppv)
58 	{
59 		if(*riid == _IID_IStream)
60 		{
61 			*ppv = cast(void*)cast(dfl.internal.wincom.IStream)this;
62 			AddRef();
63 			return S_OK;
64 		}
65 		else if(*riid == _IID_ISequentialStream)
66 		{
67 			*ppv = cast(void*)cast(dfl.internal.wincom.ISequentialStream)this;
68 			AddRef();
69 			return S_OK;
70 		}
71 		else if(*riid == _IID_IUnknown)
72 		{
73 			*ppv = cast(void*)cast(IUnknown)this;
74 			AddRef();
75 			return S_OK;
76 		}
77 		else
78 		{
79 			*ppv = null;
80 			return E_NOINTERFACE;
81 		}
82 	}
83 	
84 	
85 	HRESULT Read(void* pv, ULONG cb, ULONG* pcbRead)
86 	{
87 		ULONG read;
88 		HRESULT result = S_OK;
89 		
90 		try
91 		{
92 			read =  cast(ULONG)stm.readBlock(pv, cb);
93 		}
94 		catch(DStreamException e)
95 		{
96 			result = S_FALSE; // ?
97 		}
98 		
99 		if(pcbRead)
100 			*pcbRead = read;
101 		//if(!read)
102 		//	result = S_FALSE;
103 		return result;
104 	}
105 	
106 	
107 	HRESULT Write(void* pv, ULONG cb, ULONG* pcbWritten)
108 	{
109 		ULONG written;
110 		HRESULT result = S_OK;
111 		
112 		try
113 		{
114 			if(!stm.writeable)
115 				return E_NOTIMPL;
116 			written =  cast(ULONG)stm.writeBlock(pv, cb);
117 		}
118 		catch(DStreamException e)
119 		{
120 			result = S_FALSE; // ?
121 		}
122 		
123 		if(pcbWritten)
124 			*pcbWritten = written;
125 		//if(!written)
126 		//	result = S_FALSE;
127 		return result;
128 	}
129 	
130 	
131 	version(DFL_TANGO_NO_SEEK_COMPAT)
132 	{
133 	}
134 	else
135 	{
136 		long _fakepos = 0;
137 	}
138 	
139 	
140 	HRESULT Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
141 	{
142 		HRESULT result = S_OK;
143 		
144 		//cprintf("seek move=%u, origin=0x%x\n", cast(uint)dlibMove.QuadPart, dwOrigin);
145 		
146 		try
147 		{
148 			if(!stm.seekable)
149 				//return S_FALSE; // ?
150 				return E_NOTIMPL; // ?
151 			
152 			ulong pos;
153 			switch(dwOrigin)
154 			{
155 				case STREAM_SEEK_SET:
156 					pos = stm.seekSet(dlibMove.QuadPart);
157 					if(plibNewPosition)
158 						plibNewPosition.QuadPart = pos;
159 					break;
160 				
161 				case STREAM_SEEK_CUR:
162 					pos = stm.seekCur(dlibMove.QuadPart);
163 					if(plibNewPosition)
164 						plibNewPosition.QuadPart = pos;
165 					break;
166 				
167 				case STREAM_SEEK_END:
168 					pos = stm.seekEnd(dlibMove.QuadPart);
169 					if(plibNewPosition)
170 						plibNewPosition.QuadPart = pos;
171 					break;
172 				
173 				default:
174 					result = STG_E_INVALIDFUNCTION;
175 			}
176 		}
177 		catch(DStreamException e)
178 		{
179 			result = S_FALSE; // ?
180 		}
181 		
182 		return result;
183 	}
184 	
185 	
186 	HRESULT SetSize(ULARGE_INTEGER libNewSize)
187 	{
188 		return E_NOTIMPL;
189 	}
190 	
191 	
192 	HRESULT CopyTo(IStream pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten)
193 	{
194 		// TODO: implement.
195 		return E_NOTIMPL;
196 	}
197 	
198 	
199 	HRESULT Commit(DWORD grfCommitFlags)
200 	{
201 		// Ignore -grfCommitFlags- and just flush the stream..
202 		//stm.flush();
203 		stm.flush();
204 		return S_OK; // ?
205 	}
206 	
207 	
208 	HRESULT Revert()
209 	{
210 		return E_NOTIMPL; // ? S_FALSE ?
211 	}
212 	
213 	
214 	HRESULT LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
215 	{
216 		return E_NOTIMPL;
217 	}
218 	
219 	
220 	HRESULT UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
221 	{
222 		return E_NOTIMPL;
223 	}
224 	
225 	
226 	HRESULT Stat(STATSTG* pstatstg, DWORD grfStatFlag)
227 	{
228 		return E_NOTIMPL; // ?
229 	}
230 	
231 	
232 	HRESULT Clone(IStream* ppstm)
233 	{
234 		// Cloned stream needs its own seek position.
235 		return E_NOTIMPL; // ?
236 	}
237 	
238 	
239 	extern(D):
240 	
241 	private:
242 	DStream stm;
243 }
244 
245 class MemoryIStream: DflComObject, dfl.internal.wincom.IStream
246 {
247 	this(void[] memory)
248 	{
249 		this.mem = memory;
250 	}
251 	
252 	
253 	extern(Windows):
254 	
255 	override HRESULT QueryInterface(IID* riid, void** ppv)
256 	{
257 		if(*riid == _IID_IStream)
258 		{
259 			*ppv = cast(void*)cast(dfl.internal.wincom.IStream)this;
260 			AddRef();
261 			return S_OK;
262 		}
263 		else if(*riid == _IID_ISequentialStream)
264 		{
265 			*ppv = cast(void*)cast(dfl.internal.wincom.ISequentialStream)this;
266 			AddRef();
267 			return S_OK;
268 		}
269 		else if(*riid == _IID_IUnknown)
270 		{
271 			*ppv = cast(void*)cast(IUnknown)this;
272 			AddRef();
273 			return S_OK;
274 		}
275 		else
276 		{
277 			*ppv = null;
278 			return E_NOINTERFACE;
279 		}
280 	}
281 	
282 	
283 	HRESULT Read(void* pv, ULONG cb, ULONG* pcbRead)
284 	{
285 		// Shouldn't happen unless the mem changes, which doesn't happen yet.
286 		if(seekpos > mem.length)
287 			return S_FALSE; // ?
288 		
289 		size_t count = mem.length - seekpos;
290 		if(count > cb)
291 			count = cb;
292 		
293 		pv[0 .. count] = mem[seekpos .. seekpos + count];
294 		seekpos += count;
295 		
296 		if(pcbRead)
297 			*pcbRead =  cast(ULONG)count;
298 		return S_OK;
299 	}
300 	
301 	
302 	HRESULT Write(void* pv, ULONG cb, ULONG* pcbWritten)
303 	{
304 		//return STG_E_ACCESSDENIED;
305 		return E_NOTIMPL;
306 	}
307 	
308 	
309 	HRESULT Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER* plibNewPosition)
310 	{
311 		//cprintf("seek move=%u, origin=0x%x\n", cast(uint)dlibMove.QuadPart, dwOrigin);
312 		
313 		auto toPos = cast(long)dlibMove.QuadPart;
314 		switch(dwOrigin)
315 		{
316 			case STREAM_SEEK_SET:
317 				break;
318 			
319 			case STREAM_SEEK_CUR:
320 				toPos = cast(long)seekpos + toPos;
321 				break;
322 			
323 			case STREAM_SEEK_END:
324 				toPos = cast(long)mem.length - toPos;
325 				break;
326 			
327 			default:
328 				return STG_E_INVALIDFUNCTION;
329 		}
330 		
331 		if(withinbounds(toPos))
332 		{
333 			seekpos = cast(size_t)toPos;
334 			if(plibNewPosition)
335 				plibNewPosition.QuadPart = seekpos;
336 			return S_OK;
337 		}
338 		else
339 		{
340 			return 0x80030005; //STG_E_ACCESSDENIED; // Seeking past end needs write access.
341 		}
342 	}
343 	
344 	
345 	HRESULT SetSize(ULARGE_INTEGER libNewSize)
346 	{
347 		return E_NOTIMPL;
348 	}
349 	
350 	
351 	HRESULT CopyTo(IStream pstm, ULARGE_INTEGER cb, ULARGE_INTEGER* pcbRead, ULARGE_INTEGER* pcbWritten)
352 	{
353 		// TODO: implement.
354 		return E_NOTIMPL;
355 	}
356 	
357 	
358 	HRESULT Commit(DWORD grfCommitFlags)
359 	{
360 		return S_OK; // ?
361 	}
362 	
363 	
364 	HRESULT Revert()
365 	{
366 		return E_NOTIMPL; // ? S_FALSE ?
367 	}
368 	
369 	
370 	HRESULT LockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
371 	{
372 		return E_NOTIMPL;
373 	}
374 	
375 	
376 	HRESULT UnlockRegion(ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType)
377 	{
378 		return E_NOTIMPL;
379 	}
380 	
381 	
382 	HRESULT Stat(STATSTG* pstatstg, DWORD grfStatFlag)
383 	{
384 		return E_NOTIMPL; // ?
385 	}
386 	
387 	
388 	HRESULT Clone(IStream* ppstm)
389 	{
390 		// Cloned stream needs its own seek position.
391 		return E_NOTIMPL; // ?
392 	}
393 	
394 	
395 	extern(D):
396 	
397 	private:
398 	void[] mem;
399 	size_t seekpos = 0;
400 	
401 	
402 	bool withinbounds(long pos)
403 	{
404 		if(pos < seekpos.min || pos > seekpos.max)
405 			return false;
406 		// Note: it IS within bounds if it's AT the end, it just can't read there.
407 		return cast(size_t)pos <= mem.length;
408 	}
409 }
410