| 1 | #include "Library/Yaml/ByamlHeader.h" |
| 2 | |
| 3 | #include <byteswap.h> |
| 4 | #include <prim/seadEndian.h> |
| 5 | #include <stream/seadStream.h> |
| 6 | |
| 7 | #include "Library/Yaml/ByamlData.h" |
| 8 | |
| 9 | #define BYAML_LE_TAG 0x5942 // 'YB' |
| 10 | #define BYAML_BE_TAG 0x4259 // 'BY' |
| 11 | |
| 12 | namespace al { |
| 13 | u16 ByamlHeader::() const { |
| 14 | return isInvertOrder() ? bswap_16(mTag) : mTag; |
| 15 | } |
| 16 | |
| 17 | bool ByamlHeader::() const { |
| 18 | return mTag == BYAML_LE_TAG; |
| 19 | } |
| 20 | |
| 21 | u16 ByamlHeader::() const { |
| 22 | if ((_0 & 0xFFFF) == BYAML_LE_TAG) // isInvertOrder() |
| 23 | return bswap_16(_0 >> 16); |
| 24 | return _0 >> 16; |
| 25 | } |
| 26 | |
| 27 | u32 ByamlHeader::() const { |
| 28 | return isInvertOrder() ? __bswap_32(x: mHashKeyOffset) : mHashKeyOffset; |
| 29 | } |
| 30 | |
| 31 | u32 ByamlHeader::() const { |
| 32 | return isInvertOrder() ? __bswap_32(x: mStringTableOffset) : mStringTableOffset; |
| 33 | } |
| 34 | |
| 35 | u32 ByamlHeader::() const { |
| 36 | return isInvertOrder() ? __bswap_32(x: mDataOffset) : mDataOffset; |
| 37 | } |
| 38 | |
| 39 | ByamlStringTableIter::ByamlStringTableIter() = default; |
| 40 | |
| 41 | ByamlStringTableIter::ByamlStringTableIter(const u8* data, bool isRev) |
| 42 | : mData(data), mIsRev(isRev) {} |
| 43 | |
| 44 | s32 ByamlStringTableIter::getSize() const { |
| 45 | u32 type_and_size = *reinterpret_cast<const u32*>(mData); |
| 46 | return mIsRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8; |
| 47 | } |
| 48 | |
| 49 | const u32* ByamlStringTableIter::getAddressTable() const { |
| 50 | // mData is an integer pointer, so getting to the table is just increasing the pointer by 1 |
| 51 | // (which is + 4) |
| 52 | return reinterpret_cast<const u32*>(mData + 4); |
| 53 | } |
| 54 | |
| 55 | u32 ByamlStringTableIter::getStringAddress(s32 idx) const { |
| 56 | if (mIsRev) |
| 57 | return bswap_32(getAddressTable()[idx]); |
| 58 | |
| 59 | return getAddressTable()[idx]; |
| 60 | } |
| 61 | |
| 62 | // NON_MATCHING: regalloc |
| 63 | u32 ByamlStringTableIter::getEndAddress() const { |
| 64 | u32 val = getAddressTable()[getSize()]; |
| 65 | return mIsRev ? bswap_32(val) : val; |
| 66 | } |
| 67 | |
| 68 | const char* ByamlStringTableIter::getString(s32 index) const { |
| 69 | return reinterpret_cast<const char*>(&mData[getStringAddress(idx: index)]); |
| 70 | } |
| 71 | |
| 72 | s32 ByamlStringTableIter::getStringSize(s32 index) const { |
| 73 | return getStringAddress(idx: index + 1) - getStringAddress(idx: index) - 1; |
| 74 | } |
| 75 | |
| 76 | s32 ByamlStringTableIter::findStringIndex(const char* str) const { |
| 77 | s32 lowerBound = 0; |
| 78 | s32 upperBound = getSize(); |
| 79 | while (lowerBound < upperBound) { |
| 80 | s32 avg = (lowerBound + upperBound) / 2; |
| 81 | s32 result = strcmp(str, getString(index: avg)); |
| 82 | if (result == 0) |
| 83 | return avg; |
| 84 | |
| 85 | if (result > 0) |
| 86 | lowerBound = avg + 1; |
| 87 | else |
| 88 | upperBound = avg; |
| 89 | } |
| 90 | return -1; |
| 91 | } |
| 92 | |
| 93 | bool ByamlStringTableIter::isValidate() const { |
| 94 | return mData != nullptr; |
| 95 | } |
| 96 | |
| 97 | } // namespace al |
| 98 | |
| 99 | namespace alByamlLocalUtil { |
| 100 | |
| 101 | const char* getDataTypeString(s32 type) { |
| 102 | switch (type) { |
| 103 | case al::ByamlDataType::TYPE_INVALID: |
| 104 | return "None" ; |
| 105 | case al::ByamlDataType::TYPE_STRING: |
| 106 | return "String" ; |
| 107 | case al::ByamlDataType::TYPE_ARRAY: |
| 108 | return "Array" ; |
| 109 | case al::ByamlDataType::TYPE_HASH: |
| 110 | return "Hash" ; |
| 111 | case al::ByamlDataType::TYPE_STRING_TABLE: |
| 112 | return "StringTable" ; |
| 113 | case al::ByamlDataType::TYPE_BOOL: |
| 114 | return "Bool" ; |
| 115 | case al::ByamlDataType::TYPE_INT: |
| 116 | return "Int" ; |
| 117 | case al::ByamlDataType::TYPE_FLOAT: |
| 118 | return "Float" ; |
| 119 | case al::ByamlDataType::TYPE_UINT: |
| 120 | return "UInt" ; |
| 121 | case al::ByamlDataType::TYPE_LONG: |
| 122 | return "Int64" ; |
| 123 | case al::ByamlDataType::TYPE_ULONG: |
| 124 | return "UInt64" ; |
| 125 | case al::ByamlDataType::TYPE_DOUBLE: |
| 126 | return "Double" ; |
| 127 | case al::ByamlDataType::TYPE_NULL: |
| 128 | return "NULL" ; |
| 129 | case al::ByamlDataType::TYPE_BINARY: |
| 130 | default: |
| 131 | return "Unknown" ; |
| 132 | }; |
| 133 | } |
| 134 | |
| 135 | al::ByamlStringTableIter getHashKeyTable(const u8* data) { |
| 136 | const al::ByamlHeader* = reinterpret_cast<const al::ByamlHeader*>(data); |
| 137 | s32 off = header->getHashKeyTableOffset(); |
| 138 | if (off == 0) |
| 139 | return {}; |
| 140 | return {&data[off], header->isInvertOrder()}; |
| 141 | } |
| 142 | |
| 143 | al::ByamlStringTableIter getStringTable(const u8* data) { |
| 144 | const al::ByamlHeader* = reinterpret_cast<const al::ByamlHeader*>(data); |
| 145 | s32 off = header->getStringTableOffset(); |
| 146 | if (off == 0) |
| 147 | return {}; |
| 148 | return {&data[off], header->isInvertOrder()}; |
| 149 | } |
| 150 | |
| 151 | u64 getData64Bit(const u8* data, u32 off, bool isRev) { |
| 152 | u64 val = *reinterpret_cast<const u64*>(&data[off]); |
| 153 | return isRev ? bswap_32_64(val) : val; |
| 154 | } |
| 155 | |
| 156 | void writeU24(sead::WriteStream* stream, s32 val) { |
| 157 | if (sead::Endian::getHostEndian() == sead::Endian::cBig) { |
| 158 | stream->writeU8(val >> 16); |
| 159 | stream->writeU8(val >> 8); |
| 160 | stream->writeU8(val); |
| 161 | } else { |
| 162 | stream->writeU8(val); |
| 163 | stream->writeU8(val >> 8); |
| 164 | stream->writeU8(val >> 16); |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | // NON_MATCHING: inlined verifiHeader, splitted loads for unswappedAfterOffset and diff in final |
| 169 | // logic |
| 170 | bool verifiByaml(const u8* data) { |
| 171 | if (!verifiByamlHeader(data)) |
| 172 | return false; |
| 173 | |
| 174 | bool isRev = *((const u16*)data) == BYAML_LE_TAG; |
| 175 | const u32* biggerData = (const u32*)data; |
| 176 | |
| 177 | u32 afterHashOffset = 0; |
| 178 | u32 hashOffset = isRev ? bswap_32(biggerData[1]) : biggerData[1]; |
| 179 | if (hashOffset) { |
| 180 | const u8* hashData = &data[hashOffset]; |
| 181 | if (!verifiByamlStringTable(data: hashData, isRev)) |
| 182 | return false; |
| 183 | u32 type_and_size = *reinterpret_cast<const u32*>(hashData); |
| 184 | s32 hash_size = isRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8; |
| 185 | s32 unswappedAfterOffset = *(s32*)&hashData[(hash_size * 4) + 4]; |
| 186 | u32 swappedAfterOffset = bswap_32(unswappedAfterOffset); |
| 187 | afterHashOffset = isRev ? swappedAfterOffset : unswappedAfterOffset; |
| 188 | } |
| 189 | |
| 190 | u32 afterStringOffset = 0; |
| 191 | u32 stringOffset = isRev ? bswap_32(biggerData[2]) : biggerData[2]; |
| 192 | if (stringOffset) { |
| 193 | const u8* stringData = &data[stringOffset]; |
| 194 | if (!verifiByamlStringTable(data: stringData, isRev)) |
| 195 | return false; |
| 196 | u32 type_and_size = *reinterpret_cast<const u32*>(stringData); |
| 197 | s32 string_size = isRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8; |
| 198 | s32 unswappedAfterOffset = *(s32*)&stringData[4 * string_size + 4]; |
| 199 | u32 swappedAfterOffset = bswap_32(unswappedAfterOffset); |
| 200 | afterStringOffset = isRev ? swappedAfterOffset : unswappedAfterOffset; |
| 201 | } |
| 202 | |
| 203 | u32 rootOffset = isRev ? bswap_32(biggerData[3]) : biggerData[3]; |
| 204 | |
| 205 | return (((!hashOffset && !stringOffset) || rootOffset) && |
| 206 | (!hashOffset || ((!stringOffset || afterHashOffset <= stringOffset) && |
| 207 | (!rootOffset || afterHashOffset <= rootOffset)))) && |
| 208 | (afterStringOffset <= rootOffset || !stringOffset || !rootOffset); |
| 209 | } |
| 210 | |
| 211 | // NON_MATCHING: missing & 0xFFFF |
| 212 | bool (const u8* data) { |
| 213 | const al::ByamlHeader* = reinterpret_cast<const al::ByamlHeader*>(data); |
| 214 | return header->getTag() == BYAML_BE_TAG && (u32)(header->getVersion() - 1) < 3; |
| 215 | } |
| 216 | |
| 217 | bool verifiByamlStringTable(const u8* data, bool isRev) { |
| 218 | const s32* address_table = reinterpret_cast<const s32*>(data + 4); |
| 219 | |
| 220 | u32 type_and_size = *reinterpret_cast<const u32*>(data); |
| 221 | if ((type_and_size & 0xff) != al::ByamlDataType::TYPE_STRING_TABLE) |
| 222 | return false; |
| 223 | s32 size = isRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8; |
| 224 | if (size < 1) |
| 225 | return false; |
| 226 | |
| 227 | // check that strings are null-terminated |
| 228 | for (s32 i = 1; i <= size; i++) { |
| 229 | s32 off = isRev ? bswap_32(address_table[i]) : address_table[i]; |
| 230 | if (data[off - 1]) |
| 231 | return false; |
| 232 | } |
| 233 | |
| 234 | for (s32 i = 0; i < size; i++) { |
| 235 | s32 val1 = isRev ? bswap_32(address_table[i]) : address_table[i]; |
| 236 | s32 val2 = isRev ? bswap_32(address_table[i + 1]) : address_table[i + 1]; |
| 237 | if (val1 >= val2) |
| 238 | return false; |
| 239 | } |
| 240 | |
| 241 | // check for correct length of address table |
| 242 | s32 calcFirstStringPos = 4 * size + 8; |
| 243 | s32 dataFirstStringPos = isRev ? bswap_32(address_table[0]) : address_table[0]; |
| 244 | if (dataFirstStringPos != calcFirstStringPos) |
| 245 | return false; |
| 246 | |
| 247 | // TODO: improve this. Matching, but ugly |
| 248 | if (isRev) { |
| 249 | for (s32 i = 0; i < size - 1; i++) { |
| 250 | const char* c1 = |
| 251 | (const char*)&data[isRev ? bswap_32(address_table[i]) : address_table[i]]; |
| 252 | const char* c2 = |
| 253 | (const char*)&data[isRev ? bswap_32(address_table[i + 1]) : address_table[i + 1]]; |
| 254 | if (strcmp(c1, c2) > 0) |
| 255 | return false; |
| 256 | } |
| 257 | } else { |
| 258 | for (s32 i = 0; i < size - 1; i++) { |
| 259 | const char* c1 = |
| 260 | (const char*)&data[isRev ? bswap_32(address_table[i]) : address_table[i]]; |
| 261 | const char* c2 = |
| 262 | (const char*)&data[false ? bswap_32(address_table[i + 1]) : address_table[i + 1]]; |
| 263 | if (strcmp(c1, c2) > 0) |
| 264 | return false; |
| 265 | } |
| 266 | } |
| 267 | return true; |
| 268 | } |
| 269 | |
| 270 | } // namespace alByamlLocalUtil |
| 271 | |