From 9207148324d0ae385131e6d0feea0a77fd55c97f Mon Sep 17 00:00:00 2001
From: Amorilia <amorilia@users.sourceforge.net>
Date: Fri, 17 Mar 2006 20:35:38 +0000
Subject: [PATCH] Added interface for KFM files (first try, untested).

---
 niflib.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 niflib.h   | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 200 insertions(+)

diff --git a/niflib.cpp b/niflib.cpp
index 31354538..f32beac7 100644
--- a/niflib.cpp
+++ b/niflib.cpp
@@ -1006,3 +1006,103 @@ IPosData * QueryPosData ( blk_ref & block ) {
 IPosData const * QueryPosData ( blk_ref const & block ) {
 	return (IPosData const *)block->QueryInterface( ID_POS_DATA );
 }
+
+//--Kfm Functions--//
+
+void KfmEventString::Read( istream & in, unsigned int version ) {
+	unk_int = ReadUInt(in);
+	event = ReadString(in);
+};
+
+void KfmEventString::Write( ostream & out, unsigned int version ) {
+	WriteUInt(unk_int, out);
+	WriteString(event, out);
+};
+
+void KfmEvent::Read( istream & in, uint version ) {
+	id = ReadUInt(in);
+	type = ReadUInt(in);
+	if ( type != 5 ) {
+		unk_float = ReadFloat(in);
+		event_strings.resize(ReadUInt(in));
+		for ( vector<KfmEventString>::iterator it = event_strings.begin(); it != event_strings.end(); it++ ) it->Read(in, version);
+		unk_int3 = ReadUInt(in);
+	};
+};
+
+void KfmAction::Read( istream & in, uint version ) {
+	if ( version <= VER_KFM_1_2_4b ) action_name = ReadString(in);
+	action_filename = ReadString(in);
+	unk_int1 = ReadUInt(in);
+	events.resize(ReadUInt(in));
+	for ( vector<KfmEvent>::iterator it = events.begin(); it != events.end(); it++ ) it->Read(in, version);
+	unk_int2 = ReadUInt(in);
+};
+
+unsigned int Kfm::Read( string const & file_name ) {
+	ifstream in( file_name.c_str(), ifstream::binary );
+	unsigned int version = Read(in);
+	if ( in.eof() )
+		throw runtime_error("End of file reached prematurely. This KFM may be corrupt or improperly supported.");
+	ReadByte( in ); // this should fail, and trigger the in.eof() flag
+	if ( ! in.eof() )
+		throw runtime_error("End of file not reached. This KFM may be corrupt or improperly supported.");
+	return version;
+};
+
+unsigned int Kfm::Read( istream & in ) {
+	//--Read Header--//
+	char header_string[64];
+	in.getline( header_string, 64 );
+	string headerstr(header_string);
+
+	// make sure this is a KFM file
+	if ( headerstr.substr(0, 26) != ";Gamebryo KFM File Version" ) {
+		version = VER_INVALID;
+		return version;
+	};
+
+	// supported versions
+	if ( headerstr == ";Gamebryo KFM File Version 2.0.0.0b" ) version = VER_KFM_2_0_0_0b;
+	else if ( headerstr == ";Gamebryo KFM File Version 1.2.4b" ) version = VER_KFM_1_2_4b;
+	//else if ( headerstr == ";Gamebryo KFM File Version 1.0" ) version = VER_KFM_1_0;
+	//else if ( headerstr == ";Gamebryo KFM File Version 1.0\r" ) version = VER_KFM_1_0; // Windows eol style
+	else {
+		version = VER_UNSUPPORTED;
+		return version;
+	};
+	
+	//--Read remainder--//
+	if (version == VER_KFM_1_0) {
+		// TODO write a parser
+	} else {
+		if (version >= VER_KFM_2_0_0_0b) unk_byte = ReadByte(in);
+		else unk_byte = 1;
+		nif_file_name = ReadString(in);
+		master = ReadString(in);
+		unk_int1 = ReadUInt(in);
+		unk_int2 = ReadUInt(in);
+		unk_float1 = ReadFloat(in);
+		unk_float2 = ReadFloat(in);
+		actions.resize(ReadUInt(in));
+		unk_int3 = ReadUInt(in);
+		for ( vector<KfmAction>::iterator it = actions.begin(); it != actions.end(); it++ ) it->Read(in, version);
+	};
+	return version;
+};
+
+/*
+void Kfm::Write( ostream & out, uint version ) {
+	if ( version == VER_KFM_1_0 ) {
+		// handle this case seperately
+		out << ";Gamebryo KFM File Version 1.0" << endl;
+		// TODO write the rest of the data
+	} else {
+		if ( version == VER_KFM_1_2_4b )
+			out.write(";Gamebryo KFM File Version 1.2.4b\n", 34);
+		else if ( version == VER_KFM_2_0_0_0b )
+			out.write(";Gamebryo KFM File Version 2.0.0.0b\n", 37);
+		else throw runtime_error("Cannot write KFM file of this version.");
+	};
+};
+*/
\ No newline at end of file
diff --git a/niflib.h b/niflib.h
index b8cbd2be..ab006845 100644
--- a/niflib.h
+++ b/niflib.h
@@ -231,11 +231,25 @@ enum PixelFormat {
  * <b>Example:</b> 
  * \code
  * unsigned int ver = CheckNifHeader("test_in.nif");
+ * if ( ver == VER_UNSUPPORTED ) cout << "unsupported" << endl;
+ * else if ( ver == VER_INVALID ) cout << "invalid" << endl;
+ * else {
+ *   vector<blk_ref> blocks = ReadNifList( "test_in.nif" );
+ *   cout << blocks[0] << endl;
+ * };
+ *
  * \endcode
  * 
  * <b>In Python:</b>
  * \code
  * ver = CheckNifHeader("test_in.nif")
+ * if ( ver == VER_UNSUPPORTED ):
+ *     print "unsupported"
+ * elif ( ver == VER_INVALID ):
+ *     print "invalid"
+ * else:
+ *      blocks = ReadNifList( "test_in.nif" )
+ *      print blocks[0]
  * \endcode
  */
 unsigned int CheckNifHeader( string const & file_name );
@@ -3331,6 +3345,92 @@ struct TexDesc {
 	float unknownFloat2; /*!< An unknown floating point value that exists from version 10.1.0.0 on. */ 
 };
 
+//--KFM File Format--//
+
+//KFM Versions
+const unsigned int VER_KFM_1_0 = 0x01000000; /*!< Kfm Version 1.0 */ 
+const unsigned int VER_KFM_1_2_4b = 0x01020400; /*!< Kfm Version 1.2.4b */ 
+const unsigned int VER_KFM_2_0_0_0b = 0x02000000; /*!< Kfm Version 2.0.0.0b */ 
+
+//KFM Data Structure
+
+struct KfmEventString {
+	unsigned int unk_int;
+	string event;
+
+	KfmEventString() : unk_int(0), event() {};
+	void Read( istream & in, unsigned int version );
+	void Write( ostream & out, unsigned int version );
+};
+
+struct KfmEvent {
+	unsigned int id;
+	unsigned int type;
+	float unk_float;
+	vector<KfmEventString> event_strings;
+	unsigned int unk_int3;
+	
+	KfmEvent() : id(0), type(0), unk_float(0.5f), event_strings(), unk_int3(0) {};
+	void Read( istream & in, unsigned int version );
+	//void Write( ostream & out, unsigned int version );
+};
+
+struct KfmAction {
+	string action_name;
+	string action_filename;
+	unsigned int unk_int1;
+	vector<KfmEvent> events;
+	unsigned int unk_int2;
+
+	void Read( istream & in, unsigned int version );
+	//void Write( ostream & out, unsigned int version );
+};
+
+struct Kfm {
+	unsigned int version;
+	unsigned char unk_byte;
+	string nif_file_name;
+	string master;
+	unsigned int unk_int1;
+	unsigned int unk_int2;
+	float unk_float1;
+	float unk_float2;
+	unsigned int unk_int3;
+	vector<KfmAction> actions;
+	
+	/*!
+	 * Reads the given file and returns the KFM version.
+	 * \param file_name The input file name.
+	 * \return The KFM version of the file, in hexadecimal format. If the file is not a KFM file, it returns VER_INVALID. If it is a KFM file, but its version is not supported by the library, it returns VER_UNSUPPORTED.
+	 * 
+	 * <b>Example:</b> 
+	 * \code
+	 * Kfm kfm;
+	 * unsigned int ver = kfm.Read( "test_in.kfm" );
+	 * if ( ver == VER_UNSUPPORTED ) cout << "unsupported" << endl;
+	 * else if ( ver == VER_INVALID ) cout << "invalid" << endl;
+	 * else cout << "Describes keyframes for NIF file " << kfm.nif_file_name << "." << endl;
+	 *
+	 * \endcode
+	 * 
+	 * <b>In Python:</b>
+	 * \code
+	 * kfm = Kfm()
+	 * ver = kfm.Read( "test_in.kfm" )
+	 * if ( ver == VER_UNSUPPORTED ):
+	 *     print "unsupported"
+	 * elif ( ver == VER_INVALID ):
+	 *     print "invalid"
+	 * else:
+	 *      print "Describes keyframes for NIF file %s."%kfm.nif_file_name
+	 * \endcode
+	 */
+	unsigned int Read( string const & file_name ); // returns Kfm version
+	unsigned int Read( istream & in ); // returns Kfm version
+	//void Write( string const & file_name, unsigned int version );
+	//void Write( ostream & out, unsigned int version );
+};
+
 //--USER GUIDE DOCUMENTATION--//
 
 /*! \mainpage Niflib Documentation
-- 
GitLab