1 #ifndef _INCLUDED_MEDIA_HPP_
2 #define _INCLUDED_MEDIA_HPP_
3
4 #include <stdio.h>
5 #include <limits.h>
6 #include <string.h>
7
8 // type names that do not conflcit with anything else
9 typedef signed char S8;
10 typedef unsigned char U8;
11 typedef signed short int S16;
12 typedef unsigned short int U16;
13 typedef signed int S32;
14 typedef unsigned int U32;
15
16 class MediaMedium
17 {
18 protected:
19 // standard constructor
MediaMedium()20 MediaMedium() :
21 m_fError(0),
22 m_nDefBufSize(1024),
23 m_pWriteBuffer(NULL),
24 m_pReadBuffer(NULL),
25 m_nReadBufPos(0),
26 m_nWriteBufPos(0),
27 m_nBufSize(0),
28 m_nBufLenUsed(0),
29 m_nRefCnt(1)
30 {}
31
~MediaMedium()32 virtual ~MediaMedium() {}
33
34 public:
35 // allow reference counting
AddRef()36 unsigned AddRef() { return ++m_nRefCnt; }
Release()37 unsigned Release() { if (0==(--m_nRefCnt)) { delete this; return 0;} else return m_nRefCnt; }
38
39 enum
40 {
41 // error code flags
42 MME_VEOFMET = 0x00000001 // virtual end of file met
43 ,MME_EOFMET = 0x00000002 // actual end of file met
44 ,MME_OPENFAIL = 0x00000004 // failed to open medium
45 ,MME_CLOSEFAIL = 0x00000008 // failed to close medium
46 ,MME_UNAVAIL = 0x00000010 // requested operation was not available
47 ,MME_IOERROR = 0x00000020 // read/write operation failed
48 };
49 unsigned m_fError;
50
51 unsigned m_nDefBufSize; // default read or write buffer sizes for buffering small objects
52
53 // flush read/write buffers. You should call this function after the last read or write operation on the object
54 // alternatively, derived (implementation) classes close methods should call this
Flush()55 void Flush()
56 {
57 if (m_pReadBuffer)
58 {
59 unsigned nBufStartPos = DoGetPos();
60 CloseReadBuffer(m_nReadBufPos > m_nBufLenUsed ? m_nReadBufPos : m_nBufLenUsed);
61 DoSetPos(nBufStartPos + m_nReadBufPos);
62 m_pReadBuffer = NULL;
63 m_nReadBufPos = 0;
64 }
65 else if (m_pWriteBuffer)
66 {
67 unsigned nBufStartPos = DoGetPos();
68 CloseWriteBuffer(m_nWriteBufPos > m_nBufLenUsed ? m_nWriteBufPos : m_nBufLenUsed);
69 DoSetPos(nBufStartPos + m_nWriteBufPos);
70 m_pWriteBuffer = NULL;
71 m_nWriteBufPos = 0;
72 }
73 m_nBufSize = 0;
74 m_nBufLenUsed = 0;
75 }
76
77 // use this to write a block of raw data
WriteBlock(void const * pData,unsigned nSize)78 void WriteBlock(void const * pData, unsigned nSize)
79 {
80 Flush();
81 DoWriteBlock(pData,nSize);
82 }
83
84 // this may be faster, but will only work if the block size no more than the default buffer size
WriteBufferedBlock(void const * pData,unsigned nSize)85 void WriteBufferedBlock(void const * pData, unsigned nSize)
86 {
87 if (m_nWriteBufPos + nSize <= m_nBufSize)
88 {
89 memcpy(static_cast<char *>(m_pWriteBuffer) + m_nWriteBufPos/sizeof(char), pData, nSize);
90 m_nWriteBufPos += nSize;
91 }
92 else
93 {
94 Flush();
95 m_pWriteBuffer = GetWriteBuffer(&m_nBufSize,m_nDefBufSize);
96 if (nSize <= m_nBufSize)
97 {
98 memcpy(m_pWriteBuffer, pData, nSize);
99 m_nWriteBufPos = nSize;
100 }
101 else
102 {
103 m_fError |= MME_VEOFMET;
104 }
105 }
106 }
107
108 // use this to read a block of raw data
ReadBlock(void * pData,unsigned nSize)109 void ReadBlock(void * pData, unsigned nSize)
110 {
111 Flush();
112 DoReadBlock(pData,nSize);
113 }
114
115 // this may be faster, but will only work if the block size no more than the default buffer size
ReadBufferedBlock(void * pData,unsigned nSize)116 void ReadBufferedBlock(void * pData, unsigned nSize)
117 {
118 if (m_nReadBufPos + nSize <= m_nBufSize)
119 {
120 memcpy(pData, static_cast<char const *>(m_pReadBuffer) + m_nReadBufPos/sizeof(char), nSize);
121 m_nReadBufPos += nSize;
122 }
123 else
124 {
125 Flush();
126 m_pReadBuffer = GetReadBuffer(&m_nBufSize,m_nDefBufSize);
127 if (nSize <= m_nBufSize)
128 {
129 memcpy(pData, m_pReadBuffer, nSize);
130 m_nReadBufPos = nSize;
131 }
132 else
133 {
134 m_fError |= MME_VEOFMET;
135 }
136 }
137 }
138
139 // move the 'file' pointer nOffset bytes
140 // this will not necessarily cause buffers to be flushed
141 // if the pointer can be moved within the current buffer,
142 // some of the buffer may be left uninitialized, and no
143 // error will occur, which otherwise might (particularly
144 // if the object has write access)
MovePos(signed nOffset)145 void MovePos(signed nOffset)
146 {
147 if (m_pReadBuffer)
148 {
149 if (nOffset>0 && m_nReadBufPos+nOffset<=m_nBufSize)
150 {
151 m_nReadBufPos+=nOffset;
152 return;
153 }
154 else if (nOffset<=0 && m_nReadBufPos>=static_cast<unsigned>(-nOffset))
155 {
156 if (m_nBufLenUsed < m_nReadBufPos) m_nBufLenUsed = m_nReadBufPos;
157 m_nReadBufPos+=nOffset;
158 return;
159 }
160 }
161 else if (m_pWriteBuffer)
162 {
163 if (nOffset>0 && m_nWriteBufPos+nOffset<=m_nBufSize)
164 {
165 m_nWriteBufPos+=nOffset;
166 return;
167 }
168 else if (nOffset<=0 && m_nWriteBufPos>=static_cast<unsigned>(-nOffset))
169 {
170 if (m_nBufLenUsed < m_nWriteBufPos) m_nBufLenUsed = m_nWriteBufPos;
171 m_nWriteBufPos+=nOffset;
172 return;
173 }
174 }
175 // else
176 Flush();
177 DoSetPos(DoGetPos()+nOffset);
178 }
179
180 // set the 'file' pointer
181 // you would normally only pass values which have been
182 // previously returned by a call to GetPos
183 // note that this will not necessarily cause buffers to be flushed
184 // if the pointer can be moved within the current buffer,
185 // some of the buffer may be left uninitialized, and no
186 // error will occur, which otherwise might (particularly
187 // if the object has write access)
SetPos(unsigned nPos)188 void SetPos(unsigned nPos)
189 {
190 unsigned nNewBufPos = nPos - DoGetPos();
191 if (nNewBufPos <= m_nBufSize)
192 {
193 if (m_pReadBuffer)
194 {
195 if (m_nBufLenUsed < m_nReadBufPos) m_nBufLenUsed = m_nReadBufPos;
196 m_nReadBufPos = nNewBufPos;
197 }
198 else // pWriteBuffer
199 {
200 if (m_nBufLenUsed < m_nWriteBufPos) m_nBufLenUsed = m_nWriteBufPos;
201 m_nWriteBufPos = nNewBufPos;
202 }
203 }
204 else
205 {
206 Flush();
207 DoSetPos(nPos);
208 }
209 }
210
211 // get the 'file' pointer. The returned value
212 // can be used in a call to SetPos
GetPos()213 unsigned GetPos()
214 {
215 return DoGetPos()+m_nReadBufPos+m_nWriteBufPos;
216 }
217
218 virtual unsigned GetRemainingSize();
219
220 private:
221 void * m_pWriteBuffer;
222 void const * m_pReadBuffer;
223 unsigned m_nReadBufPos;
224 unsigned m_nWriteBufPos;
225 unsigned m_nBufSize;
226 unsigned m_nBufLenUsed;
227
228 unsigned m_nRefCnt;
229
230 protected:
231
232 // the non-pure functions default implementation sets the unavailable error flag
233
234 // it is safe to assume that these four functions will be called in a logical order
235 // and that only one buffer (read or write) will be required at once
236
237 // this two functions may return NULL only if *pSize is set to zero
238 // *pSize should otherwise be set to the actual size of the buffer returned
239
240 // get a pointer to memory where data can be written to directly
241 virtual void * GetWriteBuffer(unsigned * pSize, unsigned nDesiredSize);
242
243 // get a pointer to memory where data can be read from directly
244 virtual void const * GetReadBuffer(unsigned * pSize, unsigned nDesiredSize);
245
246 // close the buffer 'allocated' above and assume nPosOffset bytes were transferred
247 // and that the 'file' pointer should be positioned at the end of the transferred data
248 virtual void CloseWriteBuffer(unsigned nPosOffset);
249 virtual void CloseReadBuffer(unsigned nPosOffset);
250
251 // transfer a block of data
252 // it is safe to assume that no buffer will be open
253 virtual void DoWriteBlock(void const * pData, unsigned nSize);
254 virtual void DoReadBlock(void * pData, unsigned nSize);
255
256 // if a buffer is open, should return pos at start of buffer
257 virtual unsigned DoGetPos() = 0;
258
259 // it is safe to assume that no buffer will be open
260 virtual void DoSetPos(unsigned nPos) = 0;
261
262 friend class MediaSection;
263
264 friend void MediaRead(MediaMedium * pThis, S8 * p);
265 friend void MediaRead(MediaMedium * pThis, U8 * p);
266 friend void MediaRead(MediaMedium * pThis, S16 * p);
267 friend void MediaRead(MediaMedium * pThis, U16 * p);
268 friend void MediaRead(MediaMedium * pThis, S32 * p);
269 friend void MediaRead(MediaMedium * pThis, U32 * p);
270 };
271
272 // use this to read in simple data types
273 // note especially that if the size of TYPE is greater than the
274 // default buffer size, then the operation will fail
275 // and the virtual end of file error flag will be set
276 // - use ReadBlock instead
MediaRead(MediaMedium * pThis,S8 * p)277 inline void MediaRead(MediaMedium * pThis, S8 * p)
278 {
279 if (pThis->m_nReadBufPos + sizeof(S8) <= pThis->m_nBufSize)
280 {
281 *p = static_cast<S8 const *>(pThis->m_pReadBuffer)[pThis->m_nReadBufPos];
282 pThis->m_nReadBufPos += sizeof(S8);
283 }
284 else
285 {
286 pThis->Flush();
287 pThis->m_pReadBuffer = pThis->GetReadBuffer(&pThis->m_nBufSize,pThis->m_nDefBufSize);
288 if (sizeof(S8) <= pThis->m_nBufSize)
289 {
290 *p = *static_cast<S8 const *>(pThis->m_pReadBuffer);
291 pThis->m_nReadBufPos = sizeof(S8);
292 }
293 else
294 {
295 pThis->m_fError |= MediaMedium::MME_VEOFMET;
296 }
297 }
298 }
299
MediaRead(MediaMedium * pThis,U8 * p)300 inline void MediaRead(MediaMedium * pThis, U8 * p)
301 {
302 if (pThis->m_nReadBufPos + sizeof(U8) <= pThis->m_nBufSize)
303 {
304 *p = static_cast<U8 const *>(pThis->m_pReadBuffer)[pThis->m_nReadBufPos];
305 pThis->m_nReadBufPos += sizeof(U8);
306 }
307 else
308 {
309 pThis->Flush();
310 pThis->m_pReadBuffer = pThis->GetReadBuffer(&pThis->m_nBufSize,pThis->m_nDefBufSize);
311 if (sizeof(U8) <= pThis->m_nBufSize)
312 {
313 *p = *static_cast<U8 const *>(pThis->m_pReadBuffer);
314 pThis->m_nReadBufPos = sizeof(U8);
315 }
316 else
317 {
318 pThis->m_fError |= MediaMedium::MME_VEOFMET;
319 }
320 }
321 }
322
MediaRead(MediaMedium * pThis,S16 * p)323 inline void MediaRead(MediaMedium * pThis, S16 * p)
324 {
325 S8 b0, b1;
326 ::MediaRead(pThis, &b0);
327 ::MediaRead(pThis, &b1);
328 *p = (b0 << 0) | (b1 << 8);
329 }
330
MediaRead(MediaMedium * pThis,U16 * p)331 inline void MediaRead(MediaMedium * pThis, U16 * p)
332 {
333 U8 b0, b1;
334 ::MediaRead(pThis, &b0);
335 ::MediaRead(pThis, &b1);
336 *p = (b0 << 0) | (b1 << 8);
337 }
338
MediaRead(MediaMedium * pThis,S32 * p)339 inline void MediaRead(MediaMedium * pThis, S32 * p)
340 {
341 S8 b0, b1, b2, b3;
342 ::MediaRead(pThis, &b0);
343 ::MediaRead(pThis, &b1);
344 ::MediaRead(pThis, &b2);
345 ::MediaRead(pThis, &b3);
346 *p = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
347 }
348
MediaRead(MediaMedium * pThis,U32 * p)349 inline void MediaRead(MediaMedium * pThis, U32 * p)
350 {
351 U8 b0, b1, b2, b3;
352 ::MediaRead(pThis, &b0);
353 ::MediaRead(pThis, &b1);
354 ::MediaRead(pThis, &b2);
355 ::MediaRead(pThis, &b3);
356 *p = (b0 << 0) | (b1 << 8) | (b2 << 16) | (b3 << 24);
357 }
358
359 #ifdef _MEDIA_WIN_TARGET
360
361 class MediaWinFileMedium : public MediaMedium
362 {
363 public:
MediaWinFileMedium()364 MediaWinFileMedium() : m_hFile(INVALID_HANDLE_VALUE), m_nReadBufLen(0) {}
365
Attach(HANDLE hFile)366 void Attach(HANDLE hFile)
367 {
368 m_hFile = hFile;
369 }
Detach()370 void Detach()
371 {
372 Flush();
373 m_hFile = INVALID_HANDLE_VALUE;
374 }
375
Open(char * pszFileName,DWORD dwDesiredAccess)376 void Open(char *pszFileName, DWORD dwDesiredAccess)
377 {
378 DWORD dwShareMode;
379 DWORD dwCreationDistribution;
380 switch (dwDesiredAccess & (GENERIC_READ|GENERIC_WRITE))
381 {
382 case 0:
383 dwShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE;
384 dwCreationDistribution = OPEN_EXISTING;
385 break;
386 case GENERIC_READ:
387 dwShareMode = FILE_SHARE_READ;
388 dwCreationDistribution = OPEN_EXISTING;
389 break;
390 case GENERIC_WRITE:
391 dwShareMode = 0;
392 dwCreationDistribution = CREATE_ALWAYS;
393 break;
394 default: // GENERIC_WRITE|GENERIC_READ
395 dwCreationDistribution = OPEN_ALWAYS;
396 dwShareMode = 0;
397 }
398 m_hFile = CreateFile
399 (
400 pszFileName,
401 dwDesiredAccess,
402 dwShareMode,
403 0,
404 dwCreationDistribution,
405 FILE_ATTRIBUTE_NORMAL,
406 0
407 );
408 if (INVALID_HANDLE_VALUE == m_hFile)
409 m_fError |= MME_OPENFAIL;
410 }
Close()411 void Close()
412 {
413 if (INVALID_HANDLE_VALUE == m_hFile)
414 m_fError |= MME_CLOSEFAIL;
415 else
416 {
417 Flush();
418 if (!CloseHandle(m_hFile))
419 m_fError |= MME_CLOSEFAIL;
420 else
421 m_hFile = INVALID_HANDLE_VALUE;
422 }
423 }
424
~MediaWinFileMedium()425 ~MediaWinFileMedium()
426 {
427 // should already be closed...
428 Close();
429 }
430
431 virtual unsigned GetRemainingSize();
432
433 private:
434 HANDLE m_hFile;
435
436 char * m_pBuffer;
437 unsigned m_nReadBufLen;
438
439 protected:
440
441 // get a pointer to memory where data can be written to directly
442 virtual void * GetWriteBuffer(unsigned * pSize, unsigned nDesiredSize);
443
444 // get a pointer to memory where data can be read from directly
445 virtual void const * GetReadBuffer(unsigned * pSize, unsigned nDesiredSize);
446
447 // close the buffer allocated above and assume nPosOffset were transferred
448 virtual void CloseWriteBuffer(unsigned nPosOffset);
449 virtual void CloseReadBuffer(unsigned nPosOffset);
450
451 // transfer a block of data: the buffer should be closed
452 virtual void DoWriteBlock(void const * pData, unsigned nSize);
453 virtual void DoReadBlock(void * pData, unsigned nSize);
454
455 // if a buffer is open, should return pos at start of buffer
456 virtual unsigned DoGetPos();
457
458 // requires that no buffer is oben
459 virtual void DoSetPos(unsigned nPos);
460 };
461
462 #endif // _MEDIA_WIN_TARGET
463
464 class MediaStdFileMedium : public MediaMedium
465 {
466 public:
MediaStdFileMedium()467 MediaStdFileMedium() : m_pFile(NULL), m_nReadBufLen(0) {}
468
Attach(FILE * pFile)469 void Attach(FILE * pFile)
470 {
471 m_pFile = pFile;
472 }
Detach()473 void Detach()
474 {
475 Flush();
476 m_pFile = NULL;
477 }
478
Open(char const * pszFileName,char const * pszOpenMode)479 void Open(char const * pszFileName, char const * pszOpenMode)
480 {
481 if (pszOpenMode[0] != 'r' || pszOpenMode[1] != 'b') {
482 fprintf(stderr, "Open(%s, %s)\n", pszFileName, pszOpenMode);
483 m_fError |= MME_OPENFAIL;
484 return;
485 }
486 m_pFile = OpenGameFile(pszFileName, FILEMODE_READONLY, FILETYPE_PERM);
487 if (!m_pFile)
488 m_fError |= MME_OPENFAIL;
489 }
Close()490 void Close()
491 {
492 if (!m_pFile)
493 m_fError |= MME_CLOSEFAIL;
494 else
495 {
496 Flush();
497 if (fclose(m_pFile))
498 m_fError |= MME_CLOSEFAIL;
499 else
500 m_pFile = NULL;
501 }
502 }
503
~MediaStdFileMedium()504 ~MediaStdFileMedium()
505 {
506 // should already be closed...
507 Close();
508 }
509
510 virtual unsigned GetRemainingSize();
511
512 private:
513 FILE * m_pFile;
514
515 char * m_pBuffer;
516 unsigned m_nReadBufLen;
517
518 protected:
519
520 // get a pointer to memory where data can be written to directly
521 virtual void * GetWriteBuffer(unsigned * pSize, unsigned nDesiredSize);
522
523 // get a pointer to memory where data can be read from directly
524 virtual void const * GetReadBuffer(unsigned * pSize, unsigned nDesiredSize);
525
526 // close the buffer allocated above and assume nPosOffset were transferred
527 virtual void CloseWriteBuffer(unsigned nPosOffset);
528 virtual void CloseReadBuffer(unsigned nPosOffset);
529
530 // transfer a block of data: the buffer should be closed
531 virtual void DoWriteBlock(void const * pData, unsigned nSize);
532 virtual void DoReadBlock(void * pData, unsigned nSize);
533
534 // if a buffer is open, should return pos at start of buffer
535 virtual unsigned DoGetPos();
536
537 // requires that no buffer is oben
538 virtual void DoSetPos(unsigned nPos);
539 };
540
541 class MediaMemoryReadMedium : public MediaMedium
542 {
543 public:
MediaMemoryReadMedium()544 MediaMemoryReadMedium() : m_pMem(NULL) {}
545
Open(void const * p)546 void Open(void const * p)
547 {
548 m_pMem = p;
549 m_nOffset = 0;
550 }
551
Close()552 void Close()
553 {
554 if (m_pMem)
555 {
556 Flush();
557 m_pMem = NULL;
558 }
559 else
560 m_fError |= MME_CLOSEFAIL;
561 }
562
563 private:
564 void const * m_pMem;
565
566 protected:
567 unsigned m_nOffset;
568
569 // get a pointer to memory where data can be read from directly
570 virtual void const * GetReadBuffer(unsigned * pSize, unsigned nDesiredSize);
571
572 // close the buffer allocated above and assume nPosOffset were transferred
573 virtual void CloseReadBuffer(unsigned nPosOffset);
574
575 // transfer a block of data: the buffer should be closed
576 virtual void DoReadBlock(void * pData, unsigned nSize);
577
578 // if a buffer is open, should return pos at start of buffer
579 virtual unsigned DoGetPos();
580
581 // requires that no buffer is oben
582 virtual void DoSetPos(unsigned nPos);
583 };
584
585 class MediaMemoryMedium : public MediaMemoryReadMedium
586 {
587 public:
MediaMemoryMedium()588 MediaMemoryMedium() : m_pMem(NULL) {}
589
Open(void * p)590 void Open(void * p)
591 {
592 m_pMem = p;
593 MediaMemoryReadMedium::Open(p);
594 }
595
Close()596 void Close()
597 {
598 MediaMemoryReadMedium::Close();
599 m_pMem = NULL;
600 }
601
602 private:
603 void * m_pMem;
604
605 protected:
606
607 // get a pointer to memory where data can be written to directly
608 virtual void * GetWriteBuffer(unsigned * pSize, unsigned nDesiredSize);
609
610 // close the buffer allocated above and assume nPosOffset were transferred
611 virtual void CloseWriteBuffer(unsigned nPosOffset);
612
613 // transfer a block of data: the buffer should be closed
614 virtual void DoWriteBlock(void const * pData, unsigned nSize);
615 };
616
617
618 class MediaSection : public MediaMedium
619 {
620 public:
MediaSection()621 MediaSection() : m_pMedium(NULL) {}
622
Open(MediaMedium * pMedium,unsigned nMaxSize=UINT_MAX)623 void Open(MediaMedium * pMedium, unsigned nMaxSize = UINT_MAX)
624 {
625 m_pMedium = pMedium;
626 m_nMaxSize = nMaxSize;
627 m_nPos = 0;
628 m_nUsedPos = 0;
629 }
Close()630 void Close()
631 {
632 if (m_pMedium)
633 Flush();
634 if (m_nPos > m_nUsedPos) m_nUsedPos = m_nPos;
635 m_pMedium = NULL;
636 }
GetUsedSize() const637 unsigned GetUsedSize() const
638 {
639 return (m_nPos > m_nUsedPos) ? m_nPos : m_nUsedPos;
640 }
641
642 virtual unsigned GetRemainingSize();
643
644 private:
645 MediaMedium * m_pMedium;
646 unsigned m_nMaxSize;
647 unsigned m_nPos;
648 unsigned m_nUsedPos;
649
650 protected:
651 // get a pointer to memory where data can be written to directly
652 virtual void * GetWriteBuffer(unsigned * pSize, unsigned nDesiredSize);
653
654 // get a pointer to memory where data can be read from directly
655 virtual void const * GetReadBuffer(unsigned * pSize, unsigned nDesiredSize);
656
657 // close the buffer allocated above and assume nPosOffset were transferred
658 virtual void CloseWriteBuffer(unsigned nPosOffset);
659 virtual void CloseReadBuffer(unsigned nPosOffset);
660
661 // transfer a block of data: the buffer should be closed
662 virtual void DoWriteBlock(void const * pData, unsigned nSize);
663 virtual void DoReadBlock(void * pData, unsigned nSize);
664
665 // if a buffer is open, should return pos at start of buffer
666 virtual unsigned DoGetPos();
667
668 // requires that no buffer is oben
669 virtual void DoSetPos(unsigned nPos);
670 };
671
672
673 #endif
674