Complete disaster-recovery snapshot: engine/game source, game data assets, VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive. Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false, no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
608 lines
15 KiB
C++
608 lines
15 KiB
C++
//===========================================================================//
|
|
// File: AnimFormat.cpp
|
|
// Project: MechWarrior 4
|
|
// Contents:
|
|
// This is the base animation player source file.
|
|
// This file defines Keyframes and Animation Player classes
|
|
// PLEASE NOTE!!!! THIS FILE IS USED BY THE PLUGIN :
|
|
// 3dsplugins/AnimationExport
|
|
// If you change this you must also change the plugin!!!!
|
|
//
|
|
// THIS IS A THREAT!
|
|
// All the animations that have been exported rely on this format
|
|
// If you change this format, you better know what you are doing!
|
|
// If you don't support multiple versions when you change this
|
|
// Then you HAVE TO EXPORT ALL THE ANIMATIONS AGAIN!!!!!
|
|
// The max plugin environment allows for this.
|
|
//---------------------------------------------------------------------------//
|
|
// Date Who Modification //
|
|
// -------- --- ---------------------------------------------------------- //
|
|
// 07/01/98 JSE Initial coding,
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1998, Fasa Interactive, Inc.
|
|
// All Rights reserved worldwide
|
|
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
|
|
//===========================================================================//
|
|
|
|
|
|
|
|
|
|
#include <MW4\MW4AnimationSystem.hpp>
|
|
#include <Adept\Resource.hpp>
|
|
|
|
using namespace MW4Animation;
|
|
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
AnimData::AnimData()
|
|
{
|
|
|
|
startOfBlocks = NULL;
|
|
animHeaderBlock = NULL;
|
|
animJointBlock = NULL;
|
|
animNameBlock = NULL;
|
|
animKeyTimeBlock = NULL;
|
|
animKeyBlock = NULL;
|
|
animChannelBlock = NULL;
|
|
|
|
|
|
animHeaderBlockSize = 0;
|
|
animJointBlockSize = 0;
|
|
animKeyTimeBlockSize = 0;
|
|
animKeyBlockSize = 0;
|
|
animNameBlockSize = 0;
|
|
animChannelBlockSize = 0;
|
|
|
|
}
|
|
|
|
|
|
AnimData::~AnimData()
|
|
{
|
|
|
|
// I don't actually delete any of the extra arrays.
|
|
// The reason is that it is allocated as one LARGE piece
|
|
// of memory. I do, however register the memory as a
|
|
// "precaution"
|
|
|
|
|
|
if (animKeyBlock != NULL)
|
|
{
|
|
Unregister_Pointer(animKeyBlock);
|
|
// delete[] animKeyBlock;
|
|
}
|
|
|
|
if (animKeyTimeBlock != NULL)
|
|
{
|
|
Unregister_Pointer(animKeyTimeBlock);
|
|
// delete[] animKeyTimeBlock;
|
|
}
|
|
|
|
|
|
if (animChannelBlock != NULL)
|
|
{
|
|
Unregister_Pointer(animChannelBlock);
|
|
// delete[] animChannelBlock;
|
|
}
|
|
|
|
if (animNameBlock != NULL)
|
|
{
|
|
Unregister_Pointer(animNameBlock);
|
|
// delete[] animNameBlock;
|
|
}
|
|
|
|
if (animJointBlock != NULL)
|
|
{
|
|
Unregister_Pointer(animJointBlock);
|
|
// delete[] animJointBlock;
|
|
}
|
|
|
|
if (animHeaderBlock != NULL)
|
|
{
|
|
Unregister_Pointer(animHeaderBlock);
|
|
// delete animHeaderBlock;
|
|
}
|
|
|
|
if (startOfBlocks != NULL)
|
|
{
|
|
Unregister_Pointer(startOfBlocks);
|
|
delete[] startOfBlocks;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
|
|
bool AnimData::Save(const char *file_name)
|
|
{
|
|
Stuff::FileStream file_stream(file_name, Stuff::FileStream::WriteOnly);
|
|
Save(&file_stream);
|
|
return true;
|
|
}
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
|
|
bool AnimData::Load(const char *file_name)
|
|
{
|
|
Stuff::FileStream file_stream(file_name);
|
|
if (!Load(&file_stream))
|
|
{
|
|
SPEW((0, "Anim is corrupt or wrong format : "));
|
|
SPEW((0, file_name));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
bool AnimData::Load(Adept::Resource *resource)
|
|
{
|
|
resource->LoadData();
|
|
|
|
|
|
resource->AdvancePointer(sizeof(BYTE));
|
|
|
|
AnimHeader temp_header_block;
|
|
|
|
resource->ReadBytes(&temp_header_block, sizeof(AnimHeader));
|
|
|
|
resource->RewindPointer(sizeof(BYTE) + sizeof(AnimHeader));
|
|
|
|
|
|
if (temp_header_block.majorVersion == MAJOR_ANIM_VERSION_1_0 && temp_header_block.minorVersion == MINOR_ANIM_VERSION_1_0)
|
|
{
|
|
return Load_1_0((Stuff::MemoryStream*)resource);
|
|
}
|
|
|
|
if (temp_header_block.majorVersion == MAJOR_ANIM_VERSION_2_0 && temp_header_block.minorVersion == MINOR_ANIM_VERSION_2_0)
|
|
{
|
|
return Load_2_0((Stuff::MemoryStream*)resource);
|
|
}
|
|
|
|
if (temp_header_block.majorVersion == MAJOR_ANIM_VERSION_2_1 && temp_header_block.minorVersion == MINOR_ANIM_VERSION_2_1)
|
|
{
|
|
return Load_2_1((Stuff::MemoryStream*)resource);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
bool AnimData::Save(Stuff::FileStream *stream)
|
|
{
|
|
return Save((Stuff::MemoryStream*)stream);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
bool AnimData::Load(Stuff::FileStream *stream)
|
|
{
|
|
return CopyAndLoad(stream);
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
bool AnimData::CopyAndLoad(Stuff::MemoryStream *stream)
|
|
{
|
|
// copy the file into a memory chunk and make a stream out of it.
|
|
// then pass it to the load function.
|
|
|
|
|
|
// skip the null byte and get the header
|
|
// we are getting the header since this format can
|
|
// be munged together inside of a bigger file
|
|
|
|
// still try to verify this is a good file
|
|
|
|
stream->AdvancePointer(sizeof(BYTE));
|
|
|
|
AnimHeader temp_header_block;
|
|
|
|
stream->ReadBytes(&temp_header_block, sizeof(AnimHeader));
|
|
|
|
stream->RewindPointer(sizeof(BYTE) + sizeof(AnimHeader));
|
|
|
|
|
|
if (temp_header_block.majorVersion == MAJOR_ANIM_VERSION_1_0 && temp_header_block.minorVersion == MINOR_ANIM_VERSION_1_0)
|
|
{
|
|
|
|
// since it is a file and it will be closed we are going to buffer it
|
|
// to memory
|
|
|
|
gos_PushCurrentHeap(g_AnimDataHeap);
|
|
|
|
startOfBlocks = new BYTE[temp_header_block.totalSizeOfAnimation + 1];
|
|
Register_Pointer(startOfBlocks);
|
|
|
|
gos_PopCurrentHeap();
|
|
|
|
Stuff::MemoryStream mem_stream(startOfBlocks, temp_header_block.totalSizeOfAnimation + 1);
|
|
|
|
stream->ReadBytes(mem_stream.GetPointer(), temp_header_block.totalSizeOfAnimation);
|
|
|
|
|
|
return Load_1_0(&mem_stream);
|
|
}
|
|
|
|
if (temp_header_block.majorVersion == MAJOR_ANIM_VERSION_2_0 && temp_header_block.minorVersion == MINOR_ANIM_VERSION_2_0)
|
|
{
|
|
// since it is a file and it will be closed we are going to buffer it
|
|
// to memory
|
|
|
|
gos_PushCurrentHeap(g_AnimDataHeap);
|
|
|
|
startOfBlocks = new BYTE[temp_header_block.totalSizeOfAnimation + 1];
|
|
Register_Pointer(startOfBlocks);
|
|
|
|
gos_PopCurrentHeap();
|
|
|
|
Stuff::MemoryStream mem_stream(startOfBlocks, temp_header_block.totalSizeOfAnimation + 1);
|
|
|
|
stream->ReadBytes(mem_stream.GetPointer(), temp_header_block.totalSizeOfAnimation);
|
|
|
|
|
|
return Load_2_0(&mem_stream);
|
|
}
|
|
|
|
if (temp_header_block.majorVersion == MAJOR_ANIM_VERSION_2_1 && temp_header_block.minorVersion == MINOR_ANIM_VERSION_2_1)
|
|
{
|
|
// since it is a file and it will be closed we are going to buffer it
|
|
// to memory
|
|
|
|
gos_PushCurrentHeap(g_AnimDataHeap);
|
|
|
|
startOfBlocks = new BYTE[temp_header_block.totalSizeOfAnimation + 1];
|
|
Register_Pointer(startOfBlocks);
|
|
|
|
gos_PopCurrentHeap();
|
|
|
|
Stuff::MemoryStream mem_stream(startOfBlocks, temp_header_block.totalSizeOfAnimation + 1);
|
|
|
|
stream->ReadBytes(mem_stream.GetPointer(), temp_header_block.totalSizeOfAnimation);
|
|
|
|
|
|
return Load_2_1(&mem_stream);
|
|
}
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
|
|
bool AnimData::Save(Stuff::MemoryStream *stream)
|
|
{
|
|
BYTE null_byte = NULL;
|
|
|
|
stream->WriteBytes(&null_byte, sizeof(BYTE));
|
|
|
|
// write the rest of the data out
|
|
Verify(animHeaderBlock->offsetToHeaderBlock == stream->GetBytesUsed()-1);
|
|
stream->WriteBytes(animHeaderBlock,animHeaderBlockSize);
|
|
Verify(animHeaderBlock->offsetToJointBlock == stream->GetBytesUsed()-1);
|
|
stream->WriteBytes(animJointBlock,animJointBlockSize);
|
|
Verify(animHeaderBlock->offsetToNameBlock == stream->GetBytesUsed()-1);
|
|
stream->WriteBytes(animNameBlock,animNameBlockSize);
|
|
Verify(animHeaderBlock->offsetToKeyBlock == stream->GetBytesUsed()-1);
|
|
stream->WriteBytes(animKeyBlock,animKeyBlockSize);
|
|
Verify(animHeaderBlock->offsetToKeyTimeBlock == stream->GetBytesUsed()-1);
|
|
stream->WriteBytes(animKeyTimeBlock, animKeyTimeBlockSize);
|
|
Verify(animHeaderBlock->offsetToChannelBlock == stream->GetBytesUsed()-1);
|
|
stream->WriteBytes(animChannelBlock,animChannelBlockSize);
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
|
|
bool AnimData::Load_2_1(Stuff::MemoryStream *stream)
|
|
{
|
|
|
|
|
|
// skip the null byte
|
|
BYTE *null_byte;
|
|
null_byte = (BYTE *)stream->GetPointer();
|
|
|
|
if (*null_byte != NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Verify(*null_byte == NULL);
|
|
stream->AdvancePointer(sizeof(BYTE));
|
|
|
|
size_t start_index = stream->GetIndex();
|
|
|
|
// load the header
|
|
animHeaderBlock = (AnimHeader *)stream->GetPointer();
|
|
Register_Pointer(animHeaderBlock);
|
|
|
|
|
|
if (animHeaderBlock->majorVersion != MAJOR_ANIM_VERSION_2_1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (animHeaderBlock->minorVersion != MINOR_ANIM_VERSION_2_1)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
Verify(
|
|
(animHeaderBlock->offsetToJointBlock - animHeaderBlock->offsetToHeaderBlock)
|
|
== sizeof(AnimHeader)
|
|
);
|
|
|
|
Verify(animHeaderBlock->majorVersion == MAJOR_ANIM_VERSION_2_1);
|
|
Verify(animHeaderBlock->minorVersion == MINOR_ANIM_VERSION_2_1);
|
|
|
|
// register the rest of the arrays
|
|
stream->SetPointer(animHeaderBlock->offsetToJointBlock + start_index);
|
|
animJointBlock = (JointAnimData *)stream->GetPointer();
|
|
Register_Pointer(animJointBlock);
|
|
|
|
stream->SetPointer(animHeaderBlock->offsetToNameBlock + start_index);
|
|
animNameBlock = (char *)stream->GetPointer();
|
|
Register_Pointer(animNameBlock);
|
|
|
|
stream->SetPointer(animHeaderBlock->offsetToKeyBlock + start_index);
|
|
animKeyBlock = (void *)stream->GetPointer();
|
|
Register_Pointer(animKeyBlock);
|
|
|
|
stream->SetPointer(animHeaderBlock->offsetToKeyTimeBlock + start_index);
|
|
animKeyTimeBlock = (void *)stream->GetPointer();
|
|
Register_Pointer(animKeyTimeBlock);
|
|
|
|
stream->SetPointer(animHeaderBlock->offsetToChannelBlock + start_index);
|
|
animChannelBlock = (ChannelAnimData *)stream->GetPointer();
|
|
Register_Pointer(animChannelBlock);
|
|
|
|
stream->SetPointer(animHeaderBlock->totalSizeOfAnimation + start_index);
|
|
|
|
animHeaderBlockSize = animHeaderBlock->offsetToJointBlock - start_index;
|
|
animJointBlockSize = animHeaderBlock->offsetToNameBlock - animHeaderBlock->offsetToJointBlock;
|
|
animNameBlockSize = animHeaderBlock->offsetToKeyBlock - animHeaderBlock->offsetToNameBlock;
|
|
animKeyBlockSize = animHeaderBlock->offsetToKeyTimeBlock - animHeaderBlock->offsetToKeyBlock;
|
|
animKeyTimeBlockSize = animHeaderBlock->offsetToChannelBlock - animHeaderBlock->offsetToKeyTimeBlock;
|
|
animChannelBlockSize = animHeaderBlock->totalSizeOfAnimation - animHeaderBlock->offsetToChannelBlock;
|
|
|
|
|
|
// check that keyframe counts are lower than 256
|
|
|
|
#ifdef _ARMOR
|
|
int count = GetChannelCount();
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
{
|
|
Verify(animChannelBlock[i].keyframeCount < AnimIterator::MaxKeyFrame);
|
|
}
|
|
#endif
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
bool AnimData::Load_1_0(Stuff::MemoryStream *stream)
|
|
{
|
|
|
|
|
|
// skip the null byte
|
|
BYTE *null_byte;
|
|
null_byte = (BYTE *)stream->GetPointer();
|
|
|
|
if (*null_byte != NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Verify(*null_byte == NULL);
|
|
stream->AdvancePointer(sizeof(BYTE));
|
|
|
|
|
|
// load the header
|
|
animHeaderBlock = (AnimHeader *)stream->GetPointer();
|
|
Register_Pointer(animHeaderBlock);
|
|
|
|
|
|
if (animHeaderBlock->majorVersion != MAJOR_ANIM_VERSION_1_0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (animHeaderBlock->minorVersion != MINOR_ANIM_VERSION_1_0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
STOP (("Animformat 1.0 Not Supported"));
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
bool AnimData::Load_2_0(Stuff::MemoryStream *stream)
|
|
{
|
|
|
|
|
|
// skip the null byte
|
|
BYTE *null_byte;
|
|
null_byte = (BYTE *)stream->GetPointer();
|
|
|
|
if (*null_byte != NULL)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Verify(*null_byte == NULL);
|
|
stream->AdvancePointer(sizeof(BYTE));
|
|
|
|
|
|
// load the header
|
|
animHeaderBlock = (AnimHeader *)stream->GetPointer();
|
|
Register_Pointer(animHeaderBlock);
|
|
|
|
|
|
if (animHeaderBlock->majorVersion != MAJOR_ANIM_VERSION_1_0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (animHeaderBlock->minorVersion != MINOR_ANIM_VERSION_1_0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
STOP (("Animformat 2.0 Not Supported"));
|
|
|
|
return false;
|
|
|
|
}
|
|
//
|
|
//#############################################################################
|
|
//#############################################################################
|
|
//
|
|
|
|
|
|
|
|
char *Joint_Name_Compression_Table[COMPRESS_JOINT_COUNT] =
|
|
{
|
|
"joint_cage",
|
|
"joint_centertorsofront",
|
|
"joint_centertorsorear",
|
|
"joint_head",
|
|
"joint_hip",
|
|
"joint_hipabove",
|
|
"joint_hipbelow",
|
|
"joint_lankle",
|
|
"joint_lbelowankle",
|
|
"joint_lbtoe",
|
|
"joint_ldleg",
|
|
"joint_lefttorsofront",
|
|
"joint_lfoot",
|
|
"joint_lftoe",
|
|
"joint_lgun",
|
|
"joint_lgunabove",
|
|
"joint_litoe",
|
|
"joint_lmissile",
|
|
"joint_lmleg",
|
|
"joint_lotoe",
|
|
"joint_ltoe",
|
|
"joint_luarm",
|
|
"joint_luleg",
|
|
"joint_rankle",
|
|
"joint_rbelowankle",
|
|
"joint_rbtoe",
|
|
"joint_rdleg",
|
|
"joint_rfoot",
|
|
"joint_rftoe",
|
|
"joint_rgun",
|
|
"joint_rgunabove",
|
|
"joint_righttorsofront",
|
|
"joint_ritoe",
|
|
"joint_rmissile",
|
|
"joint_rmleg",
|
|
"joint_root",
|
|
"joint_rotoe",
|
|
"joint_rtoe",
|
|
"joint_ruarm",
|
|
"joint_ruleg",
|
|
"joint_specialone",
|
|
"joint_specialtwo",
|
|
"joint_torso",
|
|
"joint_torsoabove",
|
|
"joint_torsobelow",
|
|
"joint_vel",
|
|
"joint_world"
|
|
};
|
|
|
|
|
|
int MW4Animation::CompressName(const char *source, Stuff::MString &dest)
|
|
{
|
|
for (BYTE i = 0; i < COMPRESS_JOINT_COUNT; ++i)
|
|
{
|
|
if (!stricmp(source, Joint_Name_Compression_Table[i]))
|
|
{
|
|
BYTE index = (BYTE)(128 + i);
|
|
dest = "#";
|
|
dest += (char)index;
|
|
dest += "\0";
|
|
|
|
return i;
|
|
}
|
|
}
|
|
|
|
dest = source;
|
|
return -1;
|
|
}
|
|
|
|
|
|
int MW4Animation::UncompressName(const char *source, Stuff::MString &dest)
|
|
{
|
|
|
|
const char *test = source;
|
|
if (*test == '#')
|
|
{
|
|
++test;
|
|
BYTE index = (BYTE)*test;
|
|
index -= (BYTE)128;
|
|
dest = Joint_Name_Compression_Table[index];
|
|
|
|
return index;
|
|
}
|
|
|
|
dest = source;
|
|
return -1;
|
|
|
|
} |