mirror of
https://github.com/MonsterDruide1/OdysseyDecomp
synced 2026-05-01 13:04:42 +00:00
271 lines
8.8 KiB
C++
271 lines
8.8 KiB
C++
#include "Library/Yaml/ByamlHeader.h"
|
|
|
|
#include <byteswap.h>
|
|
#include <prim/seadEndian.h>
|
|
#include <stream/seadStream.h>
|
|
|
|
#include "Library/Yaml/ByamlData.h"
|
|
|
|
#define BYAML_LE_TAG 0x5942 // 'YB'
|
|
#define BYAML_BE_TAG 0x4259 // 'BY'
|
|
|
|
namespace al {
|
|
u16 ByamlHeader::getTag() const {
|
|
return isInvertOrder() ? bswap_16(mTag) : mTag;
|
|
}
|
|
|
|
bool ByamlHeader::isInvertOrder() const {
|
|
return mTag == BYAML_LE_TAG;
|
|
}
|
|
|
|
u16 ByamlHeader::getVersion() const {
|
|
if ((_0 & 0xFFFF) == BYAML_LE_TAG) // isInvertOrder()
|
|
return bswap_16(_0 >> 16);
|
|
return _0 >> 16;
|
|
}
|
|
|
|
u32 ByamlHeader::getHashKeyTableOffset() const {
|
|
return isInvertOrder() ? __bswap_32(mHashKeyOffset) : mHashKeyOffset;
|
|
}
|
|
|
|
u32 ByamlHeader::getStringTableOffset() const {
|
|
return isInvertOrder() ? __bswap_32(mStringTableOffset) : mStringTableOffset;
|
|
}
|
|
|
|
u32 ByamlHeader::getDataOffset() const {
|
|
return isInvertOrder() ? __bswap_32(mDataOffset) : mDataOffset;
|
|
}
|
|
|
|
ByamlStringTableIter::ByamlStringTableIter() = default;
|
|
|
|
ByamlStringTableIter::ByamlStringTableIter(const u8* data, bool isRev)
|
|
: mData(data), mIsRev(isRev) {}
|
|
|
|
s32 ByamlStringTableIter::getSize() const {
|
|
u32 type_and_size = *reinterpret_cast<const u32*>(mData);
|
|
return mIsRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8;
|
|
}
|
|
|
|
const u32* ByamlStringTableIter::getAddressTable() const {
|
|
// mData is an integer pointer, so getting to the table is just increasing the pointer by 1
|
|
// (which is + 4)
|
|
return reinterpret_cast<const u32*>(mData + 4);
|
|
}
|
|
|
|
u32 ByamlStringTableIter::getStringAddress(s32 index) const {
|
|
if (mIsRev)
|
|
return bswap_32(getAddressTable()[index]);
|
|
|
|
return getAddressTable()[index];
|
|
}
|
|
|
|
// NON_MATCHING: regalloc (https://decomp.me/scratch/AdRVH)
|
|
u32 ByamlStringTableIter::getEndAddress() const {
|
|
u32 val = getAddressTable()[getSize()];
|
|
return mIsRev ? bswap_32(val) : val;
|
|
}
|
|
|
|
const char* ByamlStringTableIter::getString(s32 index) const {
|
|
return reinterpret_cast<const char*>(&mData[getStringAddress(index)]);
|
|
}
|
|
|
|
s32 ByamlStringTableIter::getStringSize(s32 index) const {
|
|
return getStringAddress(index + 1) - getStringAddress(index) - 1;
|
|
}
|
|
|
|
s32 ByamlStringTableIter::findStringIndex(const char* str) const {
|
|
s32 lowerBound = 0;
|
|
s32 upperBound = getSize();
|
|
while (lowerBound < upperBound) {
|
|
s32 avg = (lowerBound + upperBound) / 2;
|
|
s32 result = strcmp(str, getString(avg));
|
|
if (result == 0)
|
|
return avg;
|
|
|
|
if (result > 0)
|
|
lowerBound = avg + 1;
|
|
else
|
|
upperBound = avg;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
bool ByamlStringTableIter::isValidate() const {
|
|
return mData != nullptr;
|
|
}
|
|
|
|
} // namespace al
|
|
|
|
namespace alByamlLocalUtil {
|
|
|
|
const char* getDataTypeString(s32 type) {
|
|
switch (type) {
|
|
case al::ByamlDataType::TYPE_INVALID:
|
|
return "None";
|
|
case al::ByamlDataType::TYPE_STRING:
|
|
return "String";
|
|
case al::ByamlDataType::TYPE_ARRAY:
|
|
return "Array";
|
|
case al::ByamlDataType::TYPE_HASH:
|
|
return "Hash";
|
|
case al::ByamlDataType::TYPE_STRING_TABLE:
|
|
return "StringTable";
|
|
case al::ByamlDataType::TYPE_BOOL:
|
|
return "Bool";
|
|
case al::ByamlDataType::TYPE_INT:
|
|
return "Int";
|
|
case al::ByamlDataType::TYPE_FLOAT:
|
|
return "Float";
|
|
case al::ByamlDataType::TYPE_UINT:
|
|
return "UInt";
|
|
case al::ByamlDataType::TYPE_LONG:
|
|
return "Int64";
|
|
case al::ByamlDataType::TYPE_ULONG:
|
|
return "UInt64";
|
|
case al::ByamlDataType::TYPE_DOUBLE:
|
|
return "Double";
|
|
case al::ByamlDataType::TYPE_NULL:
|
|
return "NULL";
|
|
case al::ByamlDataType::TYPE_BINARY:
|
|
default:
|
|
return "Unknown";
|
|
};
|
|
}
|
|
|
|
al::ByamlStringTableIter getHashKeyTable(const u8* data) {
|
|
const al::ByamlHeader* header = reinterpret_cast<const al::ByamlHeader*>(data);
|
|
s32 off = header->getHashKeyTableOffset();
|
|
if (off == 0)
|
|
return {};
|
|
return {&data[off], header->isInvertOrder()};
|
|
}
|
|
|
|
al::ByamlStringTableIter getStringTable(const u8* data) {
|
|
const al::ByamlHeader* header = reinterpret_cast<const al::ByamlHeader*>(data);
|
|
s32 off = header->getStringTableOffset();
|
|
if (off == 0)
|
|
return {};
|
|
return {&data[off], header->isInvertOrder()};
|
|
}
|
|
|
|
u64 getData64Bit(const u8* data, u32 off, bool isRev) {
|
|
u64 val = *reinterpret_cast<const u64*>(&data[off]);
|
|
return isRev ? bswap_32_64(val) : val;
|
|
}
|
|
|
|
void writeU24(sead::WriteStream* stream, s32 val) {
|
|
if (sead::Endian::getHostEndian() == sead::Endian::cBig) {
|
|
stream->writeU8(val >> 16);
|
|
stream->writeU8(val >> 8);
|
|
stream->writeU8(val);
|
|
} else {
|
|
stream->writeU8(val);
|
|
stream->writeU8(val >> 8);
|
|
stream->writeU8(val >> 16);
|
|
}
|
|
}
|
|
|
|
// NON_MATCHING: inlined verifiHeader, splitted loads for unswappedAfterOffset and diff in final
|
|
// logic (https://decomp.me/scratch/5xOvy)
|
|
bool verifiByaml(const u8* data) {
|
|
if (!verifiByamlHeader(data))
|
|
return false;
|
|
|
|
bool isRev = *((const u16*)data) == BYAML_LE_TAG;
|
|
const u32* biggerData = (const u32*)data;
|
|
|
|
u32 afterHashOffset = 0;
|
|
u32 hashOffset = isRev ? bswap_32(biggerData[1]) : biggerData[1];
|
|
if (hashOffset) {
|
|
const u8* hashData = &data[hashOffset];
|
|
if (!verifiByamlStringTable(hashData, isRev))
|
|
return false;
|
|
u32 type_and_size = *reinterpret_cast<const u32*>(hashData);
|
|
s32 hash_size = isRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8;
|
|
s32 unswappedAfterOffset = *(s32*)&hashData[(hash_size * 4) + 4];
|
|
u32 swappedAfterOffset = bswap_32(unswappedAfterOffset);
|
|
afterHashOffset = isRev ? swappedAfterOffset : unswappedAfterOffset;
|
|
}
|
|
|
|
u32 afterStringOffset = 0;
|
|
u32 stringOffset = isRev ? bswap_32(biggerData[2]) : biggerData[2];
|
|
if (stringOffset) {
|
|
const u8* stringData = &data[stringOffset];
|
|
if (!verifiByamlStringTable(stringData, isRev))
|
|
return false;
|
|
u32 type_and_size = *reinterpret_cast<const u32*>(stringData);
|
|
s32 string_size = isRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8;
|
|
s32 unswappedAfterOffset = *(s32*)&stringData[4 * string_size + 4];
|
|
u32 swappedAfterOffset = bswap_32(unswappedAfterOffset);
|
|
afterStringOffset = isRev ? swappedAfterOffset : unswappedAfterOffset;
|
|
}
|
|
|
|
u32 rootOffset = isRev ? bswap_32(biggerData[3]) : biggerData[3];
|
|
|
|
return (((!hashOffset && !stringOffset) || rootOffset) &&
|
|
(!hashOffset || ((!stringOffset || afterHashOffset <= stringOffset) &&
|
|
(!rootOffset || afterHashOffset <= rootOffset)))) &&
|
|
(afterStringOffset <= rootOffset || !stringOffset || !rootOffset);
|
|
}
|
|
|
|
// NON_MATCHING: missing & 0xFFFF (https://decomp.me/scratch/dRP3R)
|
|
bool verifiByamlHeader(const u8* data) {
|
|
const al::ByamlHeader* header = reinterpret_cast<const al::ByamlHeader*>(data);
|
|
return header->getTag() == BYAML_BE_TAG && (u32)(header->getVersion() - 1) < 3;
|
|
}
|
|
|
|
bool verifiByamlStringTable(const u8* data, bool isRev) {
|
|
const s32* address_table = reinterpret_cast<const s32*>(data + 4);
|
|
|
|
u32 type_and_size = *reinterpret_cast<const u32*>(data);
|
|
if ((type_and_size & 0xff) != al::ByamlDataType::TYPE_STRING_TABLE)
|
|
return false;
|
|
s32 size = isRev ? bswap_24(type_and_size >> 8) : type_and_size >> 8;
|
|
if (size < 1)
|
|
return false;
|
|
|
|
// check that strings are null-terminated
|
|
for (s32 i = 1; i <= size; i++) {
|
|
s32 off = isRev ? bswap_32(address_table[i]) : address_table[i];
|
|
if (data[off - 1])
|
|
return false;
|
|
}
|
|
|
|
for (s32 i = 0; i < size; i++) {
|
|
s32 val1 = isRev ? bswap_32(address_table[i]) : address_table[i];
|
|
s32 val2 = isRev ? bswap_32(address_table[i + 1]) : address_table[i + 1];
|
|
if (val1 >= val2)
|
|
return false;
|
|
}
|
|
|
|
// check for correct length of address table
|
|
s32 calcFirstStringPos = 4 * size + 8;
|
|
s32 dataFirstStringPos = isRev ? bswap_32(address_table[0]) : address_table[0];
|
|
if (dataFirstStringPos != calcFirstStringPos)
|
|
return false;
|
|
|
|
// TODO: improve this. Matching, but ugly
|
|
if (isRev) {
|
|
for (s32 i = 0; i < size - 1; i++) {
|
|
const char* c1 =
|
|
(const char*)&data[isRev ? bswap_32(address_table[i]) : address_table[i]];
|
|
const char* c2 =
|
|
(const char*)&data[isRev ? bswap_32(address_table[i + 1]) : address_table[i + 1]];
|
|
if (strcmp(c1, c2) > 0)
|
|
return false;
|
|
}
|
|
} else {
|
|
for (s32 i = 0; i < size - 1; i++) {
|
|
const char* c1 =
|
|
(const char*)&data[isRev ? bswap_32(address_table[i]) : address_table[i]];
|
|
const char* c2 =
|
|
(const char*)&data[false ? bswap_32(address_table[i + 1]) : address_table[i + 1]];
|
|
if (strcmp(c1, c2) > 0)
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
} // namespace alByamlLocalUtil
|