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: 20555 $
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     NW_ASSERT( (! res.IsValid()) || ResDynamicCast<TDest>( res ).IsValid() );
100     return TDest( res.ptr() );
101 }
102 
103 //@}
104 
105 //! @details :private
106 typedef struct DataBlockHeader
107 {
108     ResU32 signature;
109     ResU32 length;
110 } DataBlockHeader;
111 
112 /* ------------------------------------------------------------------------
113     NW_RES_CTOR
114 
115     ResCommon<T>を継承するリソースアクセサクラスのコンストラクタを
116     定義するためのマクロ。
117     class_nameにはリソースアクセサクラスのクラス名を入れる。
118     4バイトアラインメントでなくてはならない
119 
120     operator==, operator!=はポインタがNULLの場合でも動作させるために
121     ptr()を使わない
122    ------------------------------------------------------------------------ */
123 #define NW_RES_CTOR_ALIGN(class_name, align)                                       \
124     typedef class_name SelfType;          /*!< :private */                         \
125     typedef class_name##Data ResDataType; /*!< :private */                         \
126                                                                                    \
127     /*! @brief コンストラクタです。*/                                              \
128     /* ctor */ explicit class_name(const void *p = NULL)                           \
129         : nw::ut::ResCommon<class_name##Data>(p) { NW_ASSERT(!((u32)p & ((align)-1))); }   \
130     /*! 実データへの参照を取得します。*/                                           \
131     class_name##Data& ref()                                                        \
132     {                                                                              \
133         NW_VALIDITY_ASSERT;  return *ptr();                                        \
134     }                                                                              \
135     /*! 実データへの const 参照を取得します。*/                                    \
136     const class_name##Data& ref() const                                            \
137     {                                                                              \
138         NW_VALIDITY_ASSERT; return *ptr();                                         \
139     }                                                                              \
140     /*! クラス名の文字列を取得します。*/ /* */                                     \
141     static const char* GetClassName()                                              \
142     {                                                                              \
143         return #class_name;                                                        \
144     }                                                                              \
145     /*! 比較演算子のオーバーロードです。*/                                         \
146     bool operator==(const class_name& rhs) const { return ptr() == rhs.ptr(); }    \
147     /*! 不一致の比較演算子のオーバーロードです。*/                                 \
148     bool operator!=(const class_name& rhs) const { return ptr() != rhs.ptr(); }    \
149 
150 
151 #define NW_RES_CTOR(class_name) NW_RES_CTOR_ALIGN(class_name, 4)
152 
153 
154 #define NW_RES_CTOR_INHERIT(class_name, base_name)                                  \
155     typedef class_name SelfType;          /*!< :private */                          \
156     typedef class_name##Data ResDataType; /*!< :private */                          \
157                                                                                     \
158     /*! @brief コンストラクタです。*/                                               \
159     /* ctor */ explicit class_name(const void* p = NULL) : base_name(p) {}          \
160                                                                                     \
161     /*! @brief 実データへのポインタを取得します。 */                                \
162     ResDataType* ptr()                                                              \
163     {                                                                               \
164         return reinterpret_cast<ResDataType*>(void_ptr());                          \
165     }                                                                               \
166     /*! @brief 実データへの const ポインタを取得します。 */                         \
167     const ResDataType* ptr() const                                                  \
168     {                                                                               \
169         return reinterpret_cast<const ResDataType*>(void_ptr());                    \
170     }                                                                               \
171     /*! @brief 実データへの参照を取得します。 */                                    \
172     ResDataType& ref()                                                              \
173     {                                                                               \
174         NW_NULL_ASSERT(void_ptr());                                                 \
175         return *reinterpret_cast<ResDataType*>(void_ptr());                         \
176     }                                                                               \
177     /*! @brief 実データへの const 参照を取得します。 */                             \
178     const ResDataType& ref() const                                                  \
179     {                                                                               \
180         NW_NULL_ASSERT(void_ptr());                                                 \
181         return *reinterpret_cast<const ResDataType*>(void_ptr());                   \
182     }                                                                               \
183     /*! クラス名の文字列を取得します。*/                                     \
184     static const char* GetClassName() { return #class_name; }                       \
185     /*! 比較演算子のオーバーロードです。*/                                   \
186     bool operator==(const class_name& rhs) const { return ptr() == rhs.ptr(); }     \
187     /*! 不一致の比較演算子のオーバーロードです。*/                           \
188     bool operator!=(const class_name& rhs) const { return ptr() != rhs.ptr(); }     \
189 
190 
191 /*------------------------------------------------------------------------
192     共通部分の定義
193 
194     Tを実体化したクラスを継承して使う。おおよそのアクセサの基底クラスにできる
195     データメンバはmpData以外に存在してはならない
196   ------------------------------------------------------------------------*/
197 //--------------------------------------------------------------------------
198 //! @brief        リソースアクセサの基底クラスの共通部分定義です。
199 //---------------------------------------------------------------------------
200 class ResCommonBase
201 {
202 private:
203     void* mpData;
204 
205 public:
206     //! @brief コンストラクタです。
ResCommonBase(void * p)207     explicit ResCommonBase(void *p) : mpData(p) {}
ResCommonBase(const void * p)208     explicit ResCommonBase(const void* p) : mpData(const_cast<void*>(p)) {}
209 
210     //--------------------------------------------------------------------------
211     //! @brief        リソースへのポインタが null でないかどうかを判定します。
212     //!
213     //! @return       保持しているリソースへのポインタが null の場合には false、
214     //!               null でない場合には true を返します。
215     //---------------------------------------------------------------------------
IsValid()216     bool IsValid() const { return (mpData != NULL); }
217 
218 protected:
219     //! @details :private
void_ptr()220     NW_FORCE_INLINE void*       void_ptr()       { return mpData; }
221     //! @details :private
void_ptr()222     NW_FORCE_INLINE const void* void_ptr() const { return mpData; }
223 
224     // 構造体の先頭からofsバイト先のポインタを返します。ofsが0の場合はNULLを返します。
225     //! @details :private
226     template<class X>
ofs_to_ptr(Offset ofs)227     X* ofs_to_ptr(Offset ofs)
228     {
229         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
230         u8* p = reinterpret_cast<u8*>(mpData);
231         if (ofs != 0)
232         {
233             return reinterpret_cast<X*>(p + ofs);
234         }
235         else
236         {
237             return NULL;
238         }
239     }
240 
241     // 構造体の先頭からofsバイト先のポインタを返します。ofsが0の場合はNULLを返します。
242     //! @details :private
243     template<class X>
ofs_to_ptr(Offset ofs)244     const X* ofs_to_ptr(Offset ofs) const
245     {
246         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
247         const u8* p = reinterpret_cast<const u8*>(mpData);
248         if (ofs != 0)
249         {
250             return reinterpret_cast<const X*>(p + ofs);
251         }
252         else
253         {
254             return NULL;
255         }
256     }
257 
258     // 構造体の先頭からofsバイト先のポインタを引数にしたアクセサオブジェクトを返します。
259     // ofsが0の場合はNULLを引数にしたアクセサオブジェクトを返します。
260     //! @details :private
261     template<class X>
ofs_to_obj(Offset ofs)262     X ofs_to_obj(Offset ofs)
263     {
264         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
265         u8* p = reinterpret_cast<u8*>(mpData);
266         if (ofs != 0)
267         {
268             return X(p + ofs);
269         }
270         else
271         {
272             return X(NULL);
273         }
274     }
275 
276     // 構造体の先頭からofsバイト先のポインタを引数にしたアクセサオブジェクトを返します。
277     // ofsが0の場合はNULLを引数にしたアクセサオブジェクトを返します。
278     //! @details :private
279     template<class X>
ofs_to_obj(Offset ofs)280     const X ofs_to_obj(Offset ofs) const
281     {
282         // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない
283         const u8* p = reinterpret_cast<const u8*>(mpData);
284         if (ofs != 0)
285         {
286             return X(const_cast<u8*>(p + ofs));
287         }
288         else
289         {
290             return X(NULL);
291         }
292     }
293 
294     // ofs_to_ptrで、ofs!=0であることが保証できる場合に使用できます。
295     //! @details :private
296     template<class X>
ofs_to_ptr_raw(Offset ofs)297     X* ofs_to_ptr_raw(Offset ofs)
298     {
299         NW_ASSERT(ofs != 0);
300         return reinterpret_cast<X*>(reinterpret_cast<u8*>(mpData) + ofs);
301     }
302 
303     // ofs_to_ptrで、ofs!=0であることが保証できる場合に使用できます。
304     //! @details :private
305     template<class X>
ofs_to_ptr_raw(Offset ofs)306     const X* ofs_to_ptr_raw(Offset ofs) const
307     {
308         NW_ASSERT(ofs != 0);
309         return reinterpret_cast<const X*>(reinterpret_cast<const u8*>(mpData) + ofs);
310     }
311 
312     // 状態を Invalid にします。 参照していたデータの実体の破棄は行ないません。
313     //! @details :private
invalidate()314     void invalidate() { mpData = NULL; }
315 };
316 
317 
318 //--------------------------------------------------------------------------
319 //! @brief        リソースアクセサの基底クラスです。
320 //---------------------------------------------------------------------------
321 template<class T>
322 class ResCommon : public ResCommonBase
323 {
324 public:
325     //! @brief コンストラクタです。
ResCommon(void * p)326     explicit ResCommon(void *p) : ResCommonBase(p) {}
ResCommon(const void * p)327     explicit ResCommon(const void* p) : ResCommonBase(p) {}
328 
329     //! @brief バイナリリソースの構造体へのポインタを返します
ptr()330     NW_FORCE_INLINE T* ptr() { return reinterpret_cast<T*>(void_ptr()); }
331 
332     //! @brief バイナリリソースの構造体へのポインタを返します
ptr()333     NW_FORCE_INLINE const T* ptr() const { return reinterpret_cast<const T*>(void_ptr()); }
334 
335     //! @brief バイナリリソースの構造体へのリファレンスを返します
ref()336     NW_FORCE_INLINE T& ref() { NW_ASSERT(this->IsValid()); return *reinterpret_cast<T*>(void_ptr()); }
337 
338     //! @brief バイナリリソースの構造体へのリファレンスを返します
ref()339     NW_FORCE_INLINE const T& ref() const { NW_ASSERT(this->IsValid()); return *reinterpret_cast<const T*>(void_ptr()); }
340 };
341 
342 //----------------------------------------
343 //! @name リソース関連
344 //@{
345 
346 //---------------------------------------------------------------------------
347 //! @brief        リソースの有無を調べて破棄するためのデリーターです。
348 //!
349 //! @tparam       削除するオブジェクトです。
350 //---------------------------------------------------------------------------
351 template<typename TResource>
352 NW_INLINE void
SafeCleanup(TResource res)353 SafeCleanup(
354     TResource res
355 )
356 {
357     if (res.IsValid())
358     {
359         res.Cleanup();
360     }
361 }
362 
363 //---------------------------------------------------------------------------
364 //! @brief        リソースを破棄するためのデリーターです。
365 //!
366 //! @tparam       削除するオブジェクトです。
367 //---------------------------------------------------------------------------
368 template<typename TObject>
369 struct SafeCleanupper : public std::unary_function<TObject, void>
370 {
371     //! Resourceを破棄します。
operatorSafeCleanupper372     void operator()(TObject res) const
373     {
374         SafeCleanup(res);
375     }
376 };
377 
378 //---------------------------------------------------------------------------
379 //! @brief        SafeCleanup でコンテナ要素の全てのリソースを破棄するための関数です。
380 //!
381 //! @tparam       TArray 削除するリソースの配列型です。
382 //!
383 //! @param[in]    array 削除するリソースの配列です。
384 //---------------------------------------------------------------------------
385 template<typename TArray>
386 NW_INLINE void
SafeCleanupAll(TArray array)387 SafeCleanupAll(
388     TArray array
389 )
390 {
391     std::for_each(array.begin(), array.end(), SafeCleanupper<typename TArray::value_type>());
392 }
393 
394 //@}
395 
396 // 名前クラス
397 //! @details :private
398 struct ResNameData
399 {
400     ResS32 len;
401     char   str[4];  // 実際には長さlenの文字列が入っている。末尾は4バイトアラインメントされていて、パディングは0である。
402 };
403 
404 
405 //--------------------------------------------------------------------------
406 //! @brief        名前リソースを表すクラスです。
407 //---------------------------------------------------------------------------
408 class ResName : public ResCommon<ResNameData>
409 {
410 public:
NW_RES_CTOR(ResName)411     NW_RES_CTOR( ResName )
412 
413     //! @brief 文字列の長さを返します
414     s32 GetLength() const { return ref().len; }
415 
416     //! @brief 文字列へのポインタを返します
GetName()417     const char* GetName() const { return &ref().str[0]; }
418 
419     //! @brief 長さ付き文字列と比較します
Equals(const char * str,size_t len)420     bool Equals(const char* str, size_t len) const
421         { return (GetLength() == len) && (::std::strcmp(GetName(), str) == 0); }
422 };
423 
424 
425 // ファイル情報クラス
426 //! @details :private
427 struct ResFileData
428 {
429     ResU32  signature;
430     ResU16  byteOrder;
431     ResU16  headerSize;
432     ResU32  revision;
433     ResU32  fileSize;
434 };
435 
436 //--------------------------------------------------------------------------
437 //! @brief        ファイルリソースを表すクラスです。
438 //---------------------------------------------------------------------------
439 class ResFile : public ResCommon<ResFileData>
440 {
441 private:
442     static const u16 BOM = 0xFEFF;
443     static const u32 SIGNATURE = 'NWFL';
444 
445 public:
NW_RES_CTOR(ResFile)446     NW_RES_CTOR( ResFile )
447 
448     //! @brief ビッグエンディアンかどうかを取得します。
449     bool IsBigEndian() const
450     {
451         NW_ASSERT( (ref().byteOrder == BOM) || (ref().byteOrder == BOM) );
452     #if defined( NW_SWAP_ENDIAN )
453         return *(u8*)(&ref().byteOrder) == 0xFE;
454     #else
455         return *(u8*)(&ref().byteOrder) == 0xFF;
456     #endif
457     }
458 
459     //! @brief リトルエンディアンかどうかを取得します。
IsLittleEndian()460     bool IsLittleEndian() const
461     {
462         NW_ASSERT( (ref().byteOrder == BOM) || (ref().byteOrder == BOM) );
463     #if defined( NW_SWAP_ENDIAN )
464         return *(u8*)(&ref().byteOrder) == 0xFF;
465     #else
466         return *(u8*)(&ref().byteOrder) == 0xFE;
467     #endif
468     }
469 
470     //! @brief バイトオーダーがサポート対象であるかをチェックします。
TestByteOrder()471     bool  TestByteOrder() const { return ref().byteOrder == BOM;       }
472     //! @brief シグニチャが正しいかどうかをチェックします。
TestSignature()473     bool  TestSignature() const { return ref().signature == SIGNATURE; }
474 
475     //! @brief リビジョンを取得します。
GetRevision()476     u32   GetRevision()   const { return ref().revision;   }
477 
478     //! @brief ファイルサイズを取得します。
GetFileSize()479     u32   GetFileSize()   const { return ref().fileSize;   }
480 
481     //! @brief ファイルヘッダサイズを取得します。
GetHeaderSize()482     u16   GetHeaderSize() const { return ref().headerSize; }
483 
484     //! @brief データアドレスを取得します。
GetDataAddress()485     void*       GetDataAddress() { return (reinterpret_cast<u8*>(ptr()) + u32(ref().headerSize)); }
486 
487     //! @brief データアドレスを const ポインタで取得します。
GetDataAddress()488     const void* GetDataAddress() const { return (reinterpret_cast<const u8*>(ptr()) + u32(ref().headerSize)); }
489 };
490 
491 } /* namespace ut */
492 } /* namespace nw */
493 
494 #endif /* NW_UT_RESUTIL_H_ */
495