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