diff --git a/NIF_Blocks.cpp b/NIF_Blocks.cpp index 077086e87f371548ab8eed8a70834ccb5e5aad02..74029b508b9667b7caa02f987d7fc1d93e930d1d 100644 --- a/NIF_Blocks.cpp +++ b/NIF_Blocks.cpp @@ -67,6 +67,7 @@ ABlock::~ABlock() { delete _attr_vect[i].ptr(); } + //cout << endl << "Removing cross reference to " << this << " from " << uint(_cross_refs.size()) << " blocks"; // Inform all cross-linked blocks that have added their references that this block is dying list<IBlock*>::iterator it; for (it = _cross_refs.begin(); it != _cross_refs.end(); ++it) { @@ -180,6 +181,15 @@ void ABlock::AddAttr( AttrType type, string const & name, unsigned int first_ver case attr_quaternion: attr = new QuaternionAttr( name, this, first_ver, last_ver ); break; + case attr_emitterobject: + attr = new EmitterObjectAttr( name, this, first_ver, last_ver ); + break; + case attr_selflink: + attr = new SelfLinkAttr( name, this, first_ver, last_ver ); + break; + case attr_crossref: + attr = new CrossRefAttr( name, this, first_ver, last_ver ); + break; default: cout << type << endl; throw runtime_error("Unknown attribute type requested."); @@ -357,6 +367,15 @@ void ABlock::RemoveChild( IBlock * old_child ) { //} } +void ABlock::RemoveCrossLink( IBlock * block_to_remove ) { + //Ask all attributes to remove any cross links they might have to the specified block + //cout << endl << "ABlock::RemoveCrossLink()"; + vector<attr_ref>::iterator it; + for ( it = _attr_vect.begin(); it != _attr_vect.end(); ++it ) { + ((AAttr*)it->ptr())->RemoveCrossLinks( block_to_remove ); + } +} + void ABlock::AddParent( IBlock * new_parent) { //Don't add null parents @@ -1772,14 +1791,17 @@ void NiMeshPSysData::Read( istream& file, unsigned int version ) { unkFloats.resize( vertices.size() * 14 ); NifStream( unkFloats, file ); - NifStream( unk2Ints[0], file ); - NifStream( unk2Ints[1], file ); + NifStream( unkInt, file ); - NifStream( unkByte, file ); + GetAttr("Modifier")->Read( file, version ); + + // From version 10.2.0.0 there are several new entries here + if ( version >= VER_10_2_0_0 ) { + NifStream( unkByte, file ); - NifStream( unk3Ints[0], file ); - NifStream( unk3Ints[1], file ); - NifStream( unk3Ints[2], file ); + GetAttr("Unknown Link Group")->Read( file, version ); + GetAttr("Unknown Link 2")->Read( file, version ); + } } void NiMeshPSysData::Write( ostream& file, unsigned int version ) const { @@ -1787,14 +1809,17 @@ void NiMeshPSysData::Write( ostream& file, unsigned int version ) const { NifStream( unkFloats, file ); - NifStream( unk2Ints[0], file ); - NifStream( unk2Ints[1], file ); + NifStream( unkInt, file ); - NifStream( unkByte, file ); + GetAttr("Modifier")->Write( file, version ); - NifStream( unk3Ints[0], file ); - NifStream( unk3Ints[1], file ); - NifStream( unk3Ints[2], file ); + // From version 10.2.0.0 there are several new entries here + if ( version >= VER_10_2_0_0 ) { + NifStream( unkByte, file ); + + GetAttr("Unknown Link Group")->Write( file, version ); + GetAttr("Unknown Link 2")->Write( file, version ); + } } string NiMeshPSysData::asString() const { @@ -1814,12 +1839,11 @@ string NiMeshPSysData::asString() const { out << " <<<Data Not Shown>>>"; } - out << "Unknown Int 1: " << unk2Ints[0] << endl - << "Unknown Int 2: " << unk2Ints[1] << endl + out << "Unknown Int: " << unkInt << endl + << "Modifier: " << GetAttr("Modifier")->asString() << endl << "Unknown Byte: " << unkByte << endl - << "Unknown Int 3: " << unk3Ints[0] << endl - << "Unknown Int 4: " << unk3Ints[1] << endl - << "Unknown Int 5: " << unk3Ints[2] << endl; + << "Unknown Link Group: " << GetAttr("Unknown Link Group")->asString() << endl + << "Unknown Link 2: " << GetAttr("Unknown Link 2")->asString() << endl; return out.str(); } diff --git a/NIF_Blocks.h b/NIF_Blocks.h index e1268c3a2183387a4568839f50270ccdae60a372..6368624a9d7215c2845462d9b2aada31a93d2ef6 100644 --- a/NIF_Blocks.h +++ b/NIF_Blocks.h @@ -138,7 +138,7 @@ public: void AddChild( IBlock * new_child ); void RemoveChild( IBlock * old_child ); - virtual void RemoveCrossLink( IBlock * block_to_remove ) {}; + virtual void RemoveCrossLink( IBlock * block_to_remove ); void IncCrossRef( IBlock * block ); void DecCrossRef( IBlock * block ); @@ -877,7 +877,11 @@ protected: class NiMeshPSysData : public APSysData { public: - NiMeshPSysData() {} + NiMeshPSysData() { + AddAttr( attr_link, "Modifier" ); + AddAttr( attr_linkgroup, "Unknown Link Group", VER_10_2_0_0 ); + AddAttr( attr_link, "Unknown Link 2", VER_10_2_0_0 ); + } ~NiMeshPSysData() {} void Read( istream& in, unsigned int version ); void Write( ostream& out, unsigned int version ) const; @@ -885,7 +889,7 @@ public: string GetBlockType() const { return "NiMeshPSysData"; }; protected: vector<float> unkFloats; - uint unk2Ints[2]; + uint unkInt; byte unkByte; uint unk3Ints[3]; }; diff --git a/docsys_extract.cpp b/docsys_extract.cpp index 72e4dc5a90882a76ffbb6aa044229a53d86ec1ba..686466457b8af3726f473040ea945f065680e96c 100644 --- a/docsys_extract.cpp +++ b/docsys_extract.cpp @@ -182,7 +182,7 @@ APSysModifier::APSysModifier() { } APSysVolumeEmitter::APSysVolumeEmitter() { - AddAttr( attr_link, "Emitter Object", 335544324, 0xFFFFFFFF ); + AddAttr( attr_emitterobject, "Emitter Object", 335544324, 0xFFFFFFFF ); Init(); } @@ -577,7 +577,7 @@ NiPSysColliderManager::NiPSysColliderManager() { AddAttr( attr_bool, "Spawn on Collide", 0, 0xFFFFFFFF ); AddAttr( attr_bool, "Die on Collide", 0, 0xFFFFFFFF ); AddAttr( attr_link, "Spawn Modifier", 0, 0xFFFFFFFF ); - AddAttr( attr_link, "Manager", 0, 0xFFFFFFFF ); + AddAttr( attr_selflink, "Manager", 0, 0xFFFFFFFF ); AddAttr( attr_link, "Unknown Link?", 0, 0xFFFFFFFF ); AddAttr( attr_link, "Collider Object", 0, 0xFFFFFFFF ); Init(); @@ -614,7 +614,7 @@ NiPSysEmitterInitialRadiusCtlr::NiPSysEmitterInitialRadiusCtlr() { } NiPSysGravityModifier::NiPSysGravityModifier() { - AddAttr( attr_link, "Gravity Object", 0, 0xFFFFFFFF ); + AddAttr( attr_crossref, "Gravity Object", 0, 0xFFFFFFFF ); AddAttr( attr_vector3, "Gravity Axis", 0, 0xFFFFFFFF ); AddAttr( attr_float, "Decay", 0, 0xFFFFFFFF ); AddAttr( attr_float, "Strength", 0, 0xFFFFFFFF ); diff --git a/nif_attrs.h b/nif_attrs.h index a8a9af40a202b9671f791a3a62cc8df977f0ddff..52f68a6a12323cce4abf6693ebe4e96c23aa62b5 100644 --- a/nif_attrs.h +++ b/nif_attrs.h @@ -91,6 +91,8 @@ public: this->WriteAttr( out, version ); } } + //Function to remove CrossLinks without decrimenting them + virtual void RemoveCrossLinks( IBlock * block_to_remove ) {}; protected: //Internal Read/Write Functions virtual void ReadAttr( istream& in, unsigned int version ) = 0; @@ -450,6 +452,127 @@ private: lnk_ref link; }; +class CrossRefAttr : public AAttr { +public: + CrossRefAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), link(NULL), tmp_blk_num(-1) {} + ~CrossRefAttr() { + //cout << endl << "~CrossRefAttr()"; + //This attribute is dying, so decriment the link + UpdateLink(NULL); + } + AttrType GetType() const { + //cout << endl << "CrossRefAttr::GetType()"; + return attr_link; + } + void ReadAttr( istream& in, unsigned int version ) { + //cout << endl << "CrossRefAttr::ReadAttr()"; + //Update any existing links to null + UpdateLink(NULL); + + tmp_blk_num = ReadUInt( in ); + } + void WriteAttr( ostream& out, unsigned int version ) const { + //cout << endl << "CrossRefAttr::WriteAttr()"; + if ( link == NULL ) { + WriteUInt( -1, out ); + } else { + WriteUInt( link->GetBlockNum(), out ); + } + } + string asString() const { + //cout << endl << "CrossRefAttr::asString()"; + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + out << GetBlkRef(); + + return out.str(); + } + bool HasLinks() const { + //cout << endl << "CrossRefAttr::HasLinks()"; + return true; + } + list<blk_ref> asLinkList() const { + //cout << endl << "CrossRefAttr::asLinkList()"; + list<blk_ref> out; + + out.push_back( GetBlkRef() ); + + return out; + } + void ClearLinks() { + //cout << endl << "CrossRefAttr::ClearLinks()"; + UpdateLink( NULL ); + } + void AddLinks( list<blk_ref> const & new_links ) { + //cout << endl << "CrossRefAttr::AddLinks()"; + //Just take the first one + UpdateLink( (*(new_links.begin())).get_block() ); + } + + void UpdateLink( IBlock * new_ptr ) { + //cout << endl << "CrossRefAttr::UpdateLink()"; + //cout << endl << "Setting tmp_blk_num to -1"; + //The link cannot be temporary after being updated + tmp_blk_num = -1; + + /*cout << endl << "Check if new pointer is same as old" + << endl << "Old Value: " << link + << endl << "New Value: " << new_ptr;*/ + //Check if the new pointer is the same as the old pointer + if ( new_ptr == link ) + return; + + //cout << endl << "If different, decrement ref to old site if it exists."; + //The new pointer is different, so decrement the reference at the old site if it exists + if ( link != NULL ) { + //cout << endl << "Decrimenting Cross Reference at: " << link; + ((ABlock*)link)->DecCrossRef( _owner ); + } + + //Now set the pointer to the new location + link = new_ptr; + + //If link is not null, we need to increment the cross reference count + if ( link != NULL ) { + //cout << endl << "Incrementing Cross Reference at: " << link; + ((ABlock*)link)->IncCrossRef( _owner ); + } + } + + blk_ref GetBlkRef() const { + //cout << endl << "CrossRefAttr::GetBlkRef()"; + //If the link is not fixed, there will be a temporary block number + if ( tmp_blk_num != -1 ) { + return blk_ref(tmp_blk_num); + } else { + if ( link == NULL ) { + return blk_ref(-1); + } else { + return blk_ref(link); + } + } + } + + blk_ref asLink() const { return GetBlkRef(); } + void Set( blk_ref const & n ) { + //cout << endl << "CrossRefAttr::Set()"; + UpdateLink( n.get_block() ); + } + void RemoveCrossLinks( IBlock * block_to_remove ) { + //cout << endl << "CrossRefAttr::RemoveCrossLinks()"; + if ( link == block_to_remove ) { + link = NULL; + //Do not decrement target location because it is already dead + } + } +private: + int tmp_blk_num; + IBlock * link; +}; + + class FlagsAttr : public AAttr { public: FlagsAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ), data(0) {} @@ -713,14 +836,23 @@ protected: LinkSetList links; }; -class TargetGroupAttr : public LinkGroupAttr { +class TargetGroupAttr : public AAttr { public: - TargetGroupAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : LinkGroupAttr( name, owner, first_ver, last_ver ) {} - ~TargetGroupAttr() {} + TargetGroupAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr( name, owner, first_ver, last_ver ) { + //cout << endl << "TargetGroupAttr()"; + } + ~TargetGroupAttr() { + //cout << endl << "~TargetGroupAttr()"; + ClearLinks(); + } - AttrType GetType() const { return attr_targetgroup; } + AttrType GetType() const { + //cout << endl << "GetType()"; + return attr_targetgroup; + } void ReadAttr( istream& in, unsigned int version ) { + //cout << endl << "ReadAttr()"; int len = ReadUShort( in ); if ( len > 1000 ) { @@ -728,12 +860,13 @@ public: } for (int i = 0; i < len; ++i ) { - int index = ReadUInt( in ); - if (index != -1 ) - AddLink( blk_ref( index ) ); + CrossRefAttr * new_attr = new CrossRefAttr( "", _owner, 0, 0xFFFFFFFF ); + new_attr->Read( in, version ); + links.push_back( new_attr ); } } void WriteAttr( ostream& out, unsigned int version ) const { + //cout << endl << "WriteAttr()"; //Write the number of links WriteUShort( ushort(links.size()), out ); //cout << "Link Group Size: " << uint(links.size()) << endl; @@ -743,10 +876,110 @@ public: } //Write the block indices - for (LinkSetConstIt it = links.begin(); it != links.end(); ++it ) { - WriteUInt( it->get_index(), out ); + for ( uint i = 0; i < links.size(); ++i ) { + links[i]->Write( out, version ); } } + string asString() const { + //cout << endl << "asString()"; + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + for ( uint i = 0; i < links.size(); ++i ) { + out << endl << " " << links[i]->asString(); + } + if (links.size() == 0 ) { + out << "None"; + } + + return out.str(); + } + + bool HasLinks() const { + //cout << endl << "HasLinks()"; + return true; + } + + list<blk_ref> asLinkList() const { + //cout << endl << "asLinkList()"; + list<blk_ref> out; + + //cout << endl << "Link List size: " << uint(links.size()); + for ( uint i = 0; i < links.size(); ++i ) { + //cout << endl << "Link: " << links[i]->asLink(); + out.push_back( links[i]->asLink() ); + } + + //cout << endl << "Out List Size: " << uint(out.size()); + + return out; + } + + void AddLink( blk_ref const & block ) { + //cout << endl << "AddLink()"; + CrossRefAttr * new_attr = new CrossRefAttr( "", _owner, 0, 0xFFFFFFFF ); + new_attr->Set( block ); + links.push_back( new_attr ); + } + + void AddLinks( list<blk_ref> const & new_links ) { + //cout << endl << "AddLinks()"; + //Add new list of links + list<blk_ref>::const_iterator it; + for (it = new_links.begin(); it != new_links.end(); ++it ) { + AddLink( *it ); + } + } + + blk_ref FindLink( string const & block_type ) const { + //cout << endl << "FindLink()"; + //Find the first link with the requested block type + for ( uint i = 0; i < links.size(); ++i ) { + blk_ref found_block = links[i]->asLink(); + if ( found_block->GetBlockType() == block_type ) { + return found_block; + } + } + + //No block was found, so return a null one + return blk_ref(-1); + } + + void ClearLinks() { + //cout << endl << "ClearLinks()"; + + vector<CrossRefAttr*>::iterator it; + for ( it = links.begin(); it != links.end(); ++it ) { + delete *it; + } + links.clear(); + } + + void RemoveLinks( blk_ref const & block ) { + cout << endl << "RemoveLinks()"; + //Remove all links that match this block + vector<CrossRefAttr*>::iterator it; + for ( it = links.begin(); it != links.end(); ++it ) { + if ( (*it)->asLink() == block ) { + delete *it; + links.erase( it ); + } + } + } + void RemoveCrossLinks( IBlock * block_to_remove ) { + //cout << endl << "RemoveCrossLinks()"; + vector<CrossRefAttr*>::iterator it; + for ( it = links.begin(); it != links.end(); ++it ) { + (*it)->RemoveCrossLinks( block_to_remove ); + if ( (*it)->asLink().is_null() == true ) { + delete *it; + links.erase( it ); + } + } + } +private: + vector<CrossRefAttr*> links; }; class ModifierGroupAttr : public LinkGroupAttr { @@ -1244,6 +1477,56 @@ public: if ( par->IsControllable() == true ) return par; + //We didn't find a controllable object this time, set block to par and try again + block = par; + } + } + string asString() const { + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + out << FindTarget(); + + return out.str(); + } + blk_ref asLink() const { return FindTarget(); } + void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically. You cannot change it directly."); } + +}; + +class EmitterObjectAttr : public AAttr { +public: + EmitterObjectAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {} + ~EmitterObjectAttr() {} + AttrType GetType() const { return attr_emitterobject; } + void ReadAttr( istream& in, unsigned int version ) { + ReadUInt(in); + } + void WriteAttr( ostream& out, unsigned int version ) const { + //WriteUInt( FindTarget()->GetBlockNum(), out ); + WriteUInt( FindTarget().get_index(), out ); // we need get_index(), GetBlockNum() chokes on null block references + } + blk_ref FindTarget() const { + //Find first ancestor that is a node + blk_ref block(_owner); + blk_ref par; + while ( true ) { + //Get parent + par = block->GetParent(); + + //If parent is null, we're done - there are no node ancestors so return a null reference + if (par.is_null() == true) + return blk_ref(-1); + + // If parent is NiSequenceStreamHelper, return null reference (this is necessary to create consistent XKf files) + if ( par->GetBlockType() == "NiSequenceStreamHelper" ) + return blk_ref(-1); + + //If parent is a node, return it + if ( QueryNode( par ) != NULL ) + return par; + //We didn't find a node this time, set block to par and try again block = par; } @@ -1262,6 +1545,32 @@ public: }; +class SelfLinkAttr : public AAttr { +public: + SelfLinkAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {} + ~SelfLinkAttr() {} + AttrType GetType() const { return attr_emitterobject; } + void ReadAttr( istream& in, unsigned int version ) { + ReadUInt(in); + } + void WriteAttr( ostream& out, unsigned int version ) const { + //WriteUInt( FindTarget()->GetBlockNum(), out ); + WriteUInt( _owner->GetBlockNum(), out ); + } + string asString() const { + stringstream out; + out.setf(ios::fixed, ios::floatfield); + out << setprecision(1); + + out << blk_ref(_owner) << endl; + + return out.str(); + } + blk_ref asLink() const { return blk_ref(_owner); } + void Set(blk_ref const &) { throw runtime_error("The attribute you tried to set is calculated automatically. You cannot change it directly."); } + +}; + class SkeletonRootAttr : public AAttr { public: SkeletonRootAttr( string const & name, IBlock * owner, unsigned int first_ver, unsigned int last_ver ) : AAttr(name, owner, first_ver, last_ver) {} diff --git a/niflib.h b/niflib.h index 95e202a86d20964d7b576c8c79c25c6cdb020ec1..4ca29c70cd309ebc5ebbdcdc60e1ca3b993f89a9 100644 --- a/niflib.h +++ b/niflib.h @@ -112,7 +112,8 @@ enum AttrType { attr_float3, /*!< Float3 Attribute. Holds a Float3 structure. */ attr_float4, /*!< Float4 Attribute. Holds a Float4 structure. */ attr_string, /*!< String Attribute. Holds a text string. */ - attr_link, /*!< Link Attribute. Links to one other Nif block lower in the Nif tree. */ + attr_link, /*!< Link Attribute. Links to one other Nif block lower in the Nif tree. */ + attr_crossref, /*!< Cross Reference Attribute. Links to one other Nif block higher in the Nif tree. Will be automatically cleared if the block it links to is destroyed. */ attr_flags, /*!< Flags Attribute. Holds a set of 16 bit flags whos function depends on the block that uses them. */ attr_matrix33, /*!< Matrix33 Attribute. Holds a Matrix33 structure. */ attr_linkgroup, /*!< Link Group Attribute. Links to several other Nif blocks lower in the Nif tree. */ @@ -129,6 +130,8 @@ enum AttrType { attr_mipmapformat, /*!< Mipmap Format Attribute. Holds an integer that corresponds to the mipmap format. */ attr_modifiergroup, /*!< A link group conditioned on a boolean value. */ attr_alphaformat, /*!< Alpha Format Attribute. Holds an integer that corresponds to the alpha format. */ + attr_selflink, /*!< Self Link Attribute. Automatic. */ + attr_emitterobject, /*!< Emitter Object Attribute. Automatic. */ attr_controllertarget, /*!< Controller Target Attribute. Automatic. */ attr_skeletonroot, /*!< Skeleton Root Attribute. Automatic. */ attr_particlegroup, /*!< Particle Group Attribute. */