| 1 | #pragma once |
| 2 | |
| 3 | #include <array> |
| 4 | #include <basis/seadRawPrint.h> |
| 5 | #include <basis/seadTypes.h> |
| 6 | #include <prim/seadTypedBitFlag.h> |
| 7 | #include "common/aglResCommon.h" |
| 8 | |
| 9 | namespace sead { |
| 10 | template <typename Key, typename Value> |
| 11 | class TreeMap; |
| 12 | } |
| 13 | |
| 14 | namespace agl::utl { |
| 15 | |
| 16 | struct ResParameterData { |
| 17 | constexpr u32 getParameterNameHash() const { return name_hash; } |
| 18 | constexpr u32 getOffset() const { return 4 * (offset_and_type & 0xFFFFFF); } |
| 19 | constexpr u32 getType() const { return offset_and_type >> 24; } |
| 20 | |
| 21 | u32 name_hash; |
| 22 | u32 offset_and_type; |
| 23 | }; |
| 24 | static_assert(sizeof(ResParameterData) == 0x8); |
| 25 | |
| 26 | struct ResParameter { |
| 27 | ResParameterData* ptr() const { return mPtr; } |
| 28 | u8* ptrBytes() const { return reinterpret_cast<u8*>(mPtr); } |
| 29 | |
| 30 | u32 getParameterNameHash() const { return ptr()->getParameterNameHash(); } |
| 31 | |
| 32 | template <typename T> |
| 33 | T* getData() const { |
| 34 | return reinterpret_cast<T*>(ptrBytes() + ptr()->getOffset()); |
| 35 | } |
| 36 | |
| 37 | /// Get the data size in bytes. |
| 38 | size_t getDataSize() const; |
| 39 | |
| 40 | template <typename T> |
| 41 | bool copyData(T* out) const; |
| 42 | |
| 43 | /// Get the number of elements in the buffer. |
| 44 | /// @warning Only valid for buffer types. |
| 45 | size_t getBufferSize() const { |
| 46 | return *reinterpret_cast<u32*>(ptrBytes() + ptr()->getOffset() - 4); |
| 47 | } |
| 48 | |
| 49 | ResParameterData* mPtr; |
| 50 | }; |
| 51 | |
| 52 | struct ResParameterObjData { |
| 53 | constexpr u32 getParameterObjNameHash() const { return name_hash; } |
| 54 | constexpr u32 getParametersOffset() const { return 4 * u16(param_offset_and_num); } |
| 55 | constexpr u16 getNumParameters() const { return param_offset_and_num >> 16; } |
| 56 | constexpr bool hasParameters() const { return getNumParameters() != 0; } |
| 57 | |
| 58 | u32 name_hash; |
| 59 | u32 param_offset_and_num; |
| 60 | }; |
| 61 | static_assert(sizeof(ResParameterObjData) == 8); |
| 62 | |
| 63 | struct ResParameterObj { |
| 64 | class Iterator { |
| 65 | public: |
| 66 | Iterator(ResParameterData* ptr, s32 idx) : mIdx(idx), mPtr(ptr) {} |
| 67 | bool operator==(const Iterator& rhs) const { return getIndex() == rhs.getIndex(); } |
| 68 | bool operator!=(const Iterator& rhs) const { return !operator==(rhs); } |
| 69 | s32 getIndex() const { return mIdx; } |
| 70 | ResParameter getParam() const { return {.mPtr: mPtr}; } |
| 71 | ResParameter operator*() const { return getParam(); } |
| 72 | Iterator& operator++() { |
| 73 | ++mIdx; |
| 74 | ++mPtr; |
| 75 | return *this; |
| 76 | } |
| 77 | |
| 78 | private: |
| 79 | s32 mIdx; |
| 80 | ResParameterData* mPtr; |
| 81 | }; |
| 82 | |
| 83 | Iterator begin() const { |
| 84 | if (!ptr()->hasParameters()) |
| 85 | return {nullptr, 0}; |
| 86 | return {reinterpret_cast<ResParameterData*>(ptrBytes() + ptr()->getParametersOffset()), 0}; |
| 87 | } |
| 88 | Iterator end() const { return {nullptr, s32(ptr()->getNumParameters())}; } |
| 89 | |
| 90 | explicit operator bool() const { return ptr() != nullptr; } |
| 91 | ResParameterObjData* ptr() const { return mPtr; } |
| 92 | u8* ptrBytes() const { return reinterpret_cast<u8*>(mPtr); } |
| 93 | |
| 94 | u32 getParameterObjNameHash() const { return ptr()->getParameterObjNameHash(); } |
| 95 | s32 getNum() const { return ptr()->getNumParameters(); } |
| 96 | |
| 97 | /// Get a parameter by index. The index must be valid. |
| 98 | ResParameter getResParameter(s32 index) const { |
| 99 | SEAD_ASSERT(0 <= index && index < getNum()); |
| 100 | return getResParameter(index, offset: ptr()->getParametersOffset()); |
| 101 | } |
| 102 | |
| 103 | ResParameter getResParameter(s32 index, u32 offset) const { |
| 104 | return {.mPtr: reinterpret_cast<ResParameterData*>(ptrBytes() + offset + |
| 105 | sizeof(ResParameterData) * index)}; |
| 106 | } |
| 107 | |
| 108 | template <typename T> |
| 109 | T* getParameterData(s32 index) const { |
| 110 | return getResParameter(index).getData<T>(); |
| 111 | } |
| 112 | |
| 113 | /// @returns the index of the specified parameter, or -1 if not found. |
| 114 | s32 searchIndex(u32 param_hash) const; |
| 115 | |
| 116 | ResParameterObjData* mPtr; |
| 117 | }; |
| 118 | |
| 119 | struct ResParameterListData { |
| 120 | constexpr u32 getParameterListNameHash() const { return name_hash; } |
| 121 | constexpr u32 getListsOffset() const { return 4 * u16(list_offset_and_num); } |
| 122 | constexpr u32 getObjectsOffset() const { return 4 * u16(obj_offset_and_num); } |
| 123 | constexpr u32 getNumLists() const { return list_offset_and_num >> 16; } |
| 124 | constexpr s32 getNumObjects() const { return obj_offset_and_num >> 16; } |
| 125 | constexpr bool hasLists() const { return getNumLists() != 0; } |
| 126 | constexpr bool hasObjects() const { return getNumObjects() != 0; } |
| 127 | |
| 128 | u32 name_hash; |
| 129 | u32 list_offset_and_num; |
| 130 | u32 obj_offset_and_num; |
| 131 | }; |
| 132 | static_assert(sizeof(ResParameterListData) == 0xc); |
| 133 | |
| 134 | struct ResParameterList { |
| 135 | class ListIterator { |
| 136 | public: |
| 137 | ListIterator(ResParameterListData* ptr, s32 idx) : mIdx(idx), mPtr(ptr) {} |
| 138 | bool operator==(const ListIterator& rhs) const { return getIndex() == rhs.getIndex(); } |
| 139 | bool operator!=(const ListIterator& rhs) const { return !operator==(rhs); } |
| 140 | s32 getIndex() const { return mIdx; } |
| 141 | ResParameterList getList() const { return {.mPtr: mPtr}; } |
| 142 | ResParameterList operator*() const { return getList(); } |
| 143 | ListIterator& operator++() { |
| 144 | ++mIdx; |
| 145 | ++mPtr; |
| 146 | return *this; |
| 147 | } |
| 148 | |
| 149 | private: |
| 150 | s32 mIdx; |
| 151 | ResParameterListData* mPtr; |
| 152 | }; |
| 153 | |
| 154 | ListIterator listBegin() const { |
| 155 | if (!ptr()->hasLists()) |
| 156 | return {nullptr, 0}; |
| 157 | return {reinterpret_cast<ResParameterListData*>(ptrBytes() + ptr()->getListsOffset()), 0}; |
| 158 | } |
| 159 | ListIterator listEnd() const { return {nullptr, s32(ptr()->getNumLists())}; } |
| 160 | |
| 161 | class ObjIterator { |
| 162 | public: |
| 163 | ObjIterator(ResParameterObjData* ptr, s32 idx) : mIdx(idx), mPtr(ptr) {} |
| 164 | bool operator==(const ObjIterator& rhs) const { return getIndex() == rhs.getIndex(); } |
| 165 | bool operator!=(const ObjIterator& rhs) const { return !operator==(rhs); } |
| 166 | s32 getIndex() const { return mIdx; } |
| 167 | ResParameterObj getObj() const { return {.mPtr: mPtr}; } |
| 168 | ResParameterObj operator*() const { return getObj(); } |
| 169 | ObjIterator& operator++() { |
| 170 | ++mIdx; |
| 171 | ++mPtr; |
| 172 | return *this; |
| 173 | } |
| 174 | |
| 175 | private: |
| 176 | s32 mIdx; |
| 177 | ResParameterObjData* mPtr; |
| 178 | }; |
| 179 | |
| 180 | ObjIterator objBegin() const { |
| 181 | if (!ptr()->hasObjects()) |
| 182 | return {nullptr, 0}; |
| 183 | return {reinterpret_cast<ResParameterObjData*>(ptrBytes() + ptr()->getObjectsOffset()), 0}; |
| 184 | } |
| 185 | ObjIterator objEnd() const { return {nullptr, s32(ptr()->getNumObjects())}; } |
| 186 | |
| 187 | explicit operator bool() const { return ptr() != nullptr; } |
| 188 | ResParameterListData* ptr() const { return mPtr; } |
| 189 | u8* ptrBytes() const { return reinterpret_cast<u8*>(mPtr); } |
| 190 | |
| 191 | u32 getParameterListNameHash() const { return ptr()->getParameterListNameHash(); } |
| 192 | s32 getResParameterListNum() const { return ptr()->getNumLists(); } |
| 193 | s32 getResParameterObjNum() const { return ptr()->getNumObjects(); } |
| 194 | |
| 195 | /// Get a parameter list by index. The index must be valid. |
| 196 | ResParameterList getResParameterList(s32 index) const { |
| 197 | SEAD_ASSERT(0 <= index && index < getResParameterListNum()); |
| 198 | return getResParameterList(index, offset: ptr()->getListsOffset()); |
| 199 | } |
| 200 | |
| 201 | ResParameterList getResParameterList(s32 index, u32 offset) const { |
| 202 | return {.mPtr: reinterpret_cast<ResParameterListData*>(ptrBytes() + offset + |
| 203 | sizeof(ResParameterListData) * index)}; |
| 204 | } |
| 205 | |
| 206 | /// Get a parameter object by index. The index must be valid. |
| 207 | ResParameterObj getResParameterObj(s32 index) const { |
| 208 | SEAD_ASSERT(0 <= index && index < getResParameterObjNum()); |
| 209 | return getResParameterObj(index, offset: ptr()->getObjectsOffset()); |
| 210 | } |
| 211 | |
| 212 | ResParameterObj getResParameterObj(s32 index, u32 offset) const { |
| 213 | return {.mPtr: reinterpret_cast<ResParameterObjData*>(ptrBytes() + offset + |
| 214 | sizeof(ResParameterObjData) * index)}; |
| 215 | } |
| 216 | |
| 217 | /// @returns the index of the specified list, or -1 if not found. |
| 218 | s32 searchListIndex(u32 list_hash) const; |
| 219 | |
| 220 | /// @returns the index of the specified object, or -1 if not found. |
| 221 | s32 searchObjIndex(u32 obj_hash) const; |
| 222 | |
| 223 | void dump(s32, const sead::TreeMap<u32, const char*>* name_table) const; |
| 224 | |
| 225 | ResParameterListData* mPtr; |
| 226 | }; |
| 227 | |
| 228 | constexpr std::array<char, 4> = {{'A', 'A', 'M', 'P'}}; |
| 229 | |
| 230 | enum class ResParameterArchiveFlag : u32 { |
| 231 | LittleEndian = 1 << 0, |
| 232 | Utf8 = 1 << 1, |
| 233 | }; |
| 234 | |
| 235 | struct ResParameterArchiveData { |
| 236 | static u32 getVersion() { return 2; } |
| 237 | static u32 getSignature() { return 'AAMP'; } |
| 238 | |
| 239 | std::array<char, 4> magic; |
| 240 | u32 version; |
| 241 | sead::TypedBitFlag<ResParameterArchiveFlag> flags; |
| 242 | u32 file_size; |
| 243 | u32 pio_version; |
| 244 | /// Offset to parameter IO (relative to 0x30) |
| 245 | u32 offset_to_pio; |
| 246 | /// Number of lists (including parameter IO) |
| 247 | u32 num_lists; |
| 248 | u32 num_objects; |
| 249 | u32 num_parameters; |
| 250 | u32 data_section_size; |
| 251 | u32 string_section_size; |
| 252 | u32 unk_section_size; |
| 253 | }; |
| 254 | static_assert(sizeof(ResParameterArchiveData) == 0x30); |
| 255 | |
| 256 | struct ResParameterArchive : ResCommon<ResParameterArchiveData> { |
| 257 | ResParameterArchive() = default; |
| 258 | explicit ResParameterArchive(const void* p_data); |
| 259 | |
| 260 | ResParameterList getRootList() const { |
| 261 | return {.mPtr: reinterpret_cast<ResParameterListData*>( |
| 262 | ptrBytes() + sizeof(ResParameterArchiveData) + ptr()->offset_to_pio)}; |
| 263 | } |
| 264 | }; |
| 265 | |
| 266 | } // namespace agl::utl |
| 267 | |