2
0
mirror of https://github.com/AxioDL/metaforce.git synced 2025-06-06 16:33:27 +00:00

CFBStreamedCompression: Eliminate undefined pointer casting

Some (but not all pointer casting in this file is undefined behavior).
To rectify this, we can make use of a light wrapper around memcpy to
make all of this well-defined.
This commit is contained in:
Lioncash 2020-08-02 03:19:05 -04:00
parent c9f5483c59
commit 97f2576e2a

View File

@ -1,8 +1,26 @@
#include "Runtime/Character/CFBStreamedCompression.hpp" #include "Runtime/Character/CFBStreamedCompression.hpp"
#include <cstring>
#include <type_traits>
#include "Runtime/Character/CFBStreamedAnimReader.hpp" #include "Runtime/Character/CFBStreamedAnimReader.hpp"
namespace urde { namespace urde {
namespace {
template <typename T>
T ReadValue(const u8* data) {
static_assert(std::is_trivially_copyable_v<T>);
T value = 0;
std::memcpy(&value, data, sizeof(value));
return value;
}
template <typename T>
void WriteValue(u8* data, T value) {
static_assert(std::is_trivially_copyable_v<T>);
std::memcpy(data, &value, sizeof(value));
}
} // Anonymous namespace
CFBStreamedCompression::CFBStreamedCompression(CInputStream& in, IObjectStore& objStore, bool pc) : m_pc(pc) { CFBStreamedCompression::CFBStreamedCompression(CInputStream& in, IObjectStore& objStore, bool pc) : m_pc(pc) {
x0_scratchSize = in.readUint32Big(); x0_scratchSize = in.readUint32Big();
@ -16,41 +34,46 @@ CFBStreamedCompression::CFBStreamedCompression(CInputStream& in, IObjectStore& o
x10_averageVelocity = CalculateAverageVelocity(GetPerChannelHeaders()); x10_averageVelocity = CalculateAverageVelocity(GetPerChannelHeaders());
} }
const u32* CFBStreamedCompression::GetTimes() const { return reinterpret_cast<const u32*>(xc_rotsAndOffs.get() + 9); } const u32* CFBStreamedCompression::GetTimes() const { return xc_rotsAndOffs.get() + 9; }
const u8* CFBStreamedCompression::GetPerChannelHeaders() const { const u8* CFBStreamedCompression::GetPerChannelHeaders() const {
const u32* bitmap = reinterpret_cast<const u32*>(xc_rotsAndOffs.get() + 9); const u32* bitmap = GetTimes();
u32 bitmapWordCount = (bitmap[0] + 31) / 32; const u32 bitmapWordCount = (bitmap[0] + 31) / 32;
return reinterpret_cast<const u8*>(bitmap + bitmapWordCount + 1); return reinterpret_cast<const u8*>(bitmap + bitmapWordCount + 1);
} }
const u8* CFBStreamedCompression::GetBitstreamPointer() const { const u8* CFBStreamedCompression::GetBitstreamPointer() const {
const u32* bitmap = reinterpret_cast<const u32*>(xc_rotsAndOffs.get() + 9); const u32* bitmap = GetTimes();
u32 bitmapWordCount = (bitmap[0] + 31) / 32; const u32 bitmapWordCount = (bitmap[0] + 31) / 32;
const u8* chans = reinterpret_cast<const u8*>(bitmap + bitmapWordCount + 1); const u8* chans = reinterpret_cast<const u8*>(bitmap + bitmapWordCount + 1);
u32 boneChanCount = *reinterpret_cast<const u32*>(chans); const u32 boneChanCount = ReadValue<u32>(chans);
chans += 4; chans += 4;
if (m_pc) { if (m_pc) {
for (unsigned b = 0; b < boneChanCount; ++b) { for (u32 b = 0; b < boneChanCount; ++b) {
chans += 20; chans += 20;
u32 tCount = *reinterpret_cast<const u32*>(chans); const u32 tCount = ReadValue<u32>(chans);
chans += 4; chans += 4;
if (tCount) if (tCount != 0) {
chans += 12; chans += 12;
} }
}
} else { } else {
for (unsigned b = 0; b < boneChanCount; ++b) { for (u32 b = 0; b < boneChanCount; ++b) {
chans += 15; chans += 15;
u16 tCount = *reinterpret_cast<const u16*>(chans); const u16 tCount = ReadValue<u16>(chans);
chans += 2; chans += 2;
if (tCount) if (tCount != 0) {
chans += 9; chans += 9;
} }
} }
}
return chans; return chans;
} }
@ -60,19 +83,20 @@ std::unique_ptr<u32[]> CFBStreamedCompression::GetRotationsAndOffsets(u32 words,
Header head; Header head;
head.read(in); head.read(in);
*reinterpret_cast<Header*>(ret.get()) = head; std::memcpy(ret.get(), &head, sizeof(head));
u32* bitmapOut = &ret[9]; u32* bitmapOut = &ret[9];
u32 bitmapBitCount = in.readUint32Big(); const u32 bitmapBitCount = in.readUint32Big();
bitmapOut[0] = bitmapBitCount; bitmapOut[0] = bitmapBitCount;
u32 bitmapWordCount = (bitmapBitCount + 31) / 32; const u32 bitmapWordCount = (bitmapBitCount + 31) / 32;
for (u32 i = 0; i < bitmapWordCount; ++i) for (u32 i = 0; i < bitmapWordCount; ++i) {
bitmapOut[i + 1] = in.readUint32Big(); bitmapOut[i + 1] = in.readUint32Big();
}
in.readUint32Big(); in.readUint32Big();
u8* chans = reinterpret_cast<u8*>(bitmapOut + bitmapWordCount + 1); u8* chans = reinterpret_cast<u8*>(bitmapOut + bitmapWordCount + 1);
u8* bs = ReadBoneChannelDescriptors(chans, in); u8* bs = ReadBoneChannelDescriptors(chans, in);
u32 bsWords = ComputeBitstreamWords(chans); const u32 bsWords = ComputeBitstreamWords(chans);
u32* bsPtr = reinterpret_cast<u32*>(bs); u32* bsPtr = reinterpret_cast<u32*>(bs);
for (u32 w = 0; w < bsWords; ++w) for (u32 w = 0; w < bsWords; ++w)
@ -82,58 +106,58 @@ std::unique_ptr<u32[]> CFBStreamedCompression::GetRotationsAndOffsets(u32 words,
} }
u8* CFBStreamedCompression::ReadBoneChannelDescriptors(u8* out, CInputStream& in) const { u8* CFBStreamedCompression::ReadBoneChannelDescriptors(u8* out, CInputStream& in) const {
u32 boneChanCount = in.readUint32Big(); const u32 boneChanCount = in.readUint32Big();
*reinterpret_cast<u32*>(out) = boneChanCount; WriteValue(out, boneChanCount);
out += 4; out += 4;
if (m_pc) { if (m_pc) {
for (unsigned b = 0; b < boneChanCount; ++b) { for (u32 b = 0; b < boneChanCount; ++b) {
*reinterpret_cast<u32*>(out) = in.readUint32Big(); WriteValue(out, in.readUint32Big());
out += 4; out += 4;
*reinterpret_cast<u32*>(out) = in.readUint32Big(); WriteValue(out, in.readUint32Big());
out += 4; out += 4;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
*reinterpret_cast<u32*>(out) = in.readUint32Big(); WriteValue(out, in.readUint32Big());
out += 4; out += 4;
} }
u32 tCount = in.readUint32Big(); const u32 tCount = in.readUint32Big();
*reinterpret_cast<u32*>(out) = tCount; WriteValue(out, tCount);
out += 4; out += 4;
if (tCount) { if (tCount != 0) {
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
*reinterpret_cast<u32*>(out) = in.readUint32Big(); WriteValue(out, in.readUint32Big());
out += 4; out += 4;
} }
} }
} }
} else { } else {
for (unsigned b = 0; b < boneChanCount; ++b) { for (u32 b = 0; b < boneChanCount; ++b) {
*reinterpret_cast<u32*>(out) = in.readUint32Big(); WriteValue(out, in.readUint32Big());
out += 4; out += 4;
*reinterpret_cast<u16*>(out) = in.readUint16Big(); WriteValue(out, in.readUint16Big());
out += 2; out += 2;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
*reinterpret_cast<s16*>(out) = in.readInt16Big(); WriteValue(out, in.readInt16Big());
out += 2; out += 2;
*reinterpret_cast<u8*>(out) = in.readUByte(); WriteValue(out, in.readUByte());
out += 1; out += 1;
} }
u16 tCount = in.readUint16Big(); const u16 tCount = in.readUint16Big();
*reinterpret_cast<u16*>(out) = tCount; WriteValue(out, tCount);
out += 2; out += 2;
if (tCount) { if (tCount != 0) {
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
*reinterpret_cast<s16*>(out) = in.readInt16Big(); WriteValue(out, in.readInt16Big());
out += 2; out += 2;
*reinterpret_cast<u8*>(out) = in.readUByte(); WriteValue(out, in.readUByte());
out += 1; out += 1;
} }
} }
@ -144,43 +168,43 @@ u8* CFBStreamedCompression::ReadBoneChannelDescriptors(u8* out, CInputStream& in
} }
u32 CFBStreamedCompression::ComputeBitstreamWords(const u8* chans) const { u32 CFBStreamedCompression::ComputeBitstreamWords(const u8* chans) const {
u32 boneChanCount = *reinterpret_cast<const u32*>(chans); const u32 boneChanCount = ReadValue<u32>(chans);
chans += 4; chans += 4;
u32 keyCount; u32 keyCount;
u32 totalBits = 0; u32 totalBits = 0;
if (m_pc) { if (m_pc) {
keyCount = *reinterpret_cast<const u32*>(chans + 0x4); keyCount = ReadValue<u32>(chans + 0x4);
for (u32 c = 0; c < boneChanCount; ++c) { for (u32 c = 0; c < boneChanCount; ++c) {
chans += 0x8; chans += 0x8;
totalBits += 1; totalBits += 1;
totalBits += *reinterpret_cast<const u32*>(chans) & 0xff; totalBits += ReadValue<u32>(chans) & 0xff;
totalBits += *reinterpret_cast<const u32*>(chans + 0x4) & 0xff; totalBits += ReadValue<u32>(chans + 0x4) & 0xff;
totalBits += *reinterpret_cast<const u32*>(chans + 0x8) & 0xff; totalBits += ReadValue<u32>(chans + 0x8) & 0xff;
u32 tKeyCount = *reinterpret_cast<const u32*>(chans + 0xc); const u32 tKeyCount = ReadValue<u32>(chans + 0xc);
chans += 0x10; chans += 0x10;
if (tKeyCount) { if (tKeyCount != 0) {
totalBits += *reinterpret_cast<const u32*>(chans) & 0xff; totalBits += ReadValue<u32>(chans) & 0xff;
totalBits += *reinterpret_cast<const u32*>(chans + 0x4) & 0xff; totalBits += ReadValue<u32>(chans + 0x4) & 0xff;
totalBits += *reinterpret_cast<const u32*>(chans + 0x8) & 0xff; totalBits += ReadValue<u32>(chans + 0x8) & 0xff;
chans += 0xc; chans += 0xc;
} }
} }
} else { } else {
keyCount = *reinterpret_cast<const u16*>(chans + 0x4); keyCount = ReadValue<u16>(chans + 0x4);
for (u32 c = 0; c < boneChanCount; ++c) { for (u32 c = 0; c < boneChanCount; ++c) {
chans += 0x6; chans += 0x6;
totalBits += 1; totalBits += 1;
totalBits += *reinterpret_cast<const u8*>(chans + 0x2); totalBits += ReadValue<u8>(chans + 0x2);
totalBits += *reinterpret_cast<const u8*>(chans + 0x5); totalBits += ReadValue<u8>(chans + 0x5);
totalBits += *reinterpret_cast<const u8*>(chans + 0x8); totalBits += ReadValue<u8>(chans + 0x8);
u16 tKeyCount = *reinterpret_cast<const u16*>(chans + 0x9); const u16 tKeyCount = ReadValue<u16>(chans + 0x9);
chans += 0xb; chans += 0xb;
if (tKeyCount) { if (tKeyCount != 0) {
totalBits += *reinterpret_cast<const u8*>(chans + 0x2); totalBits += ReadValue<u8>(chans + 0x2);
totalBits += *reinterpret_cast<const u8*>(chans + 0x5); totalBits += ReadValue<u8>(chans + 0x5);
totalBits += *reinterpret_cast<const u8*>(chans + 0x8); totalBits += ReadValue<u8>(chans + 0x8);
chans += 0x9; chans += 0x9;
} }
} }
@ -190,40 +214,44 @@ u32 CFBStreamedCompression::ComputeBitstreamWords(const u8* chans) const {
} }
float CFBStreamedCompression::CalculateAverageVelocity(const u8* chans) const { float CFBStreamedCompression::CalculateAverageVelocity(const u8* chans) const {
u32 boneChanCount = *reinterpret_cast<const u32*>(chans); const u32 boneChanCount = ReadValue<u32>(chans);
chans += 4; chans += 4;
u32 keyCount; u32 keyCount;
u32 rootIdx = 0; u32 rootIdx = 0;
if (m_pc) { if (m_pc) {
keyCount = *reinterpret_cast<const u32*>(chans + 0x4); keyCount = ReadValue<u32>(chans + 0x4);
for (u32 c = 0; c < boneChanCount; ++c) { for (u32 c = 0; c < boneChanCount; ++c) {
u32 boneId = *reinterpret_cast<const u32*>(chans); const u32 boneId = ReadValue<u32>(chans);
if (boneId == 3) if (boneId == 3) {
break; break;
}
++rootIdx; ++rootIdx;
chans += 0x8; chans += 0x8;
u32 tKeyCount = *reinterpret_cast<const u32*>(chans + 0xc); const u32 tKeyCount = ReadValue<u32>(chans + 0xc);
chans += 0x10; chans += 0x10;
if (tKeyCount) if (tKeyCount != 0) {
chans += 0xc; chans += 0xc;
} }
}
} else { } else {
keyCount = *reinterpret_cast<const u16*>(chans + 0x4); keyCount = ReadValue<u16>(chans + 0x4);
for (u32 c = 0; c < boneChanCount; ++c) { for (u32 c = 0; c < boneChanCount; ++c) {
u32 boneId = *reinterpret_cast<const u32*>(chans); const u32 boneId = ReadValue<u32>(chans);
if (boneId == 3) if (boneId == 3) {
break; break;
}
++rootIdx; ++rootIdx;
chans += 0x6; chans += 0x6;
u16 tKeyCount = *reinterpret_cast<const u16*>(chans + 0x9); const u16 tKeyCount = ReadValue<u16>(chans + 0x9);
chans += 0xb; chans += 0xb;
if (tKeyCount) if (tKeyCount != 0) {
chans += 0x9; chans += 0x9;
} }
} }
}
CBitLevelLoader loader(GetBitstreamPointer()); CBitLevelLoader loader(GetBitstreamPointer());
CFBStreamedAnimReaderTotals tempTotals(*this); CFBStreamedAnimReaderTotals tempTotals(*this);