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