[BUG FIX] DynOS Bin Vtx overflow (#79)

Vertex coords are saved as s16 instead of f32 inside DynOS .bin files,
causing unintended overflow for vertex coords outside of the range
[-32768, +32767]. The format cannot be changed without breaking all
existing DynOS models, so, when writing a .bin file, a sentinel value
is added at the top of the vertex buffer for buffers that need f32
coords. If that sentinel is detected during the reading process,
the next vertex coords will be read as f32 instead of s16.
This commit is contained in:
PeachyPeach 2022-05-03 02:31:29 +02:00 committed by GitHub
parent 6466fbe457
commit 92d3c19c51
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 66 additions and 8 deletions

View File

@ -1,5 +1,27 @@
#include "dynos.cpp.h"
#define F32VTX_SENTINEL_0 0x3346
#define F32VTX_SENTINEL_1 0x5632
#define F32VTX_SENTINEL_2 0x5854
static inline bool ShouldUseF32Vtx(DataNode<Vtx>* aNode) {
for (u32 i = 0; i != aNode->mSize; ++i) {
for (u32 j = 0; j != 3; ++j) {
if (aNode->mData[i].n.ob[j] < -0x7FFF ||
aNode->mData[i].n.ob[j] > +0x7FFF) {
return true;
}
}
}
return false;
}
static inline bool IsUsingF32Vtx(Vec3f ob) {
return ob[0] == F32VTX_SENTINEL_0 &&
ob[1] == F32VTX_SENTINEL_1 &&
ob[2] == F32VTX_SENTINEL_2;
}
/////////////
// Parsing //
/////////////
@ -39,11 +61,34 @@ void DynOS_Vtx_Write(FILE* aFile, GfxData* aGfxData, DataNode<Vtx> *aNode) {
aNode->mName.Write(aFile);
// Data
WriteBytes<u32>(aFile, aNode->mSize);
bool shouldUseF32Vtx = ShouldUseF32Vtx(aNode);
if (shouldUseF32Vtx) {
WriteBytes<u32>(aFile, aNode->mSize + 1);
// Write sentinel
WriteBytes<s16>(aFile, F32VTX_SENTINEL_0);
WriteBytes<s16>(aFile, F32VTX_SENTINEL_1);
WriteBytes<s16>(aFile, F32VTX_SENTINEL_2);
WriteBytes<s16>(aFile, 0);
WriteBytes<s16>(aFile, 0);
WriteBytes<s16>(aFile, 0);
WriteBytes<s8> (aFile, 0);
WriteBytes<s8> (aFile, 0);
WriteBytes<s8> (aFile, 0);
WriteBytes<u8> (aFile, 0);
} else {
WriteBytes<u32>(aFile, aNode->mSize);
}
for (u32 i = 0; i != aNode->mSize; ++i) {
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[0]);
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[1]);
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[2]);
if (shouldUseF32Vtx) {
WriteBytes<f32>(aFile, aNode->mData[i].n.ob[0]);
WriteBytes<f32>(aFile, aNode->mData[i].n.ob[1]);
WriteBytes<f32>(aFile, aNode->mData[i].n.ob[2]);
} else {
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[0]);
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[1]);
WriteBytes<s16>(aFile, aNode->mData[i].n.ob[2]);
}
WriteBytes<s16>(aFile, aNode->mData[i].n.flag);
WriteBytes<s16>(aFile, aNode->mData[i].n.tc[0]);
WriteBytes<s16>(aFile, aNode->mData[i].n.tc[1]);
@ -65,19 +110,32 @@ void DynOS_Vtx_Load(FILE *aFile, GfxData *aGfxData) {
_Node->mName.Read(aFile);
// Data
bool isUsingF32Vtx = false;
_Node->mSize = ReadBytes<u32>(aFile);
_Node->mData = New<Vtx>(_Node->mSize);
for (u32 i = 0; i != _Node->mSize; ++i) {
_Node->mData[i].n.ob[0] = ReadBytes<s16>(aFile);
_Node->mData[i].n.ob[1] = ReadBytes<s16>(aFile);
_Node->mData[i].n.ob[2] = ReadBytes<s16>(aFile);
if (isUsingF32Vtx) {
_Node->mData[i].n.ob[0] = ReadBytes<f32>(aFile);
_Node->mData[i].n.ob[1] = ReadBytes<f32>(aFile);
_Node->mData[i].n.ob[2] = ReadBytes<f32>(aFile);
} else {
_Node->mData[i].n.ob[0] = ReadBytes<s16>(aFile);
_Node->mData[i].n.ob[1] = ReadBytes<s16>(aFile);
_Node->mData[i].n.ob[2] = ReadBytes<s16>(aFile);
}
_Node->mData[i].n.flag = ReadBytes<s16>(aFile);
_Node->mData[i].n.tc[0] = ReadBytes<s16>(aFile);
_Node->mData[i].n.tc[1] = ReadBytes<s16>(aFile);
_Node->mData[i].n.n[0] = ReadBytes<s8> (aFile);
_Node->mData[i].n.n[1] = ReadBytes<s8> (aFile);
_Node->mData[i].n.n[2] = ReadBytes<s8> (aFile);
_Node->mData[i].n.a = ReadBytes<u8>(aFile);
_Node->mData[i].n.a = ReadBytes<u8> (aFile);
// Check sentinel on first vertex
if (!isUsingF32Vtx && i == 0 && IsUsingF32Vtx(_Node->mData[i].n.ob)) {
_Node->mSize--; i--;
isUsingF32Vtx = true;
}
}
// Append