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