1 /*---------------------------------------------------------------------------*
2   Project:  NintendoWare
3   File:     ut_ResUtil.h
4 
5   Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Revision: 27050 $
14  *---------------------------------------------------------------------------*/
15 
16 #ifndef NW_UT_RESUTIL_H_
17 #define NW_UT_RESUTIL_H_
18 
19 #include <nw/types.h>
20 #include <nw/ut/ut_ResTypes.h>
21 #include <nw/ut/ut_Iterator.h>
22 #include <cstring>
23 #include <functional>
24 #include <algorithm>
25 
26 #define NW_VALIDITY_ASSERT \
27     NW_ASSERTMSG(IsValid(), "%s::%s: Object not valid.", GetClassName(), __FUNCTION__)
28 #define NW_INDEX_ASSERT( name, idx ) \
29     NW_ASSERT( 0<= (idx) && static_cast<s32>(idx) < static_cast<s32>(Get##name##Count()) )
30 
31 #ifdef NW_LITTLE_ENDIAN
32     #define NW_RES_SIGNATURE32(val)     \
33         ((((val) & 0x000000FF) << 24) | \
34          (((val) & 0x0000FF00) <<  8) | \
35          (((val) & 0x00FF0000) >>  8) | \
36          (((val) & 0xFF000000) >> 24) )
37 
38     #define NW_RES_SIGNATURE16(val)     \
39         ((((val) & 0x00FF) << 8) |      \
40          (((val) & 0xFF00) >>  8))
41 
42     #define NW_RES_TYPE_INFO(val)       (val)
43 #else
44     #define NW_RES_SIGNATURE32(val)     (val)
45     #define NW_RES_SIGNATURE16(val)     (val)
46     #define NW_RES_TYPE_INFO(val)       \
47         ((((val) & 0x000000FF) << 24) | \
48          (((val) & 0x0000FF00) <<  8) | \
49          (((val) & 0x00FF0000) >>  8) | \
50          (((val) & 0xFF000000) >> 24) )
51 
52 #endif
53 
54 namespace nw {
55 namespace ut {
56 
57 //! @name リソースキャスト関連
58 //@{
59 
60 //---------------------------------------------------------------------------
61 //! @brief        リソース型のオブジェクトを別のリソース型へダイナミックキャストします。
62 //!               全く継承ツリーの異なるクラスのキャストには非対応です。
63 //!
64 //! @tparam       TDown   キャスト先のリソース型です。
65 //! @tparam       TBase   キャスト元のリソース型です。
66 //! @param[in]    res     キャスト元のリソースです。
67 //!
68 //! @return       TDown型のリソースアクセサへキャストして返します。
69 //---------------------------------------------------------------------------
70 template<class TDown, class TBase>
71 NW_INLINE TDown
ResDynamicCast(TBase res)72 ResDynamicCast( TBase res )
73 {
74     if (!res.IsValid())
75     {
76         return TDown(NULL);
77     }
78 
79     if ((res.GetTypeInfo() & TDown::TYPE_INFO) == TDown::TYPE_INFO)
80     {
81         return TDown(res.ptr());
82     }
83     return TDown(NULL);
84 }
85 
86 //---------------------------------------------------------------------------
87 //! @brief        リソース型のオブジェクトを別のリソース型へスタティックキャストします。
88 //!
89 //! @tparam       TDest   キャスト先のリソース型です。
90 //! @tparam       TSrc    キャスト元のリソース型です。
91 //! @param[in]    res     キャスト元のリソースです。
92 //!
93 //! @return       TDest型のリソースアクセサへキャストして返します。
94 //---------------------------------------------------------------------------
95 template<class TDest, class TSrc>
96 NW_INLINE TDest
ResStaticCast(TSrc res)97 ResStaticCast( TSrc res )
98 {
99     // res.ptr() が NULL の場合は チェックを行わずにそのまま NULL を返します。
100     NW_ASSERT( (! res.IsValid()) || ResDynamicCast<TDest>( res ).IsValid() );
101     return TDest( res.ptr() );
102 }
103 
104 //@}
105 
106 //! @details :private
107 typedef struct DataBlockHeader
108 {
109     ResU32 signature;
110     ResU32 length;
111 } DataBlockHeader;
112 
113 /* ------------------------------------------------------------------------
114     NW_RES_CTOR
115 
116     ResCommon<T>を継承するリソースアクセサクラスのコンストラクタを
117     定義するためのマクロ。
118     class_nameにはリソースアクセサクラスのクラス名を入れる。
119     4バイトアラインメントでなくてはならない
120 
121     operator==, operator!=はポインタがNULLの場合でも動作させるために
122     ptr()を使わない
123    ------------------------------------------------------------------------ */
124 #define NW_RES_CTOR_ALIGN(class_name, align)                                       \
125     typedef class_name SelfType;          /*!< :private */                         \
126     typedef class_name##Data ResDataType; /*!< :private */                         \
127                                                                                    \
128     /*! @brief コンストラクタです。*/                                              \
129     /* ctor */ explicit class_name(const void *p = NULL)                           \
130         : nw::ut::ResCommon<class_name##Data>(p) { NW_ASSERT(!((u32)p & ((align)-1))); }   \
131     /*! 実データへの参照を取得します。*/                                           \
132     class_name##Data& ref()                                                        \
133     {                                                                              \
134         NW_VALIDITY_ASSERT;  return *ptr();                                        \
135     }                                                                              \
136     /*! 実データへの const 参照を取得します。*/                                    \
137     const class_name##Data& ref() const                                            \
138     {                                                                              \
139         NW_VALIDITY_ASSERT; return *ptr();                                         \
140     }                                                                              \
141     /*! クラス名の文字列を取得します。*/ /* */                                     \
142     static const char* GetClassName()                                              \
143     {                                                                              \
144         return #class_name;                                                        \
145     }                                                                              \
146     /*! 比較演算子のオーバーロードです。*/                                         \
147     bool operator==(const class_name& rhs) const { return ptr() == rhs.ptr(); }    \
148     /*! 不一致の比較演算子のオーバーロードです。*/                                 \
149     bool operator!=(const class_name& rhs) const { return ptr() != rhs.ptr(); }    \
150 
151 
152 #define NW_RES_CTOR(class_name) NW_RES_CTOR_ALIGN(class_name, 4)
153 
154 
155 #define NW_RES_CTOR_INHERIT(class_name, base_name)                                  \
156     typedef class_name SelfType;          /*!< :private */                          \
157     typedef class_name##Data ResDataType; /*!< :private */                          \
158                                                                                     \
159     /*! @brief コンストラクタです。*/                                               \
160     /* ctor */ explicit class_name(const void* p = NULL) : base_name(p) {}          \
161                                                                                     \
162     /*! @brief 実データへのポインタを取得します。 */                                \
163     ResDataType* ptr()                                                              \
164     {                                                                               \
165         return reinterpret_cast<ResDataType*>(void_ptr());                          \
166     }                                                                               \
167     /*! @brief 実データへの const ポインタを取得します。 */                         \
168     const ResDataType* ptr() const                                                  \
169     {                                                                               \
170         return reinterpret_cast<const ResDataType*>(void_ptr());                    \
171     }                                                                               \
172     /*! @brief 実データへの参照を取得します。 */                                    \
173     ResDataType& ref()                                                              \
174     {                                                                               \
175         NW_NULL_ASSERT(void_ptr());                                                 \
176         return *reinterpret_cast<ResDataType*>(void_ptr());                         \
177     }                                                                               \
178     /*! @brief 実データへの const 参照を取得します。 */                             \
179     const ResDataType& ref() const                                                  \
180     {                                                                               \
181         NW_NULL_ASSERT(void_ptr());                                                 \
182         return *reinterpret_cast<const ResDataType*>(void_ptr());                   \
183     }                                                                               \
184     /*! クラス名の文字列を取得します。*/                                     \
185     static const char* GetClassName() { return #class_name; }                       \
186     /*! 比較演算子のオーバーロードです。*/                                   \
187     bool operator==(const class_name& rhs) const { return ptr() == rhs.ptr(); }     \
188     /*! 不一致の比較演算子のオーバーロードです。*/                           \
189     bool operator!=(const class_name& rhs) const { return ptr() != rhs.ptr(); }     \
190 
191 
192 /*------------------------------------------------------------------------
193     共通部分の定義
194 
195     Tを実体化したクラスを継承して使う。おおよそのアクセサの基底クラスにできる
196     データメンバはmpData以外に存在してはならない
197   ------------------------------------------------------------------------*/
198 //--------------------------------------------------------------------------
199 //! @brief        リソースアクセサの基底クラスの共通部分定義です。
200 //---------------------------------------------------------------------------
201 class ResCommonBase
202 {
203 private:
204     void* mpData;
205 
206 public:
207     //! @brief コンストラクタです。
ResCommonBase(void * p)208     explicit ResCommonBase(void *p) : mpData(p) {}
ResCommonBase(const void * p)209     explicit ResCommonBase(const void* p) : mpData(const_cast<void*>(p)) {}
210 
211     //--------------------------------------------------------------------------
212     //! @brief        リソースへのポインタが null でないかどうかを判定します。
213     //!
214     //! @return       保持しているリソースへのポインタが null の場合には false、
215     //!               null でない場合には true を返します。
216     //---------------------------------------------------------------------------
IsValid()217     bool IsValid() const { return (mpData != NULL); }
218 
219 protected:
220     //! @details :private
void_ptr()221     NW_FORCE_INLINE void*       void_ptr()       { return mpData; }
222     //! @details :private
void_ptr()223     NW_FORCE_INLINE const void* void_ptr() const { return mpData; }
224 
225     // 構造体の先頭からofsバイト先のポインタを返します。ofsが0の場合はNULLを返します。
226     //! @details :private
227     template<class X>
ofs_to_ptr(Offset ofs)228     X* ofs_to_ptr(Offset ofs)
229     {
230         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
231         u8* p = reinterpret_cast<u8*>(mpData);
232         if (ofs != 0)
233         {
234             return reinterpret_cast<X*>(p + ofs);
235         }
236         else
237         {
238             return NULL;
239         }
240     }
241 
242     // 構造体の先頭からofsバイト先のポインタを返します。ofsが0の場合はNULLを返します。
243     //! @details :private
244     template<class X>
ofs_to_ptr(Offset ofs)245     const X* ofs_to_ptr(Offset ofs) const
246     {
247         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
248         const u8* p = reinterpret_cast<const u8*>(mpData);
249         if (ofs != 0)
250         {
251             return reinterpret_cast<const X*>(p + ofs);
252         }
253         else
254         {
255             return NULL;
256         }
257     }
258 
259     // 構造体の先頭からofsバイト先のポインタを引数にしたアクセサオブジェクトを返します。
260     // ofsが0の場合はNULLを引数にしたアクセサオブジェクトを返します。
261     //! @details :private
262     template<class X>
ofs_to_obj(Offset ofs)263     X ofs_to_obj(Offset ofs)
264     {
265         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
266         u8* p = reinterpret_cast<u8*>(mpData);
267         if (ofs != 0)
268         {
269             return X(p + ofs);
270         }
271         else
272         {
273             return X(NULL);
274         }
275     }
276 
277     // 構造体の先頭からofsバイト先のポインタを引数にしたアクセサオブジェクトを返します。
278     // ofsが0の場合はNULLを引数にしたアクセサオブジェクトを返します。
279     //! @details :private
280     template<class X>
ofs_to_obj(Offset ofs)281     const X ofs_to_obj(Offset ofs) const
282     {
283         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
284         const u8* p = reinterpret_cast<const u8*>(mpData);
285         if (ofs != 0)
286         {
287             return X(const_cast<u8*>(p + ofs));
288         }
289         else
290         {
291             return X(NULL);
292         }
293     }
294 
295     // ofs_to_ptrで、ofs!=0であることが保証できる場合に使用できます。
296     //! @details :private
297     template<class X>
ofs_to_ptr_raw(Offset ofs)298     X* ofs_to_ptr_raw(Offset ofs)
299     {
300         NW_ASSERT(ofs != 0);
301         return reinterpret_cast<X*>(reinterpret_cast<u8*>(mpData) + ofs);
302     }
303 
304     // ofs_to_ptrで、ofs!=0であることが保証できる場合に使用できます。
305     //! @details :private
306     template<class X>
ofs_to_ptr_raw(Offset ofs)307     const X* ofs_to_ptr_raw(Offset ofs) const
308     {
309         NW_ASSERT(ofs != 0);
310         return reinterpret_cast<const X*>(reinterpret_cast<const u8*>(mpData) + ofs);
311     }
312 
313     // 状態を Invalid にします。 参照していたデータの実体の破棄は行ないません。
314     //! @details :private
invalidate()315     void invalidate() { mpData = NULL; }
316 };
317 
318 
319 //--------------------------------------------------------------------------
320 //! @brief        リソースアクセサの基底クラスです。
321 //---------------------------------------------------------------------------
322 template<class T>
323 class ResCommon : public ResCommonBase
324 {
325 public:
326     //! @brief コンストラクタです。
ResCommon(void * p)327     explicit ResCommon(void *p) : ResCommonBase(p) {}
ResCommon(const void * p)328     explicit ResCommon(const void* p) : ResCommonBase(p) {}
329 
330     //! @brief バイナリリソースの構造体へのポインタを返します
ptr()331     NW_FORCE_INLINE T* ptr() { return reinterpret_cast<T*>(void_ptr()); }
332 
333     //! @brief バイナリリソースの構造体へのポインタを返します
ptr()334     NW_FORCE_INLINE const T* ptr() const { return reinterpret_cast<const T*>(void_ptr()); }
335 
336     //! @brief バイナリリソースの構造体へのリファレンスを返します
ref()337     NW_FORCE_INLINE T& ref() { NW_ASSERT(this->IsValid()); return *reinterpret_cast<T*>(void_ptr()); }
338 
339     //! @brief バイナリリソースの構造体へのリファレンスを返します
ref()340     NW_FORCE_INLINE const T& ref() const { NW_ASSERT(this->IsValid()); return *reinterpret_cast<const T*>(void_ptr()); }
341 };
342 
343 //----------------------------------------
344 //! @name リソース関連
345 //@{
346 
347 //---------------------------------------------------------------------------
348 //! @brief        リソースの有無を調べて破棄するためのデリーターです。
349 //!
350 //! @tparam       削除するオブジェクトです。
351 //---------------------------------------------------------------------------
352 template<typename TResource>
353 NW_INLINE void
SafeCleanup(TResource res)354 SafeCleanup(
355     TResource res
356 )
357 {
358     if (res.IsValid())
359     {
360         res.Cleanup();
361     }
362 }
363 
364 //---------------------------------------------------------------------------
365 //! @brief        リソースを破棄するためのデリーターです。
366 //!
367 //! @tparam       削除するオブジェクトです。
368 //---------------------------------------------------------------------------
369 template<typename TObject>
370 struct SafeCleanupper : public std::unary_function<TObject, void>
371 {
372     //! Resourceを破棄します。
operatorSafeCleanupper373     void operator()(TObject res) const
374     {
375         SafeCleanup(res);
376     }
377 };
378 
379 //---------------------------------------------------------------------------
380 //! @brief        SafeCleanup でコンテナ要素の全てのリソースを破棄するための関数です。
381 //!
382 //! @tparam       TArray 削除するリソースの配列型です。
383 //!
384 //! @param[in]    array 削除するリソースの配列です。
385 //---------------------------------------------------------------------------
386 template<typename TArray>
387 NW_INLINE void
SafeCleanupAll(TArray array)388 SafeCleanupAll(
389     TArray array
390 )
391 {
392     std::for_each(array.begin(), array.end(), SafeCleanupper<typename TArray::value_type>());
393 }
394 
395 //@}
396 
397 // 名前クラス
398 //! @details :private
399 struct ResNameData
400 {
401     ResS32 len;
402     char   str[4];  // 実際には長さlenの文字列が入っている。末尾は4バイトアラインメントされていて、パディングは0である。
403 };
404 
405 
406 //--------------------------------------------------------------------------
407 //! @brief        名前リソースを表すクラスです。
408 //---------------------------------------------------------------------------
409 class ResName : public ResCommon<ResNameData>
410 {
411 public:
NW_RES_CTOR(ResName)412     NW_RES_CTOR( ResName )
413 
414     //! @brief 文字列の長さを返します
415     s32 GetLength() const { return ref().len; }
416 
417     //! @brief 文字列へのポインタを返します
GetName()418     const char* GetName() const { return &ref().str[0]; }
419 
420     //! @brief 長さ付き文字列と比較します
Equals(const char * str,size_t len)421     bool Equals(const char* str, size_t len) const
422         { return (GetLength() == len) && (::std::strcmp(GetName(), str) == 0); }
423 };
424 
425 
426 // ファイル情報クラス
427 //! @details :private
428 struct ResFileData
429 {
430     ResU32  signature;
431     ResU16  byteOrder;
432     ResU16  headerSize;
433     ResU32  revision;
434     ResU32  fileSize;
435 };
436 
437 //--------------------------------------------------------------------------
438 //! @brief        ファイルリソースを表すクラスです。
439 //---------------------------------------------------------------------------
440 class ResFile : public ResCommon<ResFileData>
441 {
442 private:
443     static const u16 BOM = 0xFEFF;
444     static const u32 SIGNATURE = 'NWFL';
445 
446 public:
NW_RES_CTOR(ResFile)447     NW_RES_CTOR( ResFile )
448 
449     //! @brief ビッグエンディアンかどうかを取得します。
450     bool IsBigEndian() const
451     {
452         NW_ASSERT( (ref().byteOrder == BOM) || (ref().byteOrder == BOM) );
453     #if defined( NW_SWAP_ENDIAN )
454         return *(u8*)(&ref().byteOrder) == 0xFE;
455     #else
456         return *(u8*)(&ref().byteOrder) == 0xFF;
457     #endif
458     }
459 
460     //! @brief リトルエンディアンかどうかを取得します。
IsLittleEndian()461     bool IsLittleEndian() const
462     {
463         NW_ASSERT( (ref().byteOrder == BOM) || (ref().byteOrder == BOM) );
464     #if defined( NW_SWAP_ENDIAN )
465         return *(u8*)(&ref().byteOrder) == 0xFF;
466     #else
467         return *(u8*)(&ref().byteOrder) == 0xFE;
468     #endif
469     }
470 
471     //! @brief バイトオーダーがサポート対象であるかをチェックします。
TestByteOrder()472     bool  TestByteOrder() const { return ref().byteOrder == BOM;       }
473     //! @brief シグニチャが正しいかどうかをチェックします。
TestSignature()474     bool  TestSignature() const { return ref().signature == SIGNATURE; }
475 
476     //! @brief リビジョンを取得します。
GetRevision()477     u32   GetRevision()   const { return ref().revision;   }
478 
479     //! @brief ファイルサイズを取得します。
GetFileSize()480     u32   GetFileSize()   const { return ref().fileSize;   }
481 
482     //! @brief ファイルヘッダサイズを取得します。
GetHeaderSize()483     u16   GetHeaderSize() const { return ref().headerSize; }
484 
485     //! @brief データアドレスを取得します。
GetDataAddress()486     void*       GetDataAddress() { return (reinterpret_cast<u8*>(ptr()) + u32(ref().headerSize)); }
487 
488     //! @brief データアドレスを const ポインタで取得します。
GetDataAddress()489     const void* GetDataAddress() const { return (reinterpret_cast<const u8*>(ptr()) + u32(ref().headerSize)); }
490 };
491 
492 } /* namespace ut */
493 } /* namespace nw */
494 
495 #endif /* NW_UT_RESUTIL_H_ */
496