Просмотр исходного кода

Merge branch 'master' into explicitly-default-dtors

Kim Kulling 2 лет назад
Родитель
Сommit
f527e56f22

+ 591 - 548
code/AssetLib/MMD/MMDPmdParser.h

@@ -4,7 +4,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -48,550 +47,594 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <fstream>
 #include "MMDCpp14.h"
 
-namespace pmd
-{
-	class PmdHeader
-	{
-	public:
-		std::string name;
-		std::string name_english;
-		std::string comment;
-		std::string comment_english;
-
-		bool Read(std::ifstream* stream)
-		{
-			char buffer[256];
-			stream->read(buffer, 20);
-			name = std::string(buffer);
-			stream->read(buffer, 256);
-			comment = std::string(buffer);
-			return true;
-		}
-
-		bool ReadExtension(std::ifstream* stream)
-		{
-			char buffer[256];
-			stream->read(buffer, 20);
-			name_english = std::string(buffer);
-			stream->read(buffer, 256);
-			comment_english = std::string(buffer);
-			return true;
-		}
-	};
-
-	class PmdVertex
-	{
-	public:
-		float position[3];
-
-		float normal[3];
-
-		float uv[2];
-
-		uint16_t bone_index[2];
-
-		uint8_t bone_weight;
-
-		bool edge_invisible;
-
-		bool Read(std::ifstream* stream)
-		{
-			stream->read((char*) position, sizeof(float) * 3);
-			stream->read((char*) normal, sizeof(float) * 3);
-			stream->read((char*) uv, sizeof(float) * 2);
-			stream->read((char*) bone_index, sizeof(uint16_t) * 2);
-			stream->read((char*) &bone_weight, sizeof(uint8_t));
-			stream->read((char*) &edge_invisible, sizeof(uint8_t));
-			return true;
-		}
-	};
-
-	class PmdMaterial
-	{
-	public:
-		float diffuse[4];
-		float power;
-		float specular[3];
-		float ambient[3];
-		uint8_t toon_index;
-		uint8_t edge_flag;
-		uint32_t index_count;
-		std::string texture_filename;
-		std::string sphere_filename;
-
-		bool Read(std::ifstream* stream)
-		{
-			char buffer[20];
-			stream->read((char*) &diffuse, sizeof(float) * 4);
-			stream->read((char*) &power, sizeof(float));
-			stream->read((char*) &specular, sizeof(float) * 3);
-			stream->read((char*) &ambient, sizeof(float) * 3);
-			stream->read((char*) &toon_index, sizeof(uint8_t));
-			stream->read((char*) &edge_flag, sizeof(uint8_t));
-			stream->read((char*) &index_count, sizeof(uint32_t));
-			stream->read((char*) &buffer, sizeof(char) * 20);
-			char* pstar = strchr(buffer, '*');
-            if (nullptr == pstar)
-			{
-				texture_filename = std::string(buffer);
-				sphere_filename.clear();
-			}
-			else {
-				*pstar = 0;
-				texture_filename = std::string(buffer);
-				sphere_filename = std::string(pstar+1);
-			}
-			return true;
-		}
-	};
-
-	enum class BoneType : uint8_t
-	{
-		Rotation,
-		RotationAndMove,
-		IkEffector,
-		Unknown,
-		IkEffectable,
-		RotationEffectable,
-		IkTarget,
-		Invisible,
-		Twist,
-		RotationMovement
-	};
-
-	class PmdBone
-	{
-	public:
-		std::string name;
-		std::string name_english;
-		uint16_t parent_bone_index;
-		uint16_t tail_pos_bone_index;
-		BoneType bone_type;
-		uint16_t ik_parent_bone_index;
-		float bone_head_pos[3];
-
-		void Read(std::istream *stream)
-		{
-			char buffer[20];
-			stream->read(buffer, 20);
-			name = std::string(buffer);
-			stream->read((char*) &parent_bone_index, sizeof(uint16_t));
-			stream->read((char*) &tail_pos_bone_index, sizeof(uint16_t));
-			stream->read((char*) &bone_type, sizeof(uint8_t));
-			stream->read((char*) &ik_parent_bone_index, sizeof(uint16_t));
-			stream->read((char*) &bone_head_pos, sizeof(float) * 3);
-		}
-
-		void ReadExpantion(std::istream *stream)
-		{
-			char buffer[20];
-			stream->read(buffer, 20);
-			name_english = std::string(buffer);
-		}
-	};
-
-	class PmdIk
-	{
-	public:
-		uint16_t ik_bone_index;
-		uint16_t target_bone_index;
-		uint16_t iterations;
-		float angle_limit;
-		std::vector<uint16_t> ik_child_bone_index;
-
-		void Read(std::istream *stream)
-		{
-			stream->read((char *) &ik_bone_index, sizeof(uint16_t));
-			stream->read((char *) &target_bone_index, sizeof(uint16_t));
-			uint8_t ik_chain_length;
-			stream->read((char*) &ik_chain_length, sizeof(uint8_t));
-			stream->read((char *) &iterations, sizeof(uint16_t));
-			stream->read((char *) &angle_limit, sizeof(float));
-			ik_child_bone_index.resize(ik_chain_length);
-			for (int i = 0; i < ik_chain_length; i++)
-			{
-				stream->read((char *) &ik_child_bone_index[i], sizeof(uint16_t));
-			}
-		}
-	};
-
-	class PmdFaceVertex
-	{
-	public:
-		int vertex_index;
-		float position[3];
-
-		void Read(std::istream *stream)
-		{
-			stream->read((char *) &vertex_index, sizeof(int));
-			stream->read((char *) position, sizeof(float) * 3);
-		}
-	};
-
-	enum class FaceCategory : uint8_t
-	{
-		Base,
-		Eyebrow,
-		Eye,
-		Mouth,
-		Other
-	};
-
-	class PmdFace
-	{
-	public:
-		std::string name;
-		FaceCategory type;
-		std::vector<PmdFaceVertex> vertices;
-		std::string name_english;
-
-		void Read(std::istream *stream)
-		{
-			char buffer[20];
-			stream->read(buffer, 20);
-			name = std::string(buffer);
-			int vertex_count;
-			stream->read((char*) &vertex_count, sizeof(int));
-			stream->read((char*) &type, sizeof(uint8_t));
-			vertices.resize(vertex_count);
-			for (int i = 0; i < vertex_count; i++)
-			{
-				vertices[i].Read(stream);
-			}
-		}
-
-		void ReadExpantion(std::istream *stream)
-		{
-			char buffer[20];
-			stream->read(buffer, 20);
-			name_english = std::string(buffer);
-		}
-	};
-
-	class PmdBoneDispName
-	{
-	public:
-		std::string bone_disp_name;
-		std::string bone_disp_name_english;
-
-		void Read(std::istream *stream)
-		{
-			char buffer[50];
-			stream->read(buffer, 50);
-			bone_disp_name = std::string(buffer);
-			bone_disp_name_english.clear();
-		}
-		void ReadExpantion(std::istream *stream)
-		{
-			char buffer[50];
-			stream->read(buffer, 50);
-			bone_disp_name_english = std::string(buffer);
-		}
-	};
-
-	class PmdBoneDisp
-	{
-	public:
-		uint16_t bone_index;
-		uint8_t bone_disp_index;
-
-		void Read(std::istream *stream)
-		{
-			stream->read((char*) &bone_index, sizeof(uint16_t));
-			stream->read((char*) &bone_disp_index, sizeof(uint8_t));
-		}
-	};
-
-	enum class RigidBodyShape : uint8_t
-	{
-		Sphere = 0,
-		Box = 1,
-		Cpusel = 2
-	};
-
-	enum class RigidBodyType : uint8_t
-	{
-		BoneConnected = 0,
-		Physics = 1,
-		ConnectedPhysics = 2
-	};
-
-	class PmdRigidBody
-	{
-	public:
-		std::string name;
-		uint16_t related_bone_index;
-		uint8_t group_index;
-		uint16_t mask;
-		RigidBodyShape shape;
-		float size[3];
-		float position[3];
-		float orientation[3];
-		float weight;
-		float linear_damping;
-		float anglar_damping;
-		float restitution;
-		float friction;
-		RigidBodyType rigid_type;
-
-		void Read(std::istream *stream)
-		{
-			char buffer[20];
-			stream->read(buffer, sizeof(char) * 20);
-			name = (std::string(buffer));
-			stream->read((char*) &related_bone_index, sizeof(uint16_t));
-			stream->read((char*) &group_index, sizeof(uint8_t));
-			stream->read((char*) &mask, sizeof(uint16_t));
-			stream->read((char*) &shape, sizeof(uint8_t));
-			stream->read((char*) size, sizeof(float) * 3);
-			stream->read((char*) position, sizeof(float) * 3);
-			stream->read((char*) orientation, sizeof(float) * 3);
-			stream->read((char*) &weight, sizeof(float));
-			stream->read((char*) &linear_damping, sizeof(float));
-			stream->read((char*) &anglar_damping, sizeof(float));
-			stream->read((char*) &restitution, sizeof(float));
-			stream->read((char*) &friction, sizeof(float));
-			stream->read((char*) &rigid_type, sizeof(char));
-		}
-	};
-
-	class PmdConstraint
-	{
-	public:
-		std::string name;
-		uint32_t rigid_body_index_a;
-		uint32_t rigid_body_index_b;
-		float position[3];
-		float orientation[3];
-		float linear_lower_limit[3];
-		float linear_upper_limit[3];
-		float angular_lower_limit[3];
-		float angular_upper_limit[3];
-		float linear_stiffness[3];
-		float angular_stiffness[3];
-
-		void Read(std::istream *stream)
-		{
-			char buffer[20];
-			stream->read(buffer, 20);
-			name = std::string(buffer);
-			stream->read((char *) &rigid_body_index_a, sizeof(uint32_t));
-			stream->read((char *) &rigid_body_index_b, sizeof(uint32_t));
-			stream->read((char *) position, sizeof(float) * 3);
-			stream->read((char *) orientation, sizeof(float) * 3);
-			stream->read((char *) linear_lower_limit, sizeof(float) * 3);
-			stream->read((char *) linear_upper_limit, sizeof(float) * 3);
-			stream->read((char *) angular_lower_limit, sizeof(float) * 3);
-			stream->read((char *) angular_upper_limit, sizeof(float) * 3);
-			stream->read((char *) linear_stiffness, sizeof(float) * 3);
-			stream->read((char *) angular_stiffness, sizeof(float) * 3);
-		}
-	};
-
-	class PmdModel
-	{
-	public:
-		float version;
-		PmdHeader header;
-		std::vector<PmdVertex> vertices;
-		std::vector<uint16_t> indices;
-		std::vector<PmdMaterial> materials;
-		std::vector<PmdBone> bones;
-		std::vector<PmdIk> iks;
-		std::vector<PmdFace> faces;
-		std::vector<uint16_t> faces_indices;
-		std::vector<PmdBoneDispName> bone_disp_name;
-		std::vector<PmdBoneDisp> bone_disp;
-		std::vector<std::string> toon_filenames;
-		std::vector<PmdRigidBody> rigid_bodies;
-		std::vector<PmdConstraint> constraints;
-
-		static std::unique_ptr<PmdModel> LoadFromFile(const char *filename)
-		{
-			std::ifstream stream(filename, std::ios::binary);
-			if (stream.fail())
-			{
-				std::cerr << "could not open \"" << filename << "\"" << std::endl;
-				return nullptr;
-			}
-			auto result = LoadFromStream(&stream);
-			stream.close();
-			return result;
-		}
-
-		static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream)
-		{
-			auto result = mmd::make_unique<PmdModel>();
-			char buffer[100];
-
-			// magic
-			char magic[3];
-			stream->read(magic, 3);
-			if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd')
-			{
-				std::cerr << "invalid file" << std::endl;
-				return nullptr;
-			}
-
-			// version
-			stream->read((char*) &(result->version), sizeof(float));
-			if (result ->version != 1.0f)
-			{
-				std::cerr << "invalid version" << std::endl;
-				return nullptr;
-			}
-
-			// header
-			result->header.Read(stream);
-
-			// vertices
-			uint32_t vertex_num;
-			stream->read((char*) &vertex_num, sizeof(uint32_t));
-			result->vertices.resize(vertex_num);
-			for (uint32_t i = 0; i < vertex_num; i++)
-			{
-				result->vertices[i].Read(stream);
-			}
-
-			// indices
-			uint32_t index_num;
-			stream->read((char*) &index_num, sizeof(uint32_t));
-			result->indices.resize(index_num);
-			for (uint32_t i = 0; i < index_num; i++)
-			{
-				stream->read((char*) &result->indices[i], sizeof(uint16_t));
-			}
-
-			// materials
-			uint32_t material_num;
-			stream->read((char*) &material_num, sizeof(uint32_t));
-			result->materials.resize(material_num);
-			for (uint32_t i = 0; i < material_num; i++)
-			{
-				result->materials[i].Read(stream);
-			}
-
-			// bones
-			uint16_t bone_num;
-			stream->read((char*) &bone_num, sizeof(uint16_t));
-			result->bones.resize(bone_num);
-			for (uint32_t i = 0; i < bone_num; i++)
-			{
-				result->bones[i].Read(stream);
-			}
-
-			// iks
-			uint16_t ik_num;
-			stream->read((char*) &ik_num, sizeof(uint16_t));
-			result->iks.resize(ik_num);
-			for (uint32_t i = 0; i < ik_num; i++)
-			{
-				result->iks[i].Read(stream);
-			}
-
-			// faces
-			uint16_t face_num;
-			stream->read((char*) &face_num, sizeof(uint16_t));
-			result->faces.resize(face_num);
-			for (uint32_t i = 0; i < face_num; i++)
-			{
-				result->faces[i].Read(stream);
-			}
-
-			// face frames
-			uint8_t face_frame_num;
-			stream->read((char*) &face_frame_num, sizeof(uint8_t));
-			result->faces_indices.resize(face_frame_num);
-			for (uint32_t i = 0; i < face_frame_num; i++)
-			{
-				stream->read((char*) &result->faces_indices[i], sizeof(uint16_t));
-			}
-
-			// bone names
-			uint8_t bone_disp_num;
-			stream->read((char*) &bone_disp_num, sizeof(uint8_t));
-			result->bone_disp_name.resize(bone_disp_num);
-			for (uint32_t i = 0; i < bone_disp_num; i++)
-			{
-				result->bone_disp_name[i].Read(stream);
-			}
-
-			// bone frame
-			uint32_t bone_frame_num;
-			stream->read((char*) &bone_frame_num, sizeof(uint32_t));
-			result->bone_disp.resize(bone_frame_num);
-			for (uint32_t i = 0; i < bone_frame_num; i++)
-			{
-				result->bone_disp[i].Read(stream);
-			}
-
-			// english name
-			bool english;
-			stream->read((char*) &english, sizeof(char));
-			if (english)
-			{
-				result->header.ReadExtension(stream);
-				for (uint32_t i = 0; i < bone_num; i++)
-				{
-					result->bones[i].ReadExpantion(stream);
-				}
-				for (uint32_t i = 0; i < face_num; i++)
-				{
-					if (result->faces[i].type == pmd::FaceCategory::Base)
-					{
-						continue;
-					}
-					result->faces[i].ReadExpantion(stream);
-				}
-				for (uint32_t i = 0; i < result->bone_disp_name.size(); i++)
-				{
-					result->bone_disp_name[i].ReadExpantion(stream);
-				}
-			}
-
-			// toon textures
-			if (stream->peek() == std::ios::traits_type::eof())
-			{
-				result->toon_filenames.clear();
-			}
-			else {
-				result->toon_filenames.resize(10);
-				for (uint32_t i = 0; i < 10; i++)
-				{
-					stream->read(buffer, 100);
-					result->toon_filenames[i] = std::string(buffer);
-				}
-			}
-
-			// physics
-			if (stream->peek() == std::ios::traits_type::eof())
-			{
-				result->rigid_bodies.clear();
-				result->constraints.clear();
-			}
-			else {
-				uint32_t rigid_body_num;
-				stream->read((char*) &rigid_body_num, sizeof(uint32_t));
-				result->rigid_bodies.resize(rigid_body_num);
-				for (uint32_t i = 0; i < rigid_body_num; i++)
-				{
-					result->rigid_bodies[i].Read(stream);
-				}
-				uint32_t constraint_num;
-				stream->read((char*) &constraint_num, sizeof(uint32_t));
-				result->constraints.resize(constraint_num);
-				for (uint32_t i = 0; i < constraint_num; i++)
-				{
-					result->constraints[i].Read(stream);
-				}
-			}
-
-			if (stream->peek() != std::ios::traits_type::eof())
-			{
-				std::cerr << "there is unknown data" << std::endl;
-			}
-
-			return result;
-		}
-	};
-}
+namespace pmd {
+
+struct PmdHeader {
+    std::string name;
+    std::string name_english;
+    std::string comment;
+    std::string comment_english;
+
+    PmdHeader() = default;
+    ~PmdHeader() = default;
+
+    bool Read(std::ifstream *stream) {
+        if (stream == nullptr) {
+            return false;
+        }
+        char buffer[256] = {};
+        stream->read(buffer, 20);
+        name = std::string(buffer);
+        stream->read(buffer, 256);
+        comment = std::string(buffer);
+        return true;
+    }
+
+    bool ReadExtension(std::ifstream *stream) {
+        if (stream == nullptr) {
+            return false;
+        }
+
+        char buffer[256] = {};
+        stream->read(buffer, 20);
+        name_english = std::string(buffer);
+        stream->read(buffer, 256);
+        comment_english = std::string(buffer);
+
+        return true;
+    }
+};
+
+struct PmdVertex {
+    float position[3];
+    float normal[3];
+    float uv[2];
+    uint16_t bone_index[2];
+    uint8_t bone_weight;
+    bool edge_invisible;
+
+    PmdVertex() :
+            position{ 0.0f }, normal{ 0.0f }, uv{ 0.0f }, bone_index{ 0 }, bone_weight(0), edge_invisible(false) {}
+
+    ~PmdVertex() = default;
+
+    bool Read(std::ifstream *stream) {
+        if (stream == nullptr) {
+            return false;
+        }
+
+        stream->read((char *)position, sizeof(float) * 3);
+        stream->read((char *)normal, sizeof(float) * 3);
+        stream->read((char *)uv, sizeof(float) * 2);
+        stream->read((char *)bone_index, sizeof(uint16_t) * 2);
+        stream->read((char *)&bone_weight, sizeof(uint8_t));
+        stream->read((char *)&edge_invisible, sizeof(uint8_t));
+        return true;
+    }
+};
+
+struct PmdMaterial {
+    float diffuse[4];
+    float power;
+    float specular[3];
+    float ambient[3];
+    uint8_t toon_index;
+    uint8_t edge_flag;
+    uint32_t index_count;
+    std::string texture_filename;
+    std::string sphere_filename;
+
+    PmdMaterial() :
+            diffuse{ 0.0f }, power(0.0f), specular{ 0.0f }, ambient{ 0.0f }, toon_index(0), edge_flag(0), index_count(0), texture_filename(), sphere_filename() {}
+
+    ~PmdMaterial() = default;
+
+    bool Read(std::ifstream *stream) {
+        if (stream == nullptr) {
+            return false;
+        }
+        constexpr size_t BufferSize = 20;
+
+        char buffer[BufferSize] = {};
+        stream->read((char *)&diffuse, sizeof(float) * 4);
+        stream->read((char *)&power, sizeof(float));
+        stream->read((char *)&specular, sizeof(float) * 3);
+        stream->read((char *)&ambient, sizeof(float) * 3);
+        stream->read((char *)&toon_index, sizeof(uint8_t));
+        stream->read((char *)&edge_flag, sizeof(uint8_t));
+        stream->read((char *)&index_count, sizeof(uint32_t));
+        stream->read((char *)&buffer, sizeof(char) * BufferSize);
+        char *pstar = strchr(buffer, '*');
+        if (nullptr == pstar) {
+            texture_filename = std::string(buffer);
+            sphere_filename.clear();
+        } else {
+            *pstar = 0;
+            texture_filename = std::string(buffer);
+            sphere_filename = std::string(pstar + 1);
+        }
+
+        return true;
+    }
+};
+
+enum class BoneType : uint8_t {
+    Rotation,
+    RotationAndMove,
+    IkEffector,
+    Unknown,
+    IkEffectable,
+    RotationEffectable,
+    IkTarget,
+    Invisible,
+    Twist,
+    RotationMovement
+
+};
+
+struct PmdBone {
+    std::string name;
+    std::string name_english;
+    uint16_t parent_bone_index;
+    uint16_t tail_pos_bone_index;
+    BoneType bone_type;
+    uint16_t ik_parent_bone_index;
+    float bone_head_pos[3];
+
+    PmdBone() :
+            name(), name_english(), parent_bone_index(0), tail_pos_bone_index(0), bone_type(BoneType::Unknown), ik_parent_bone_index(0), bone_head_pos{ 0.0f } {}
+
+    ~PmdBone() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+        constexpr size_t BufferSize = 20;
+        char buffer[BufferSize] = {};
+        stream->read(buffer, BufferSize);
+        name = std::string(buffer);
+        stream->read((char *)&parent_bone_index, sizeof(uint16_t));
+        stream->read((char *)&tail_pos_bone_index, sizeof(uint16_t));
+        stream->read((char *)&bone_type, sizeof(uint8_t));
+        stream->read((char *)&ik_parent_bone_index, sizeof(uint16_t));
+        stream->read((char *)&bone_head_pos, sizeof(float) * 3);
+    }
+
+    void ReadExpantion(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+        constexpr size_t BufferSize = 20;
+        char buffer[BufferSize] = {};
+        stream->read(buffer, BufferSize);
+        name_english = std::string(buffer);
+    }
+};
+
+struct PmdIk {
+    uint16_t ik_bone_index;
+    uint16_t target_bone_index;
+    uint16_t iterations;
+    float angle_limit;
+    std::vector<uint16_t> ik_child_bone_index;
+
+    PmdIk() : ik_bone_index(0), target_bone_index(0), iterations(0), angle_limit(0.0f) {}
+
+    ~PmdIk() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+
+        stream->read((char *)&ik_bone_index, sizeof(uint16_t));
+        stream->read((char *)&target_bone_index, sizeof(uint16_t));
+        uint8_t ik_chain_length;
+        stream->read((char *)&ik_chain_length, sizeof(uint8_t));
+        stream->read((char *)&iterations, sizeof(uint16_t));
+        stream->read((char *)&angle_limit, sizeof(float));
+        ik_child_bone_index.resize(ik_chain_length);
+        for (int i = 0; i < ik_chain_length; i++) {
+            stream->read((char *)&ik_child_bone_index[i], sizeof(uint16_t));
+        }
+    }
+};
+
+struct PmdFaceVertex {
+    int vertex_index;
+    float position[3];
+
+    PmdFaceVertex() :
+            vertex_index(0), position{ 0.0f } {}
+
+    ~PmdFaceVertex() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+        stream->read((char *)&vertex_index, sizeof(int));
+        stream->read((char *)position, sizeof(float) * 3);
+    }
+};
+
+enum class FaceCategory : uint8_t {
+    Base,
+    Eyebrow,
+    Eye,
+    Mouth,
+    Other
+};
+
+struct PmdFace {
+    std::string name;
+    FaceCategory type;
+    std::vector<PmdFaceVertex> vertices;
+    std::string name_english;
+
+    PmdFace() :
+            name(), type(FaceCategory::Other), vertices(), name_english() {}
+
+    ~PmdFace() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+        constexpr size_t BufferSize = 20;
+        char buffer[BufferSize];
+        stream->read(buffer, BufferSize);
+        name = std::string(buffer);
+        int vertex_count;
+        stream->read((char *)&vertex_count, sizeof(int));
+        stream->read((char *)&type, sizeof(uint8_t));
+        vertices.resize(vertex_count);
+        for (int i = 0; i < vertex_count; i++) {
+            vertices[i].Read(stream);
+        }
+    }
+
+    void ReadExpantion(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+
+        char buffer[20];
+        stream->read(buffer, 20);
+        name_english = std::string(buffer);
+    }
+};
+
+struct PmdBoneDispName {
+    std::string bone_disp_name;
+    std::string bone_disp_name_english;
+
+    PmdBoneDispName() = default;
+
+    ~PmdBoneDispName() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+        char buffer[50];
+        stream->read(buffer, 50);
+        bone_disp_name = std::string(buffer);
+        bone_disp_name_english.clear();
+    }
+
+    void ReadExpantion(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+        char buffer[50];
+        stream->read(buffer, 50);
+        bone_disp_name_english = std::string(buffer);
+    }
+};
+
+struct PmdBoneDisp {
+    uint16_t bone_index;
+    uint8_t bone_disp_index;
+
+    PmdBoneDisp() :
+            bone_index(0), bone_disp_index(0) {}
+
+    ~PmdBoneDisp() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+
+        stream->read((char *)&bone_index, sizeof(uint16_t));
+        stream->read((char *)&bone_disp_index, sizeof(uint8_t));
+    }
+};
+
+enum class RigidBodyShape : uint8_t {
+    Sphere = 0,
+    Box = 1,
+    Cpusel = 2
+};
+
+enum class RigidBodyType : uint8_t {
+    BoneConnected = 0,
+    Physics = 1,
+    ConnectedPhysics = 2
+};
+
+struct PmdRigidBody {
+    std::string name;
+    uint16_t related_bone_index;
+    uint8_t group_index;
+    uint16_t mask;
+    RigidBodyShape shape;
+    float size[3];
+    float position[3];
+    float orientation[3];
+    float weight;
+    float linear_damping;
+    float anglar_damping;
+    float restitution;
+    float friction;
+    RigidBodyType rigid_type;
+
+    PmdRigidBody() :
+            name(), related_bone_index(0), group_index(0), mask(0), shape(RigidBodyShape::Box), size{ 0.0f }, position{ 0.0f }, weight(0.0f), linear_damping(0.0f), anglar_damping(0.0f), restitution(0.0f), friction(0.0f), rigid_type(RigidBodyType::BoneConnected) {}
+
+    ~PmdRigidBody() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+
+        char buffer[20];
+        stream->read(buffer, sizeof(char) * 20);
+        name = (std::string(buffer));
+        stream->read((char *)&related_bone_index, sizeof(uint16_t));
+        stream->read((char *)&group_index, sizeof(uint8_t));
+        stream->read((char *)&mask, sizeof(uint16_t));
+        stream->read((char *)&shape, sizeof(uint8_t));
+        stream->read((char *)size, sizeof(float) * 3);
+        stream->read((char *)position, sizeof(float) * 3);
+        stream->read((char *)orientation, sizeof(float) * 3);
+        stream->read((char *)&weight, sizeof(float));
+        stream->read((char *)&linear_damping, sizeof(float));
+        stream->read((char *)&anglar_damping, sizeof(float));
+        stream->read((char *)&restitution, sizeof(float));
+        stream->read((char *)&friction, sizeof(float));
+        stream->read((char *)&rigid_type, sizeof(char));
+    }
+};
+
+struct PmdConstraint {
+    std::string name;
+    uint32_t rigid_body_index_a;
+    uint32_t rigid_body_index_b;
+    float position[3];
+    float orientation[3];
+    float linear_lower_limit[3];
+    float linear_upper_limit[3];
+    float angular_lower_limit[3];
+    float angular_upper_limit[3];
+    float linear_stiffness[3];
+    float angular_stiffness[3];
+
+    PmdConstraint() :
+            name(), rigid_body_index_a(0), rigid_body_index_b(0), position{ 0.0f }, orientation{ 0.0f }, linear_lower_limit{ 0.0f }, linear_upper_limit{ 0.0f }, angular_lower_limit{ 0.0f }, angular_upper_limit{ 0.0f }, linear_stiffness{ 0.0f }, angular_stiffness{ 0.0f } {}
+
+    ~PmdConstraint() = default;
+
+    void Read(std::istream *stream) {
+        if (stream == nullptr) {
+            return;
+        }
+
+        char buffer[20];
+        stream->read(buffer, 20);
+        name = std::string(buffer);
+        stream->read((char *)&rigid_body_index_a, sizeof(uint32_t));
+        stream->read((char *)&rigid_body_index_b, sizeof(uint32_t));
+        stream->read((char *)position, sizeof(float) * 3);
+        stream->read((char *)orientation, sizeof(float) * 3);
+        stream->read((char *)linear_lower_limit, sizeof(float) * 3);
+        stream->read((char *)linear_upper_limit, sizeof(float) * 3);
+        stream->read((char *)angular_lower_limit, sizeof(float) * 3);
+        stream->read((char *)angular_upper_limit, sizeof(float) * 3);
+        stream->read((char *)linear_stiffness, sizeof(float) * 3);
+        stream->read((char *)angular_stiffness, sizeof(float) * 3);
+    }
+};
+
+struct PmdModel {
+    float version;
+    PmdHeader header;
+    std::vector<PmdVertex> vertices;
+    std::vector<uint16_t> indices;
+    std::vector<PmdMaterial> materials;
+    std::vector<PmdBone> bones;
+    std::vector<PmdIk> iks;
+    std::vector<PmdFace> faces;
+    std::vector<uint16_t> faces_indices;
+    std::vector<PmdBoneDispName> bone_disp_name;
+    std::vector<PmdBoneDisp> bone_disp;
+    std::vector<std::string> toon_filenames;
+    std::vector<PmdRigidBody> rigid_bodies;
+    std::vector<PmdConstraint> constraints;
+
+    PmdModel() :
+            version(0.0f) {}
+
+    ~PmdModel() = default;
+
+    static std::unique_ptr<PmdModel> LoadFromFile(const char *filename) {
+        if (filename == nullptr) {
+            return nullptr;
+        }
+
+        std::ifstream stream(filename, std::ios::binary);
+        if (stream.fail()) {
+            std::cerr << "could not open \"" << filename << "\"" << std::endl;
+            return nullptr;
+        }
+        auto result = LoadFromStream(&stream);
+        stream.close();
+        return result;
+    }
+
+    static std::unique_ptr<PmdModel> LoadFromStream(std::ifstream *stream) {
+        auto result = mmd::make_unique<PmdModel>();
+        char buffer[100];
+
+        // magic
+        char magic[3];
+        stream->read(magic, 3);
+        if (magic[0] != 'P' || magic[1] != 'm' || magic[2] != 'd') {
+            std::cerr << "invalid file" << std::endl;
+            return nullptr;
+        }
+
+        // version
+        stream->read((char *)&(result->version), sizeof(float));
+        if (result->version != 1.0f) {
+            std::cerr << "invalid version" << std::endl;
+            return nullptr;
+        }
+
+        // header
+        result->header.Read(stream);
+
+        // vertices
+        uint32_t vertex_num;
+        stream->read((char *)&vertex_num, sizeof(uint32_t));
+        result->vertices.resize(vertex_num);
+        for (uint32_t i = 0; i < vertex_num; i++) {
+            result->vertices[i].Read(stream);
+        }
+
+        // indices
+        uint32_t index_num;
+        stream->read((char *)&index_num, sizeof(uint32_t));
+        result->indices.resize(index_num);
+        for (uint32_t i = 0; i < index_num; i++) {
+            stream->read((char *)&result->indices[i], sizeof(uint16_t));
+        }
+
+        // materials
+        uint32_t material_num;
+        stream->read((char *)&material_num, sizeof(uint32_t));
+        result->materials.resize(material_num);
+        for (uint32_t i = 0; i < material_num; i++) {
+            result->materials[i].Read(stream);
+        }
+
+        // bones
+        uint16_t bone_num;
+        stream->read((char *)&bone_num, sizeof(uint16_t));
+        result->bones.resize(bone_num);
+        for (uint32_t i = 0; i < bone_num; i++) {
+            result->bones[i].Read(stream);
+        }
+
+        // iks
+        uint16_t ik_num;
+        stream->read((char *)&ik_num, sizeof(uint16_t));
+        result->iks.resize(ik_num);
+        for (uint32_t i = 0; i < ik_num; i++) {
+            result->iks[i].Read(stream);
+        }
+
+        // faces
+        uint16_t face_num;
+        stream->read((char *)&face_num, sizeof(uint16_t));
+        result->faces.resize(face_num);
+        for (uint32_t i = 0; i < face_num; i++) {
+            result->faces[i].Read(stream);
+        }
+
+        // face frames
+        uint8_t face_frame_num;
+        stream->read((char *)&face_frame_num, sizeof(uint8_t));
+        result->faces_indices.resize(face_frame_num);
+        for (uint32_t i = 0; i < face_frame_num; i++) {
+            stream->read((char *)&result->faces_indices[i], sizeof(uint16_t));
+        }
+
+        // bone names
+        uint8_t bone_disp_num;
+        stream->read((char *)&bone_disp_num, sizeof(uint8_t));
+        result->bone_disp_name.resize(bone_disp_num);
+        for (uint32_t i = 0; i < bone_disp_num; i++) {
+            result->bone_disp_name[i].Read(stream);
+        }
+
+        // bone frame
+        uint32_t bone_frame_num;
+        stream->read((char *)&bone_frame_num, sizeof(uint32_t));
+        result->bone_disp.resize(bone_frame_num);
+        for (uint32_t i = 0; i < bone_frame_num; i++) {
+            result->bone_disp[i].Read(stream);
+        }
+
+        // english name
+        bool english;
+        stream->read((char *)&english, sizeof(char));
+        if (english) {
+            result->header.ReadExtension(stream);
+            for (uint32_t i = 0; i < bone_num; i++) {
+                result->bones[i].ReadExpantion(stream);
+            }
+            for (uint32_t i = 0; i < face_num; i++) {
+                if (result->faces[i].type == pmd::FaceCategory::Base) {
+                    continue;
+                }
+                result->faces[i].ReadExpantion(stream);
+            }
+            for (uint32_t i = 0; i < result->bone_disp_name.size(); i++) {
+                result->bone_disp_name[i].ReadExpantion(stream);
+            }
+        }
+
+        // toon textures
+        if (stream->peek() == std::ios::traits_type::eof()) {
+            result->toon_filenames.clear();
+        } else {
+            result->toon_filenames.resize(10);
+            for (uint32_t i = 0; i < 10; i++) {
+                stream->read(buffer, 100);
+                result->toon_filenames[i] = std::string(buffer);
+            }
+        }
+
+        // physics
+        if (stream->peek() == std::ios::traits_type::eof()) {
+            result->rigid_bodies.clear();
+            result->constraints.clear();
+        } else {
+            uint32_t rigid_body_num;
+            stream->read((char *)&rigid_body_num, sizeof(uint32_t));
+            result->rigid_bodies.resize(rigid_body_num);
+            for (uint32_t i = 0; i < rigid_body_num; i++) {
+                result->rigid_bodies[i].Read(stream);
+            }
+            uint32_t constraint_num;
+            stream->read((char *)&constraint_num, sizeof(uint32_t));
+            result->constraints.resize(constraint_num);
+            for (uint32_t i = 0; i < constraint_num; i++) {
+                result->constraints[i].Read(stream);
+            }
+        }
+
+        if (stream->peek() != std::ios::traits_type::eof()) {
+            std::cerr << "there is unknown data" << std::endl;
+        }
+
+        return result;
+    }
+};
+
+} // namespace pmd
+

