/*---------------------------------------------------------------------------* Project: NintendoWare File: ut_ResUtil.h Copyright (C)2009-2010 Nintendo Co., Ltd./HAL Laboratory, Inc. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Revision: 20555 $ *---------------------------------------------------------------------------*/ #ifndef NW_UT_RESUTIL_H_ #define NW_UT_RESUTIL_H_ #include #include #include #include #include #include #define NW_VALIDITY_ASSERT \ NW_ASSERTMSG(IsValid(), "%s::%s: Object not valid.", GetClassName(), __FUNCTION__) #define NW_INDEX_ASSERT( name, idx ) \ NW_ASSERT( 0<= (idx) && static_cast(idx) < static_cast(Get##name##Count()) ) #ifdef NW_LITTLE_ENDIAN #define NW_RES_SIGNATURE32(val) \ ((((val) & 0x000000FF) << 24) | \ (((val) & 0x0000FF00) << 8) | \ (((val) & 0x00FF0000) >> 8) | \ (((val) & 0xFF000000) >> 24) ) #define NW_RES_SIGNATURE16(val) \ ((((val) & 0x00FF) << 8) | \ (((val) & 0xFF00) >> 8)) #define NW_RES_TYPE_INFO(val) (val) #else #define NW_RES_SIGNATURE32(val) (val) #define NW_RES_SIGNATURE16(val) (val) #define NW_RES_TYPE_INFO(val) \ ((((val) & 0x000000FF) << 24) | \ (((val) & 0x0000FF00) << 8) | \ (((val) & 0x00FF0000) >> 8) | \ (((val) & 0xFF000000) >> 24) ) #endif namespace nw { namespace ut { //! @name リソースキャスト関連 //@{ //--------------------------------------------------------------------------- //! @brief リソース型のオブジェクトを別のリソース型へダイナミックキャストします。 //! 全く継承ツリーの異なるクラスのキャストには非対応です。 //! //! @tparam TDown キャスト先のリソース型です。 //! @tparam TBase キャスト元のリソース型です。 //! @param[in] res キャスト元のリソースです。 //! //! @return TDown型のリソースアクセサへキャストして返します。 //--------------------------------------------------------------------------- template NW_INLINE TDown ResDynamicCast( TBase res ) { if (!res.IsValid()) { return TDown(NULL); } if ((res.GetTypeInfo() & TDown::TYPE_INFO) == TDown::TYPE_INFO) { return TDown(res.ptr()); } return TDown(NULL); } //--------------------------------------------------------------------------- //! @brief リソース型のオブジェクトを別のリソース型へスタティックキャストします。 //! //! @tparam TDest キャスト先のリソース型です。 //! @tparam TSrc キャスト元のリソース型です。 //! @param[in] res キャスト元のリソースです。 //! //! @return TDest型のリソースアクセサへキャストして返します。 //--------------------------------------------------------------------------- template NW_INLINE TDest ResStaticCast( TSrc res ) { NW_ASSERT( (! res.IsValid()) || ResDynamicCast( res ).IsValid() ); return TDest( res.ptr() ); } //@} //! @details :private typedef struct DataBlockHeader { ResU32 signature; ResU32 length; } DataBlockHeader; /* ------------------------------------------------------------------------ NW_RES_CTOR ResCommonを継承するリソースアクセサクラスのコンストラクタを 定義するためのマクロ。 class_nameにはリソースアクセサクラスのクラス名を入れる。 4バイトアラインメントでなくてはならない operator==, operator!=はポインタがNULLの場合でも動作させるために ptr()を使わない ------------------------------------------------------------------------ */ #define NW_RES_CTOR_ALIGN(class_name, align) \ typedef class_name SelfType; /*!< :private */ \ typedef class_name##Data ResDataType; /*!< :private */ \ \ /*! @brief コンストラクタです。*/ \ /* ctor */ explicit class_name(const void *p = NULL) \ : nw::ut::ResCommon(p) { NW_ASSERT(!((u32)p & ((align)-1))); } \ /*! 実データへの参照を取得します。*/ \ class_name##Data& ref() \ { \ NW_VALIDITY_ASSERT; return *ptr(); \ } \ /*! 実データへの const 参照を取得します。*/ \ const class_name##Data& ref() const \ { \ NW_VALIDITY_ASSERT; return *ptr(); \ } \ /*! クラス名の文字列を取得します。*/ /* */ \ static const char* GetClassName() \ { \ return #class_name; \ } \ /*! 比較演算子のオーバーロードです。*/ \ bool operator==(const class_name& rhs) const { return ptr() == rhs.ptr(); } \ /*! 不一致の比較演算子のオーバーロードです。*/ \ bool operator!=(const class_name& rhs) const { return ptr() != rhs.ptr(); } \ #define NW_RES_CTOR(class_name) NW_RES_CTOR_ALIGN(class_name, 4) #define NW_RES_CTOR_INHERIT(class_name, base_name) \ typedef class_name SelfType; /*!< :private */ \ typedef class_name##Data ResDataType; /*!< :private */ \ \ /*! @brief コンストラクタです。*/ \ /* ctor */ explicit class_name(const void* p = NULL) : base_name(p) {} \ \ /*! @brief 実データへのポインタを取得します。 */ \ ResDataType* ptr() \ { \ return reinterpret_cast(void_ptr()); \ } \ /*! @brief 実データへの const ポインタを取得します。 */ \ const ResDataType* ptr() const \ { \ return reinterpret_cast(void_ptr()); \ } \ /*! @brief 実データへの参照を取得します。 */ \ ResDataType& ref() \ { \ NW_NULL_ASSERT(void_ptr()); \ return *reinterpret_cast(void_ptr()); \ } \ /*! @brief 実データへの const 参照を取得します。 */ \ const ResDataType& ref() const \ { \ NW_NULL_ASSERT(void_ptr()); \ return *reinterpret_cast(void_ptr()); \ } \ /*! クラス名の文字列を取得します。*/ \ static const char* GetClassName() { return #class_name; } \ /*! 比較演算子のオーバーロードです。*/ \ bool operator==(const class_name& rhs) const { return ptr() == rhs.ptr(); } \ /*! 不一致の比較演算子のオーバーロードです。*/ \ bool operator!=(const class_name& rhs) const { return ptr() != rhs.ptr(); } \ /*------------------------------------------------------------------------ 共通部分の定義 Tを実体化したクラスを継承して使う。おおよそのアクセサの基底クラスにできる データメンバはmpData以外に存在してはならない ------------------------------------------------------------------------*/ //-------------------------------------------------------------------------- //! @brief リソースアクセサの基底クラスの共通部分定義です。 //--------------------------------------------------------------------------- class ResCommonBase { private: void* mpData; public: //! @brief コンストラクタです。 explicit ResCommonBase(void *p) : mpData(p) {} explicit ResCommonBase(const void* p) : mpData(const_cast(p)) {} //-------------------------------------------------------------------------- //! @brief リソースへのポインタが null でないかどうかを判定します。 //! //! @return 保持しているリソースへのポインタが null の場合には false、 //! null でない場合には true を返します。 //--------------------------------------------------------------------------- bool IsValid() const { return (mpData != NULL); } protected: //! @details :private NW_FORCE_INLINE void* void_ptr() { return mpData; } //! @details :private NW_FORCE_INLINE const void* void_ptr() const { return mpData; } // 構造体の先頭からofsバイト先のポインタを返します。ofsが0の場合はNULLを返します。 //! @details :private template X* ofs_to_ptr(Offset ofs) { // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない u8* p = reinterpret_cast(mpData); if (ofs != 0) { return reinterpret_cast(p + ofs); } else { return NULL; } } // 構造体の先頭からofsバイト先のポインタを返します。ofsが0の場合はNULLを返します。 //! @details :private template const X* ofs_to_ptr(Offset ofs) const { // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない const u8* p = reinterpret_cast(mpData); if (ofs != 0) { return reinterpret_cast(p + ofs); } else { return NULL; } } // 構造体の先頭からofsバイト先のポインタを引数にしたアクセサオブジェクトを返します。 // ofsが0の場合はNULLを引数にしたアクセサオブジェクトを返します。 //! @details :private template X ofs_to_obj(Offset ofs) { // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない u8* p = reinterpret_cast(mpData); if (ofs != 0) { return X(p + ofs); } else { return X(NULL); } } // 構造体の先頭からofsバイト先のポインタを引数にしたアクセサオブジェクトを返します。 // ofsが0の場合はNULLを引数にしたアクセサオブジェクトを返します。 //! @details :private template const X ofs_to_obj(Offset ofs) const { // ofsはmpDataから辿るデータであることが多いのでmpDataのチェックはしない const u8* p = reinterpret_cast(mpData); if (ofs != 0) { return X(const_cast(p + ofs)); } else { return X(NULL); } } // ofs_to_ptrで、ofs!=0であることが保証できる場合に使用できます。 //! @details :private template X* ofs_to_ptr_raw(Offset ofs) { NW_ASSERT(ofs != 0); return reinterpret_cast(reinterpret_cast(mpData) + ofs); } // ofs_to_ptrで、ofs!=0であることが保証できる場合に使用できます。 //! @details :private template const X* ofs_to_ptr_raw(Offset ofs) const { NW_ASSERT(ofs != 0); return reinterpret_cast(reinterpret_cast(mpData) + ofs); } // 状態を Invalid にします。 参照していたデータの実体の破棄は行ないません。 //! @details :private void invalidate() { mpData = NULL; } }; //-------------------------------------------------------------------------- //! @brief リソースアクセサの基底クラスです。 //--------------------------------------------------------------------------- template class ResCommon : public ResCommonBase { public: //! @brief コンストラクタです。 explicit ResCommon(void *p) : ResCommonBase(p) {} explicit ResCommon(const void* p) : ResCommonBase(p) {} //! @brief バイナリリソースの構造体へのポインタを返します NW_FORCE_INLINE T* ptr() { return reinterpret_cast(void_ptr()); } //! @brief バイナリリソースの構造体へのポインタを返します NW_FORCE_INLINE const T* ptr() const { return reinterpret_cast(void_ptr()); } //! @brief バイナリリソースの構造体へのリファレンスを返します NW_FORCE_INLINE T& ref() { NW_ASSERT(this->IsValid()); return *reinterpret_cast(void_ptr()); } //! @brief バイナリリソースの構造体へのリファレンスを返します NW_FORCE_INLINE const T& ref() const { NW_ASSERT(this->IsValid()); return *reinterpret_cast(void_ptr()); } }; //---------------------------------------- //! @name リソース関連 //@{ //--------------------------------------------------------------------------- //! @brief リソースの有無を調べて破棄するためのデリーターです。 //! //! @tparam 削除するオブジェクトです。 //--------------------------------------------------------------------------- template NW_INLINE void SafeCleanup( TResource res ) { if (res.IsValid()) { res.Cleanup(); } } //--------------------------------------------------------------------------- //! @brief リソースを破棄するためのデリーターです。 //! //! @tparam 削除するオブジェクトです。 //--------------------------------------------------------------------------- template struct SafeCleanupper : public std::unary_function { //! Resourceを破棄します。 void operator()(TObject res) const { SafeCleanup(res); } }; //--------------------------------------------------------------------------- //! @brief SafeCleanup でコンテナ要素の全てのリソースを破棄するための関数です。 //! //! @tparam TArray 削除するリソースの配列型です。 //! //! @param[in] array 削除するリソースの配列です。 //--------------------------------------------------------------------------- template NW_INLINE void SafeCleanupAll( TArray array ) { std::for_each(array.begin(), array.end(), SafeCleanupper()); } //@} // 名前クラス //! @details :private struct ResNameData { ResS32 len; char str[4]; // 実際には長さlenの文字列が入っている。末尾は4バイトアラインメントされていて、パディングは0である。 }; //-------------------------------------------------------------------------- //! @brief 名前リソースを表すクラスです。 //--------------------------------------------------------------------------- class ResName : public ResCommon { public: NW_RES_CTOR( ResName ) //! @brief 文字列の長さを返します s32 GetLength() const { return ref().len; } //! @brief 文字列へのポインタを返します const char* GetName() const { return &ref().str[0]; } //! @brief 長さ付き文字列と比較します bool Equals(const char* str, size_t len) const { return (GetLength() == len) && (::std::strcmp(GetName(), str) == 0); } }; // ファイル情報クラス //! @details :private struct ResFileData { ResU32 signature; ResU16 byteOrder; ResU16 headerSize; ResU32 revision; ResU32 fileSize; }; //-------------------------------------------------------------------------- //! @brief ファイルリソースを表すクラスです。 //--------------------------------------------------------------------------- class ResFile : public ResCommon { private: static const u16 BOM = 0xFEFF; static const u32 SIGNATURE = 'NWFL'; public: NW_RES_CTOR( ResFile ) //! @brief ビッグエンディアンかどうかを取得します。 bool IsBigEndian() const { NW_ASSERT( (ref().byteOrder == BOM) || (ref().byteOrder == BOM) ); #if defined( NW_SWAP_ENDIAN ) return *(u8*)(&ref().byteOrder) == 0xFE; #else return *(u8*)(&ref().byteOrder) == 0xFF; #endif } //! @brief リトルエンディアンかどうかを取得します。 bool IsLittleEndian() const { NW_ASSERT( (ref().byteOrder == BOM) || (ref().byteOrder == BOM) ); #if defined( NW_SWAP_ENDIAN ) return *(u8*)(&ref().byteOrder) == 0xFF; #else return *(u8*)(&ref().byteOrder) == 0xFE; #endif } //! @brief バイトオーダーがサポート対象であるかをチェックします。 bool TestByteOrder() const { return ref().byteOrder == BOM; } //! @brief シグニチャが正しいかどうかをチェックします。 bool TestSignature() const { return ref().signature == SIGNATURE; } //! @brief リビジョンを取得します。 u32 GetRevision() const { return ref().revision; } //! @brief ファイルサイズを取得します。 u32 GetFileSize() const { return ref().fileSize; } //! @brief ファイルヘッダサイズを取得します。 u16 GetHeaderSize() const { return ref().headerSize; } //! @brief データアドレスを取得します。 void* GetDataAddress() { return (reinterpret_cast(ptr()) + u32(ref().headerSize)); } //! @brief データアドレスを const ポインタで取得します。 const void* GetDataAddress() const { return (reinterpret_cast(ptr()) + u32(ref().headerSize)); } }; } /* namespace ut */ } /* namespace nw */ #endif /* NW_UT_RESUTIL_H_ */