+ 9 - 11
code/Common/AssertHandler.cpp

@@ -5,8 +5,6 @@ Open Asset Import Library (assimp)
 
 Copyright (c) 2006-2022, assimp team
 
-
-
 All rights reserved.
 
 Redistribution and use of this software in source and binary forms,
@@ -50,23 +48,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <iostream>
 #include <cstdlib>
 
-void Assimp::defaultAiAssertHandler(const char* failedExpression, const char* file, int line)
-{
+void Assimp::defaultAiAssertHandler(const char* failedExpression, const char* file, int line) {
     std::cerr << "ai_assert failure in " << file << "(" << line << "): " << failedExpression << std::endl;
     std::abort();
 }
 
-namespace
-{
+namespace {
     Assimp::AiAssertHandler s_handler = Assimp::defaultAiAssertHandler;
 }
 
-void Assimp::setAiAssertHandler(AiAssertHandler handler)
-{
-    s_handler = handler;
+void Assimp::setAiAssertHandler(AiAssertHandler handler) {
+    if (handler != nullptr) {
+        s_handler = handler;
+    } else {
+        s_handler = Assimp::defaultAiAssertHandler;
+    }
 }
 
-void Assimp::aiAssertViolation(const char* failedExpression, const char* file, int line)
-{
+void Assimp::aiAssertViolation(const char* failedExpression, const char* file, int line) {
     s_handler(failedExpression, file, line);
 }

+ 28 - 24
code/Common/AssertHandler.h

@@ -47,29 +47,33 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <assimp/ai_assert.h>
 #include <assimp/defs.h>
 
-namespace Assimp
-{
-    // ---------------------------------------------------------------------------
-    /** Signature of functions which handle assert violations.
-     */
-    using AiAssertHandler = void (*)(const char* failedExpression, const char* file, int line);
-
-    // ---------------------------------------------------------------------------
-    /** Set the assert handler.
-     */
-    ASSIMP_API void setAiAssertHandler(AiAssertHandler handler);
-
-    // ---------------------------------------------------------------------------
-    /** The assert handler which is set by default.
-     *
-     * This issues a message to stderr and calls abort.
-     */
-    ASSIMP_API void defaultAiAssertHandler(const char* failedExpression, const char* file, int line);
-
-    // ---------------------------------------------------------------------------
-    /** Dispatches an assert violation to the assert handler.
-     */
-    ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line);
+namespace Assimp {
+
+// ---------------------------------------------------------------------------
+/**
+ *  @brief  Signature of functions which handle assert violations.
+ */
+using AiAssertHandler = void (*)(const char* failedExpression, const char* file, int line);
+
+// ---------------------------------------------------------------------------
+/**
+ *  @brief  Set the assert handler.
+ */
+ASSIMP_API void setAiAssertHandler(AiAssertHandler handler);
+
+// ---------------------------------------------------------------------------
+/** The assert handler which is set by default.
+ *
+ *  @brief  This issues a message to stderr and calls abort.
+ */
+ASSIMP_API void defaultAiAssertHandler(const char* failedExpression, const char* file, int line);
+
+// ---------------------------------------------------------------------------
+/**
+ *  @brief  Dispatches an assert violation to the assert handler.
+ */
+ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line);
+
 } // end of namespace Assimp
 
-#endif // INCLUDED_AI_ASSERTHANDLER_H
+#endif // INCLUDED_AI_ASSERTHANDLER_H

+ 14 - 3
code/Common/Base64.cpp

@@ -46,7 +46,7 @@ namespace Assimp {
 
 namespace Base64 {
 
-static const uint8_t tableDecodeBase64[128] = {
+static constexpr uint8_t tableDecodeBase64[128] = {
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
     0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 0, 0, 0, 63,
@@ -57,7 +57,7 @@ static const uint8_t tableDecodeBase64[128] = {
     41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0
 };
 
-static const char *tableEncodeBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+static constexpr char tableEncodeBase64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
 static inline char EncodeChar(uint8_t b) {
     return tableEncodeBase64[size_t(b)];
@@ -71,6 +71,11 @@ inline uint8_t DecodeChar(char c) {
 }
 
 void Encode(const uint8_t *in, size_t inLength, std::string &out) {
+    if (in == nullptr || inLength==0) {
+        out.clear();
+        return;
+    }
+
     size_t outLength = ((inLength + 2) / 3) * 4;
 
     size_t j = out.size();
@@ -115,8 +120,14 @@ std::string Encode(const std::vector<uint8_t> &in) {
 }
 
 size_t Decode(const char *in, size_t inLength, uint8_t *&out) {
+    if (in == nullptr) {
+        out = nullptr;
+        return 0;
+    }
+
     if (inLength % 4 != 0) {
-        throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)), "\", length:", inLength);
+        throw DeadlyImportError("Invalid base64 encoded data: \"", std::string(in, std::min(size_t(32), inLength)),
+            "\", length:", inLength);
     }
 
     if (inLength < 4) {

+ 9 - 5
code/Common/BaseImporter.cpp

@@ -232,19 +232,23 @@ void BaseImporter::GetExtensionList(std::set<std::string> &extensions) {
     std::string::size_type pos = pFile.find_last_of('.');
 
     // no file extension - can't read
-    if (pos == std::string::npos)
+    if (pos == std::string::npos) {
         return false;
+    }
 
     const char *ext_real = &pFile[pos + 1];
-    if (!ASSIMP_stricmp(ext_real, ext0))
+    if (!ASSIMP_stricmp(ext_real, ext0)) {
         return true;
+    }
 
     // check for other, optional, file extensions
-    if (ext1 && !ASSIMP_stricmp(ext_real, ext1))
+    if (ext1 && !ASSIMP_stricmp(ext_real, ext1)) {
         return true;
+    }
 
-    if (ext2 && !ASSIMP_stricmp(ext_real, ext2))
-        return true;
+    if (ext2 && !ASSIMP_stricmp(ext_real, ext2)) {
+        return true;        
+    }
 
     return false;
 }

+ 11 - 2
code/Common/BaseProcess.cpp

@@ -66,17 +66,26 @@ BaseProcess::~BaseProcess() {
 // ------------------------------------------------------------------------------------------------
 void BaseProcess::ExecuteOnScene(Importer *pImp) {
     ai_assert( nullptr != pImp );
-    ai_assert( nullptr != pImp->Pimpl()->mScene);
+    if (pImp == nullptr) {
+        return;
+    }
+
+    ai_assert(nullptr != pImp->Pimpl()->mScene);
+    if (pImp->Pimpl()->mScene == nullptr) {
+        return;
+    }
 
     progress = pImp->GetProgressHandler();
     ai_assert(nullptr != progress);
+    if (progress == nullptr) {
+        return;
+    }
 
     SetupProperties(pImp);
 
     // catch exceptions thrown inside the PostProcess-Step
     try {
         Execute(pImp->Pimpl()->mScene);
-
     } catch (const std::exception &err) {
 
         // extract error description

+ 25 - 21
code/Common/BaseProcess.h

@@ -175,23 +175,24 @@ private:
  * should be executed. If the function returns true, the class' Execute()
  * function is called subsequently.
  */
-class ASSIMP_API_WINONLY BaseProcess {
+class ASSIMP_API BaseProcess {
     friend class Importer;
 
 public:
-    /** Constructor to be privately used by Importer */
+    /** @brief onstructor to be privately used by Importer */
     BaseProcess() AI_NO_EXCEPT;
 
-    /** Destructor, private as well */
+    /** @brief Destructor, private as well */
     virtual ~BaseProcess();
 
     // -------------------------------------------------------------------
-    /** Returns whether the processing step is present in the given flag.
+    /**
+     * @brief Returns whether the processing step is present in the given flag.
      * @param pFlags The processing flags the importer was called with. A
      *   bitwise combination of #aiPostProcessSteps.
      * @return true if the process is present in this flag fields,
      *   false if not.
-    */
+     */
     virtual bool IsActive(unsigned int pFlags) const = 0;
 
     // -------------------------------------------------------------------
@@ -200,33 +201,36 @@ public:
     virtual bool RequireVerboseFormat() const;
 
     // -------------------------------------------------------------------
-    /** Executes the post processing step on the given imported data.
-    * The function deletes the scene if the postprocess step fails (
-    * the object pointer will be set to nullptr).
-    * @param pImp Importer instance (pImp->mScene must be valid)
-    */
+    /**
+     * @brief Executes the post processing step on the given imported data.
+     * The function deletes the scene if the post-process step fails (
+     * the object pointer will be set to nullptr).
+     * @param pImp Importer instance (pImp->mScene must be valid)
+     */
     void ExecuteOnScene(Importer *pImp);
 
     // -------------------------------------------------------------------
-    /** Called prior to ExecuteOnScene().
-    * The function is a request to the process to update its configuration
-    * basing on the Importer's configuration property list.
-    */
+    /**
+     * @brief Called prior to ExecuteOnScene().
+     * The function is a request to the process to update its configuration
+     * basing on the Importer's configuration property list.
+     */
     virtual void SetupProperties(const Importer *pImp);
 
     // -------------------------------------------------------------------
-    /** Executes the post processing step on the given imported data.
-    * A process should throw an ImportErrorException* if it fails.
-    * This method must be implemented by deriving classes.
-    * @param pScene The imported data to work at.
-    */
+    /**
+     * @brief Executes the post processing step on the given imported data.
+     * A process should throw an ImportErrorException* if it fails.
+     * This method must be implemented by deriving classes.
+     * @param pScene The imported data to work at.
+     */
     virtual void Execute(aiScene *pScene) = 0;
 
     // -------------------------------------------------------------------
     /** Assign a new SharedPostProcessInfo to the step. This object
-     *  allows multiple postprocess steps to share data.
+     *  allows multiple post-process steps to share data.
      * @param sh May be nullptr
-    */
+     */
     inline void SetSharedData(SharedPostProcessInfo *sh) {
         shared = sh;
     }

+ 1 - 2
code/PostProcessing/JoinVerticesProcess.cpp

@@ -341,8 +341,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) {
             uniqueVertices.push_back(v);
             if (hasAnimMeshes) {
                 for (unsigned int animMeshIndex = 0; animMeshIndex < pMesh->mNumAnimMeshes; animMeshIndex++) {
-                    Vertex aniMeshVertex(pMesh->mAnimMeshes[animMeshIndex], a);
-                    uniqueAnimatedVertices[animMeshIndex].push_back(v);
+                    uniqueAnimatedVertices[animMeshIndex].emplace_back(pMesh->mAnimMeshes[animMeshIndex], a);
                 }
             }
         } else{

+ 26 - 4
include/assimp/Base64.hpp

@@ -50,16 +50,38 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 namespace Assimp {
 namespace Base64 {
 
-/// @brief Will encode the given 
-/// @param in 
-/// @param inLength 
-/// @param out 
+/// @brief Will encode the given character buffer from UTF64 to ASCII
+/// @param in           The UTF-64 buffer.
+/// @param inLength     The size of the buffer
+/// @param out          The encoded ASCII string.
 void Encode(const uint8_t *in, size_t inLength, std::string &out);
+
+/// @brief Will encode the given character buffer from UTF64 to ASCII.
+/// @param in   A vector, which contains the buffer for encoding.
+/// @param out  The encoded ASCII string.
 void Encode(const std::vector<uint8_t>& in, std::string &out);
+
+/// @brief Will encode the given character buffer from UTF64 to ASCII.
+/// @param in   A vector, which contains the buffer for encoding.
+/// @return The encoded ASCII string.
 std::string Encode(const std::vector<uint8_t>& in);
 
+/// @brief Will decode the given character buffer from ASCII to UTF64.
+/// @param in           The ASCII buffer to decode.
+/// @param inLength     The size of the buffer.
+/// @param out          The decoded buffer.
+/// @return The new buffer size.
 size_t Decode(const char *in, size_t inLength, uint8_t *&out);
+
+/// @brief Will decode the given character buffer from ASCII to UTF64.
+/// @param in   The ASCII buffer to decode as a std::string.
+/// @param out  The decoded buffer.
+/// @return The new buffer size.
 size_t Decode(const std::string& in, std::vector<uint8_t>& out);
+
+/// @brief Will decode the given character buffer from ASCII to UTF64.
+/// @param in   The ASCII string.
+/// @return The decoded buffer in a vector.
 std::vector<uint8_t> Decode(const std::string& in);
 
 } // namespace Base64

+ 2 - 1
test/CMakeLists.txt

@@ -71,7 +71,6 @@ SET( COMMON
   unit/AssimpAPITest_aiQuaternion.cpp
   unit/AssimpAPITest_aiVector2D.cpp
   unit/AssimpAPITest_aiVector3D.cpp
-  unit/Common/utHash.cpp
   unit/MathTest.cpp
   unit/MathTest.h
   unit/RandomNumberGeneration.h
@@ -98,6 +97,8 @@ SET( COMMON
   unit/Common/utAssertHandler.cpp
   unit/Common/utXmlParser.cpp
   unit/Common/utBase64.cpp
+  unit/Common/utHash.cpp
+  unit/Common/utBaseProcess.cpp
 )
 
 SET( IMPORTERS

+ 25 - 16
test/unit/Common/utBase64.cpp

@@ -47,26 +47,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace std;
 using namespace Assimp;
 
-class Base64Test : public ::testing::Test {
-public:
-    virtual void SetUp() {
-    }
-
-    virtual void TearDown() {
-    }
-};
+class Base64Test : public ::testing::Test {};
 
 static const std::vector<uint8_t> assimpStringBinary = { 97, 115, 115, 105, 109, 112 };
 static const std::string assimpStringEncoded = "YXNzaW1w";
 
-TEST_F( Base64Test, encodeTest ) {
-    EXPECT_EQ( "", Base64::Encode (std::vector<uint8_t>{}) );
-    EXPECT_EQ( "Vg==", Base64::Encode (std::vector<uint8_t>{ 86 }) );
-    EXPECT_EQ( assimpStringEncoded, Base64::Encode (assimpStringBinary) );
+TEST_F( Base64Test, encodeTest) {
+    EXPECT_EQ( "", Base64::Encode(std::vector<uint8_t>{}) );
+    EXPECT_EQ( "Vg==", Base64::Encode(std::vector<uint8_t>{ 86 }) );
+    EXPECT_EQ( assimpStringEncoded, Base64::Encode(assimpStringBinary) );
+}
+
+TEST_F( Base64Test, encodeTestWithNullptr ) {
+    std::string out;
+    Base64::Encode(nullptr, 100u, out);
+    EXPECT_TRUE(out.empty());
+
+    Base64::Encode(&assimpStringBinary[0], 0u, out);
+    EXPECT_TRUE(out.empty());
+}
+
+TEST_F( Base64Test, decodeTest) {
+    EXPECT_EQ( std::vector<uint8_t> {}, Base64::Decode("") );
+    EXPECT_EQ( std::vector<uint8_t> { 86 }, Base64::Decode("Vg==") );
+    EXPECT_EQ( assimpStringBinary, Base64::Decode(assimpStringEncoded) );
 }
 
-TEST_F( Base64Test, decodeTest ) {
-    EXPECT_EQ( std::vector<uint8_t> {}, Base64::Decode ("") );
-    EXPECT_EQ( std::vector<uint8_t> { 86 }, Base64::Decode ("Vg==") );
-    EXPECT_EQ( assimpStringBinary, Base64::Decode (assimpStringEncoded) );
+TEST_F(Base64Test, decodeTestWithNullptr) {
+    uint8_t *out = nullptr;
+    size_t size = Base64::Decode(nullptr, 100u, out);
+    EXPECT_EQ(nullptr, out);
+    EXPECT_EQ(0u, size);
 }

+ 110 - 0
test/unit/Common/utBaseProcess.cpp

@@ -0,0 +1,110 @@
+/*
+---------------------------------------------------------------------------
+Open Asset Import Library (assimp)
+---------------------------------------------------------------------------
+
+Copyright (c) 2006-2022, assimp team
+
+All rights reserved.
+
+Redistribution and use of this software in source and binary forms,
+with or without modification, are permitted provided that the following
+conditions are met:
+
+* Redistributions of source code must retain the above
+copyright notice, this list of conditions and the
+following disclaimer.
+
+* Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the
+following disclaimer in the documentation and/or other
+materials provided with the distribution.
+
+* Neither the name of the assimp team, nor the names of its
+contributors may be used to endorse or promote products
+derived from this software without specific prior
+written permission of the assimp team.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+---------------------------------------------------------------------------
+*/
+
+#include "TestIOSystem.h"
+#include "UnitTestPCH.h"
+
+#include "Common/BaseProcess.h"
+#include "Common/AssertHandler.h"
+
+using namespace Assimp;
+
+class BaseProcessTest : public ::testing::Test {
+public:
+    static void test_handler( const char*, const char*, int ) {
+        HandlerWasCalled = true;
+    }
+
+    void SetUp() override {
+        HandlerWasCalled = false;
+        setAiAssertHandler(test_handler);
+    }
+
+    void TearDown() override {
+        setAiAssertHandler(nullptr);
+    }
+
+    static bool handlerWasCalled() {
+        return HandlerWasCalled;
+    }
+
+private:
+    static bool HandlerWasCalled;
+};
+
+bool BaseProcessTest::HandlerWasCalled = false;
+
+class TestingBaseProcess : public BaseProcess {
+public:
+    TestingBaseProcess() : BaseProcess() {
+        // empty
+    }
+
+    ~TestingBaseProcess() override = default;
+
+    bool IsActive( unsigned int ) const override {
+        return true;
+    }
+
+    void Execute(aiScene*) override {
+
+    }
+};
+TEST_F( BaseProcessTest, constructTest ) {
+    bool ok = true;
+    try {
+        TestingBaseProcess process;
+    } catch (...) {
+        ok = false;
+    }
+    EXPECT_TRUE(ok);
+}
+
+TEST_F( BaseProcessTest, executeOnSceneTest ) {
+    TestingBaseProcess process;
+    process.ExecuteOnScene(nullptr);
+#ifdef ASSIMP_BUILD_DEBUG
+    EXPECT_TRUE(BaseProcessTest::handlerWasCalled());
+#else
+    EXPECT_FALSE(BaseProcessTest::handlerWasCalled());
+#endif
+
+}