/// Current makesdna.cpp is a from Blender, but we will completely rewrite it in C++ under a ZLib license
/// The Original version is at https://svn.blender.org/svnroot/bf-blender/trunk/blender/source/blender/makesdna/intern/makesdna.c

/**
 * $Id$
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 *
 * Struct muncher for making SDNA
 *
 * Originally by Ton, some mods by Frank, and some cleaning and
 * extension by Nzc.
 *
 * Makesdna creates a .c file with a long string of numbers that
 * encode the Blender file format. It is fast, because it is basically
 * a binary dump. There are some details to mind when reconstructing
 * the file (endianness and byte-alignment).
 *
 * This little program scans all structs that need to be serialized,
 * and determined the names and types of all members. It calculates
 * how much memory (on disk or in ram) is needed to store that struct,
 * and the offsets for reaching a particular one.
 *
 * There is a facility to get verbose output from sdna. Search for
 * debugSDNA. This int can be set to 0 (no output) to some int. Higher
 * numbers give more output.
 * */

#ifdef __cplusplus
extern "C"
{
#endif

#if defined(_WIN32) && !defined(FREE_WINDOWS)

	/* The __intXX are built-in types of the visual complier! So we don't
 * need to include anything else here. */

	typedef signed __int8 int8_t;
	typedef signed __int16 int16_t;
	typedef signed __int32 int32_t;
	typedef signed __int64 int64_t;

	typedef unsigned __int8 uint8_t;
	typedef unsigned __int16 uint16_t;
	typedef unsigned __int32 uint32_t;
	typedef unsigned __int64 uint64_t;

#ifndef _INTPTR_T_DEFINED
#ifdef _WIN64
	typedef __int64 btintptr_t;
#else
	typedef long btintptr_t;
#endif

#else
	typedef intptr_t btintptr_t;
#endif

#elif defined(__linux__) || defined(__NetBSD__)

/* Linux-i386, Linux-Alpha, Linux-ppc */
#include <stdint.h>
typedef intptr_t btintptr_t;

#elif defined(__APPLE__)

#include <inttypes.h>
typedef intptr_t btintptr_t;

#elif defined(FREE_WINDOWS)

#include <stdint.h>

#else

/* FreeBSD, Irix, Solaris */
#include <sys/types.h>

#endif /* ifdef platform for types */

#ifdef __cplusplus
}
#endif

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

//#include "DNA_sdna_types.h"
// include files for automatic dependancies
#include "DNA_rigidbody.h"
#include "LinearMath/btVector3.h"
#include "LinearMath/btQuaternion.h"
#include "LinearMath/btMatrix3x3.h"
#include "LinearMath/btTransform.h"
#include "BulletCollision/BroadphaseCollision/btQuantizedBvh.h"
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h"
#include "BulletCollision/CollisionShapes/btConvexInternalShape.h"
#include "BulletCollision/CollisionShapes/btMultiSphereShape.h"
#include "BulletCollision/CollisionShapes/btConvexHullShape.h"
#include "BulletCollision/CollisionShapes/btStridingMeshInterface.h"
#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
#include "BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h"
#include "BulletCollision/CollisionShapes/btCompoundShape.h"
#include "BulletCollision/CollisionShapes/btCylinderShape.h"
#include "BulletCollision/CollisionShapes/btConeShape.h"
#include "BulletCollision/CollisionShapes/btCapsuleShape.h"
#include "BulletCollision/CollisionShapes/btTriangleInfoMap.h"
#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h"
#include "BulletCollision/Gimpact/btGImpactShape.h"
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
#include "BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h"
#include "BulletDynamics/ConstraintSolver/btHingeConstraint.h"
#include "BulletDynamics/ConstraintSolver/btConeTwistConstraint.h"
#include "BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h"
#include "BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h"
#include "BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h"
#include "BulletDynamics/ConstraintSolver/btSliderConstraint.h"
#include "BulletDynamics/ConstraintSolver/btGearConstraint.h"
#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h"
#include "BulletDynamics/Dynamics/btDynamicsWorld.h"

#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "BulletSoftBody/btSoftBodyData.h"
#include "BulletDynamics/Featherstone/btMultiBody.h"
#include "BulletDynamics/Featherstone/btMultiBodyLinkCollider.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#define SDNA_MAX_FILENAME_LENGTH 255

/* Included the path relative from /source/blender/ here, so we can move     */
/* headers around with more freedom.                                         */
char *includefiles[] = {

	// if you add files here, please add them at the end
	// of makesdna.c (this file) as well
	"../makesdna/DNA_rigidbody.h",
	"../../../src/LinearMath/btVector3.h",
	"../../../src/LinearMath/btQuaternion.h",
	"../../../src/LinearMath/btMatrix3x3.h",
	"../../../src/LinearMath/btTransform.h",
	"../../../src/BulletCollision/BroadphaseCollision/btQuantizedBvh.h",
	"../../../src/BulletCollision/CollisionShapes/btCollisionShape.h",
	"../../../src/BulletCollision/CollisionShapes/btStaticPlaneShape.h",
	"../../../src/BulletCollision/CollisionShapes/btConvexInternalShape.h",
	"../../../src/BulletCollision/CollisionShapes/btMultiSphereShape.h",
	"../../../src/BulletCollision/CollisionShapes/btStridingMeshInterface.h",
	"../../../src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h",
	"../../../src/BulletCollision/CollisionShapes/btScaledBvhTriangleMeshShape.h",
	"../../../src/BulletCollision/CollisionShapes/btCompoundShape.h",
	"../../../src/BulletCollision/CollisionShapes/btCylinderShape.h",
	"../../../src/BulletCollision/CollisionShapes/btConeShape.h",
	"../../../src/BulletCollision/CollisionShapes/btCapsuleShape.h",
	"../../../src/BulletCollision/CollisionShapes/btTriangleInfoMap.h",
	"../../../src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h",
	"../../../src/BulletCollision/Gimpact/btGImpactShape.h",
	"../../../src/BulletCollision/CollisionShapes/btConvexHullShape.h",
	"../../../src/BulletCollision/CollisionDispatch/btCollisionObject.h",
	"../../../src/BulletDynamics/ConstraintSolver/btContactSolverInfo.h",
	"../../../src/BulletDynamics/Dynamics/btDynamicsWorld.h",
	"../../../src/BulletDynamics/Dynamics/btRigidBody.h",
	"../../../src/BulletDynamics/ConstraintSolver/btTypedConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btHingeConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btGeneric6DofSpring2Constraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btSliderConstraint.h",
	"../../../src/BulletDynamics/ConstraintSolver/btGearConstraint.h",

	"../../../src/BulletSoftBody/btSoftBodyData.h",
	"../../../src/BulletDynamics/Featherstone/btMultiBody.h",
	"../../../src/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h",
	// empty string to indicate end of includefiles
	""};

void *malloc_and_setzero(int numbytes)
{
	char *buf = (char *)malloc(numbytes);
	memset(buf, 0, numbytes);
	return buf;
}

int maxdata = 500000, maxnr = 50000;
int nr_names = 0;
int nr_types = 0;
int nr_structs = 0;
char **names, *namedata;      /* at adress names[a] is string a */
char **types, *typedata;      /* at adress types[a] is string a */
short *typelens;              /* at typelens[a] is de length of type a */
short *alphalens;             /* contains sizes as they are calculated on the DEC Alpha (64 bits) */
short **structs, *structdata; /* at sp= structs[a] is the first adress of a struct definition
								   sp[0] is type number
								   sp[1] is amount of elements
								   sp[2] sp[3] is typenr,  namenr (etc) */
/*
 * debugSDNA:
 *  - 0 = no output, except errors
 *  - 1 = detail actions
 *  - 2 = full trace, tell which names and types were found
 *  - 4 = full trace, plus all gritty details
 */
int debugSDNA = 0;
int additional_slen_offset;

/* ************************************************************************** */
/* Functions                                                                  */
/* ************************************************************************** */

/**
 * Add type <str> to struct indexed by <len>, if it was not yet found.
 */
int add_type(char *str, int len);

/**
 * Add variable <str> to 
 */
int add_name(char *str);

/**
 * Search whether this structure type was already found, and if not,
 * add it.
 */
short *add_struct(int namecode);

/**
 * Remove comments from this buffer. Assumes that the buffer refers to
 * ascii-code text.
 */
int preprocess_include(char *maindata, int len);

/**
 * Scan this file for serializable types.
 */
int convert_include(char *filename);

/**
 * Determine how many bytes are needed for an array.
 */
int arraysize(char *astr, int len);

/**
 * Determine how many bytes are needed for each struct.
 */
static int calculate_structlens(int);

/**
 * Construct the DNA.c file
 */
void dna_write(FILE *file, void *pntr, int size);

/**
 * Report all structures found so far, and print their lenghts.
 */
void printStructLenghts(void);

/* ************************************************************************** */
/* Implementation                                                             */
/* ************************************************************************** */

/* ************************* MAKEN DNA ********************** */

int add_type(char *str, int len)
{
	int nr;
	char *cp;

	if (str[0] == 0) return -1;

	/* search through type array */
	for (nr = 0; nr < nr_types; nr++)
	{
		if (strcmp(str, types[nr]) == 0)
		{
			if (len)
			{
				typelens[nr] = len;
				alphalens[nr] = len;
			}
			return nr;
		}
	}

	/* append new type */
	if (nr_types == 0)
		cp = typedata;
	else
	{
		cp = types[nr_types - 1] + strlen(types[nr_types - 1]) + 1;
	}
	strcpy(cp, str);
	types[nr_types] = cp;
	typelens[nr_types] = len;
	alphalens[nr_types] = len;

	if (nr_types >= maxnr)
	{
		printf("too many types\n");
		return nr_types - 1;
		;
	}
	nr_types++;

	return nr_types - 1;
}

/**
 *
 * Because of the weird way of tokenizing, we have to 'cast' function
 * pointers to ... (*f)(), whatever the original signature. In fact,
 * we add name and type at the same time... There are two special
 * cases, unfortunately. These are explicitly checked.
 *
 * */
int add_name(char *str)
{
	int nr, i, j, k;
	char *cp;
	char buf[255]; /* stupid limit, change it :) */
	char *name;

	additional_slen_offset = 0;

	if ((str[0] == 0) /*  || (str[1]==0) */) return -1;

	if (str[0] == '(' && str[1] == '*')
	{
		if (debugSDNA > 3) printf("\t\t\t\t*** Function pointer found\n");
		/* functionpointer: transform the type (sometimes) */
		i = 0;
		j = 0;

		while (str[i] != ')')
		{
			buf[i] = str[i];
			i++;
		}

		/* Another number we need is the extra slen offset. This extra
		 * offset is the overshoot after a space. If there is no
		 * space, no overshoot should be calculated. */
		j = i; /* j at first closing brace */

		if (debugSDNA > 3) printf("first brace after offset %d\n", i);

		j++; /* j beyond closing brace ? */
		while ((str[j] != 0) && (str[j] != ')'))
		{
			if (debugSDNA > 3) printf("seen %c ( %d) \n", str[j], str[j]);
			j++;
		}
		if (debugSDNA > 3) printf("seen %c ( %d) \n", str[j], str[j]);
		if (debugSDNA > 3) printf("special after offset %d\n", j);

		if (str[j] == 0)
		{
			if (debugSDNA > 3) printf("offsetting for space\n");
			/* get additional offset */
			k = 0;
			while (str[j] != ')')
			{
				j++;
				k++;
			}
			if (debugSDNA > 3) printf("extra offset %d\n", k);
			additional_slen_offset = k;
		}
		else if (str[j] == ')')
		{
			if (debugSDNA > 3) printf("offsetting for brace\n");
			; /* don't get extra offset */
		}
		else
		{
			printf("Error during tokening function pointer argument list\n");
		}

		/*
		 * Put )(void) at the end? Maybe )(). Should check this with
         * old sdna. Actually, sometimes )(), sometimes )(void...)
         * Alas.. such is the nature of braindamage :(
		 *
		 * Sorted it out: always do )(), except for headdraw and
		 * windraw, part of ScrArea. This is important, because some
		 * linkers will treat different fp's differently when called
		 * !!! This has to do with interference in byte-alignment and
		 * the way args are pushed on the stack.
		 *
		 * */
		buf[i] = 0;
		if (debugSDNA > 3) printf("Name before chomping: %s\n", buf);
		if ((strncmp(buf, "(*headdraw", 10) == 0) || (strncmp(buf, "(*windraw", 9) == 0))
		{
			buf[i] = ')';
			buf[i + 1] = '(';
			buf[i + 2] = 'v';
			buf[i + 3] = 'o';
			buf[i + 4] = 'i';
			buf[i + 5] = 'd';
			buf[i + 6] = ')';
			buf[i + 7] = 0;
		}
		else
		{
			buf[i] = ')';
			buf[i + 1] = '(';
			buf[i + 2] = ')';
			buf[i + 3] = 0;
		}
		/* now precede with buf*/
		if (debugSDNA > 3) printf("\t\t\t\t\tProposing fp name %s\n", buf);
		name = buf;
	}
	else
	{
		/* normal field: old code */
		name = str;
	}

	/* search name array */
	for (nr = 0; nr < nr_names; nr++)
	{
		if (strcmp(name, names[nr]) == 0)
		{
			return nr;
		}
	}

	/* append new type */
	if (nr_names == 0)
		cp = namedata;
	else
	{
		cp = names[nr_names - 1] + strlen(names[nr_names - 1]) + 1;
	}
	strcpy(cp, name);
	names[nr_names] = cp;

	if (nr_names >= maxnr)
	{
		printf("too many names\n");
		return nr_names - 1;
	}
	nr_names++;

	return nr_names - 1;
}

short *add_struct(int namecode)
{
	int len;
	short *sp;

	if (nr_structs == 0)
	{
		structs[0] = structdata;
	}
	else
	{
		sp = structs[nr_structs - 1];
		len = sp[1];
		structs[nr_structs] = sp + 2 * len + 2;
	}

	sp = structs[nr_structs];
	sp[0] = namecode;

	if (nr_structs >= maxnr)
	{
		printf("too many structs\n");
		return sp;
	}
	nr_structs++;

	return sp;
}

int preprocess_include(char *maindata, int len)
{
	int a, newlen, comment = 0;
	char *cp, *temp, *md;

	temp = (char *)malloc_and_setzero(len);
	memcpy(temp, maindata, len);

	// remove all c++ comments
	/* replace all enters/tabs/etc with spaces */
	cp = temp;
	a = len;
	comment = 0;
	while (a--)
	{
		if (cp[0] == '/' && cp[1] == '/')
		{
			comment = 1;
		}
		else if (*cp < 32)
		{
			comment = 0;
		}
		if (comment || *cp < 32 || *cp > 128) *cp = 32;
		cp++;
	}

	/* data from temp copy to maindata, remove comments and double spaces */
	cp = temp;
	md = maindata;
	newlen = 0;
	comment = 0;
	a = len;
	while (a--)
	{
		if (cp[0] == '/' && cp[1] == '*')
		{
			comment = 1;
			cp[0] = cp[1] = 32;
		}
		if (cp[0] == '*' && cp[1] == '/')
		{
			comment = 0;
			cp[0] = cp[1] = 32;
		}

		/* do not copy when: */
		if (comment)
			;
		else if (cp[0] == ' ' && cp[1] == ' ')
			;
		else if (cp[-1] == '*' && cp[0] == ' ')
			; /* pointers with a space */
		else
		{
			md[0] = cp[0];
			md++;
			newlen++;
		}
		cp++;
	}

	free(temp);
	return newlen;
}

static void *read_file_data(char *filename, int *len_r)
{
#ifdef WIN32
	FILE *fp = fopen(filename, "rb");
#else
	FILE *fp = fopen(filename, "r");
#endif
	void *data;

	if (!fp)
	{
		*len_r = -1;
		return NULL;
	}

	fseek(fp, 0L, SEEK_END);
	*len_r = ftell(fp);
	fseek(fp, 0L, SEEK_SET);

	data = malloc_and_setzero(*len_r);
	if (!data)
	{
		*len_r = -1;
		fclose(fp);
		return NULL;
	}

	if (fread(data, *len_r, 1, fp) != 1)
	{
		*len_r = -1;
		free(data);
		fclose(fp);
		return NULL;
	}

	fclose(fp);
	return data;
}

const char *skipStructTypes[] =
	{
		"btContactSolverInfoData",
		"btRigidBodyConstructionInfo",
		"Euler",
		"btConstraintInfo2",
		"btConstraintSetting",
		"btTriangleInfo",
		""};

int skipStruct(const char *structType)
{
	int i = 0;
	while (strlen(skipStructTypes[i]))
	{
		if (strcmp(structType, skipStructTypes[i]) == 0)
		{
			return 1;
		}
		i++;
	}
	return 0;
}

int convert_include(char *filename)
{
	/* read include file, skip structs with a '#' before it.
	   store all data in temporal arrays.
	*/
	int filelen, count, overslaan, slen, type, name, strct;
	short *structpoin, *sp;
	char *maindata, *mainend, *md, *md1;

	md = maindata = (char *)read_file_data(filename, &filelen);
	if (filelen == -1)
	{
		printf("Can't read file %s\n", filename);
		return 1;
	}

	filelen = preprocess_include(maindata, filelen);
	mainend = maindata + filelen - 1;

	/* we look for '{' and then back to 'struct' */
	count = 0;
	overslaan = 0;
	while (count < filelen)
	{
		/* code for skipping a struct: two hashes on 2 lines. (preprocess added a space) */
		if (md[0] == '#' && md[1] == ' ' && md[2] == '#')
		{
			overslaan = 1;
		}

		if (md[0] == '{')
		{
			md[0] = 0;
			if (overslaan)
			{
				overslaan = 0;
			}
			else
			{
				if (md[-1] == ' ') md[-1] = 0;
				md1 = md - 2;
				while (*md1 != 32) md1--; /* to beginning of word */
				md1++;

				/* we've got a struct name when... */
				if (strncmp(md1 - 7, "struct", 6) == 0)
				{
					if (!skipStruct(md1))
					{
						strct = add_type(md1, 0);
						structpoin = add_struct(strct);
						sp = structpoin + 2;

						if (debugSDNA > 1) printf("\t|\t|-- detected struct %s\n", types[strct]);

						/* first lets make it all nice strings */
						md1 = md + 1;
						while (*md1 != '}')
						{
							if (md1 > mainend) break;

							if (*md1 == ',' || *md1 == ' ') *md1 = 0;
							md1++;
						}

						/* read types and names until first character that is not '}' */
						md1 = md + 1;
						while (*md1 != '}')
						{
							if (md1 > mainend) break;

							/* skip when it says 'struct' or 'unsigned' or 'const' */
							if (*md1)
							{
								if (strncmp(md1, "struct", 6) == 0) md1 += 7;
								if (strncmp(md1, "unsigned", 8) == 0) md1 += 9;
								if (strncmp(md1, "const", 5) == 0) md1 += 6;

								/* we've got a type! */
								type = add_type(md1, 0);

								if (debugSDNA > 1) printf("\t|\t|\tfound type %s (", md1);

								md1 += strlen(md1);

								/* read until ';' */
								while (*md1 != ';')
								{
									if (md1 > mainend) break;

									if (*md1)
									{
										/* We've got a name. slen needs
										 * correction for function
										 * pointers! */
										slen = (int)strlen(md1);
										if (md1[slen - 1] == ';')
										{
											md1[slen - 1] = 0;

											name = add_name(md1);
											slen += additional_slen_offset;
											sp[0] = type;
											sp[1] = name;

											if ((debugSDNA > 1) && (names[name] != 0)) printf("%s |", names[name]);

											structpoin[1]++;
											sp += 2;

											md1 += slen;
											break;
										}

										name = add_name(md1);
										slen += additional_slen_offset;

										sp[0] = type;
										sp[1] = name;
										if ((debugSDNA > 1) && (names[name] != 0)) printf("%s ||", names[name]);

										structpoin[1]++;
										sp += 2;

										md1 += slen;
									}
									md1++;
								}

								if (debugSDNA > 1) printf(")\n");
							}
							md1++;
						}
					}
				}
			}
		}
		count++;
		md++;
	}

	free(maindata);

	return 0;
}

int arraysize(char *astr, int len)
{
	int a, mul = 1;
	char str[100], *cp = 0;

	memcpy(str, astr, len + 1);

	for (a = 0; a < len; a++)
	{
		if (str[a] == '[')
		{
			cp = &(str[a + 1]);
		}
		else if (str[a] == ']' && cp)
		{
			str[a] = 0;
			mul *= atoi(cp);
		}
	}

	return mul;
}

static int calculate_structlens(int firststruct)
{
	int a, b, len, alphalen, unknown = nr_structs, lastunknown, structtype, type, mul, namelen;
	short *sp, *structpoin;
	char *cp;
	int has_pointer, dna_error = 0;

	while (unknown)
	{
		lastunknown = unknown;
		unknown = 0;

		/* check all structs... */
		for (a = 0; a < nr_structs; a++)
		{
			structpoin = structs[a];
			structtype = structpoin[0];

			/* when length is not known... */
			if (typelens[structtype] == 0)
			{
				sp = structpoin + 2;
				len = 0;
				alphalen = 0;
				has_pointer = 0;

				/* check all elements in struct */
				for (b = 0; b < structpoin[1]; b++, sp += 2)
				{
					type = sp[0];
					cp = names[sp[1]];

					namelen = (int)strlen(cp);
					/* is it a pointer or function pointer? */
					if (cp[0] == '*' || cp[1] == '*')
					{
						has_pointer = 1;
						/* has the name an extra length? (array) */
						mul = 1;
						if (cp[namelen - 1] == ']') mul = arraysize(cp, namelen);

						/* 4-8 aligned/ */
						if (sizeof(void *) == 4)
						{
							if (len % 4)
							{
								printf("Align pointer error in struct (len4): %s %s\n", types[structtype], cp);
								dna_error = 1;
							}
						}
						else
						{
							if (len % 8)
							{
								printf("Align pointer error in struct (len8): %s %s\n", types[structtype], cp);
								dna_error = 1;
							}
						}

						if (alphalen % 8)
						{
							printf("Align pointer error in struct (alphalen8): %s %s\n", types[structtype], cp);
							dna_error = 1;
						}

						len += sizeof(void *) * mul;
						alphalen += 8 * mul;
					}
					else if (typelens[type])
					{
						/* has the name an extra length? (array) */
						mul = 1;
						if (cp[namelen - 1] == ']') mul = arraysize(cp, namelen);

						/* struct alignment */
						if (type >= firststruct)
						{
							if (sizeof(void *) == 8 && (len % 8))
							{
								printf("Align struct error: %s %s\n", types[structtype], cp);
								dna_error = 1;
							}
						}

						/* 2-4 aligned/ */
						if (typelens[type] > 3 && (len % 4))
						{
							printf("Align 4 error in struct: %s %s (add %d padding bytes)\n", types[structtype], cp, len % 4);
							dna_error = 1;
						}
						else if (typelens[type] == 2 && (len % 2))
						{
							printf("Align 2 error in struct: %s %s (add %d padding bytes)\n", types[structtype], cp, len % 2);
							dna_error = 1;
						}

						len += mul * typelens[type];
						alphalen += mul * alphalens[type];
					}
					else
					{
						len = 0;
						alphalen = 0;
						break;
					}
				}

				if (len == 0)
				{
					unknown++;
				}
				else
				{
					typelens[structtype] = len;
					alphalens[structtype] = alphalen;
					// two ways to detect if a struct contains a pointer:
					// has_pointer is set or alphalen != len
					if (has_pointer || alphalen != len)
					{
						if (alphalen % 8)
						{
							printf("alphalen = %d len = %d\n", alphalen, len);
							printf("Sizeerror 8 in struct: %s (add %d bytes)\n", types[structtype], alphalen % 8);
							dna_error = 1;
						}
					}

					if (len % 4)
					{
						printf("Sizeerror 4 in struct: %s (add %d bytes)\n", types[structtype], len % 4);
						dna_error = 1;
					}
				}
			}
		}

		if (unknown == lastunknown) break;
	}

	if (unknown)
	{
		printf("ERROR: still %d structs unknown\n", unknown);

		if (debugSDNA)
		{
			printf("*** Known structs : \n");

			for (a = 0; a < nr_structs; a++)
			{
				structpoin = structs[a];
				structtype = structpoin[0];

				/* length unknown */
				if (typelens[structtype] != 0)
				{
					printf("  %s\n", types[structtype]);
				}
			}
		}

		printf("*** Unknown structs : \n");

		for (a = 0; a < nr_structs; a++)
		{
			structpoin = structs[a];
			structtype = structpoin[0];

			/* length unkown yet */
			if (typelens[structtype] == 0)
			{
				printf("  %s\n", types[structtype]);
			}
		}
	}

	return (dna_error);
}

#define MAX_DNA_LINE_LENGTH 20

void dna_write(FILE *file, void *pntr, int size)
{
	static int linelength = 0;
	int i;
	char *data;

	data = (char *)pntr;

	for (i = 0; i < size; i++)
	{
		fprintf(file, "char(%d),", data[i]);
		linelength++;
		if (linelength >= MAX_DNA_LINE_LENGTH)
		{
			fprintf(file, "\n");
			linelength = 0;
		}
	}
}

void printStructLenghts(void)
{
	int a, unknown = nr_structs, lastunknown, structtype;
	short *structpoin;
	printf("\n\n*** All detected structs:\n");

	while (unknown)
	{
		lastunknown = unknown;
		unknown = 0;

		/* check all structs... */
		for (a = 0; a < nr_structs; a++)
		{
			structpoin = structs[a];
			structtype = structpoin[0];
			printf("\t%s\t:%d\n", types[structtype], typelens[structtype]);
		}
	}

	printf("*** End of list\n");
}

int make_structDNA(char *baseDirectory, FILE *file)
{
	int len, i;
	short *sp;
	/* str contains filenames. Since we now include paths, I stretched       */
	/* it a bit. Hope this is enough :) -nzc-                                */
	char str[SDNA_MAX_FILENAME_LENGTH], *cp;
	int firststruct;

	if (debugSDNA > -1)
	{
		fflush(stdout);
		printf("Running makesdna at debug level %d\n", debugSDNA);
	}

	/* the longest known struct is 50k, so we assume 100k is sufficent! */
	namedata = (char *)malloc_and_setzero(maxdata);
	typedata = (char *)malloc_and_setzero(maxdata);
	structdata = (short *)malloc_and_setzero(maxdata);

	/* a maximum of 5000 variables, must be sufficient? */
	names = (char **)malloc_and_setzero(sizeof(char *) * maxnr);
	types = (char **)malloc_and_setzero(sizeof(char *) * maxnr);
	typelens = (short *)malloc_and_setzero(sizeof(short) * maxnr);
	alphalens = (short *)malloc_and_setzero(sizeof(short) * maxnr);
	structs = (short **)malloc_and_setzero(sizeof(short) * maxnr);

	/* insertion of all known types */
	/* watch it: uint is not allowed! use in structs an unsigned int */
	add_type("char", 1);         /* 0 */
	add_type("uchar", 1);        /* 1 */
	add_type("short", 2);        /* 2 */
	add_type("ushort", 2);       /* 3 */
	add_type("int", 4);          /* 4 */
	add_type("long", 4); /* 5 */ /* should it be 8 on 64 bits? */
	add_type("ulong", 4);        /* 6 */
	add_type("float", 4);        /* 7 */
	add_type("double", 8);       /* 8 */
	add_type("void", 0);         /* 9 */

	// the defines above shouldn't be output in the padding file...
	firststruct = nr_types;

	/* add all include files defined in the global array                     */
	/* Since the internal file+path name buffer has limited length, I do a   */
	/* little test first...                                                  */
	/* Mind the breaking condition here!                                     */
	if (debugSDNA) printf("\tStart of header scan:\n");
	for (i = 0; strlen(includefiles[i]); i++)
	{
		sprintf(str, "%s%s", baseDirectory, includefiles[i]);
		if (debugSDNA) printf("\t|-- Converting %s\n", str);
		if (convert_include(str))
		{
			return (1);
		}
	}
	if (debugSDNA) printf("\tFinished scanning %d headers.\n", i);

	if (calculate_structlens(firststruct))
	{
		// error
		return (1);
	}

	/* FOR DEBUG */
	if (debugSDNA > 1)
	{
		int a, b;
		/*  		short *elem; */
		short num_types;

		printf("nr_names %d nr_types %d nr_structs %d\n", nr_names, nr_types, nr_structs);
		for (a = 0; a < nr_names; a++)
		{
			printf(" %s \n", names[a]);
		}
		printf("\n");

		sp = typelens;
		for (a = 0; a < nr_types; a++, sp++)
		{
			printf(" %s %d\n", types[a], *sp);
		}
		printf("\n");

		for (a = 0; a < nr_structs; a++)
		{
			sp = structs[a];
			printf(" struct %s elems: %d size: %d\n", types[sp[0]], sp[1], typelens[sp[0]]);
			num_types = sp[1];
			sp += 2;
			/* ? num_types was elem? */
			for (b = 0; b < num_types; b++, sp += 2)
			{
				printf("   %s %s\n", types[sp[0]], names[sp[1]]);
			}
		}
	}

	/* file writing */

	if (debugSDNA > -1) printf("Writing file ... ");

	if (nr_names == 0 || nr_structs == 0)
		;
	else
	{
		strcpy(str, "SDNA");
		dna_write(file, str, 4);

		/* write names */
		strcpy(str, "NAME");
		dna_write(file, str, 4);
		len = nr_names;
		dna_write(file, &len, 4);

		/* calculate size of datablock with strings */
		cp = names[nr_names - 1];
		cp += strlen(names[nr_names - 1]) + 1; /* +1: null-terminator */
		len = (btintptr_t)(cp - (char *)names[0]);
		len = (len + 3) & ~3;
		dna_write(file, names[0], len);

		/* write TYPES */
		strcpy(str, "TYPE");
		dna_write(file, str, 4);
		len = nr_types;
		dna_write(file, &len, 4);

		/* calculate datablock size */
		cp = types[nr_types - 1];
		cp += strlen(types[nr_types - 1]) + 1; /* +1: null-terminator */
		len = (btintptr_t)(cp - (char *)types[0]);
		len = (len + 3) & ~3;

		dna_write(file, types[0], len);

		/* WRITE TYPELENGTHS */
		strcpy(str, "TLEN");
		dna_write(file, str, 4);

		len = 2 * nr_types;
		if (nr_types & 1) len += 2;
		dna_write(file, typelens, len);

		/* WRITE STRUCTS */
		strcpy(str, "STRC");
		dna_write(file, str, 4);
		len = nr_structs;
		dna_write(file, &len, 4);

		/* calc datablock size */
		sp = structs[nr_structs - 1];
		sp += 2 + 2 * (sp[1]);
		len = (btintptr_t)((char *)sp - (char *)structs[0]);
		len = (len + 3) & ~3;

		dna_write(file, structs[0], len);

		/* a simple dna padding test */
		if (0)
		{
			FILE *fp;
			int a;

			fp = fopen("padding.c", "w");
			if (fp == NULL)
				;
			else
			{
				// add all include files defined in the global array
				for (i = 0; strlen(includefiles[i]); i++)
				{
					fprintf(fp, "#include \"%s%s\"\n", baseDirectory, includefiles[i]);
				}

				fprintf(fp, "main(){\n");
				sp = typelens;
				sp += firststruct;
				for (a = firststruct; a < nr_types; a++, sp++)
				{
					if (*sp)
					{
						fprintf(fp, "\tif(sizeof(struct %s) - %d) printf(\"ALIGN ERROR:", types[a], *sp);
						fprintf(fp, "%%d %s %d ", types[a], *sp);
						fprintf(fp, "\\n\",  sizeof(struct %s) - %d);\n", types[a], *sp);
					}
				}
				fprintf(fp, "}\n");
				fclose(fp);
			}
		}
		/*	end end padding test */
	}

	free(namedata);
	free(typedata);
	free(structdata);
	free(names);
	free(types);
	free(typelens);
	free(structs);

	if (debugSDNA > -1) printf("done.\n");

	return (0);
}

/* ************************* END MAKE DNA ********************** */

static void make_bad_file(char *file)
{
	FILE *fp = fopen(file, "w");
	fprintf(fp, "ERROR! Cannot make correct DNA.c file, STUPID!\n");
	fclose(fp);
}

#ifndef BASE_HEADER
#define BASE_HEADER "../"
#endif

int main(int argc, char **argv)
{
	//	printf("btCollisionObject=%d\n",sizeof(btCollisionObject));
	//	printf("btCollisionObjectData=%d\n",sizeof(btCollisionObjectData));
	//	printf("btTransform=%d\n",sizeof(btTransform));
	//	printf("btTransformData=%d\n",sizeof(btTransformData));
	//
	//	btCollisionObject* bla = new btCollisionObject();
	//	btCollisionObjectData* bla2 = new btCollisionObjectData();

	//int offsetof(bla,m_hasAnisotropicFriction);
	/*
	btTransformData	m_worldTransform;
	btTransform	m_interpolationWorldTransform;
	btVector3	m_interpolationLinearVelocity;
	btVector3	m_interpolationAngularVelocity;
	btVector3		m_anisotropicFriction;
	bool				m_hasAnisotropicFriction;
	btScalar		m_contactProcessingThreshold;	
	btBroadphaseProxy	*m_broadphaseHandle;
	btCollisionShape	*m_collisionShape;
	btCollisionShape	*m_rootCollisionShape;
	int				m_collisionFlags;
	int				m_islandTag1;
	int				m_companionId;
	int				m_activationState1;
	btScalar		m_deactivationTime;
	btScalar		m_friction;
	btScalar		m_restitution;
	void*			m_userObjectPointer;
	int				m_internalType;
	btScalar		m_hitFraction; 
	btScalar		m_ccdSweptSphereRadius;
	btScalar		m_ccdMotionThreshold;
	bool			m_checkCollideWith;
	char			m_pad[7];
*/

	FILE *file;
	int return_status = 0;

	if (argc != 2 && argc != 3)
	{
		printf("Usage: %s outfile.c [base directory]\n", argv[0]);
		return_status = 1;
	}
	else
	{
		file = fopen(argv[1], "w");
		if (!file)
		{
			printf("Unable to open file: %s\n", argv[1]);
			return_status = 1;
		}
		else
		{
			char baseDirectory[256];

			if (argc == 3)
			{
				strcpy(baseDirectory, argv[2]);
			}
			else
			{
				strcpy(baseDirectory, BASE_HEADER);
			}

			if (sizeof(void *) == 8)
			{
				fprintf(file, "char sBulletDNAstr64[]= {\n");
			}
			else
			{
				fprintf(file, "char sBulletDNAstr[]= {\n");
			}

			if (make_structDNA(baseDirectory, file))
			{
				// error
				fclose(file);
				make_bad_file(argv[1]);
				return_status = 1;
			}
			else
			{
				fprintf(file, "};\n");
				if (sizeof(void *) == 8)
				{
					fprintf(file, "int sBulletDNAlen64= sizeof(sBulletDNAstr64);\n");
				}
				else
				{
					fprintf(file, "int sBulletDNAlen= sizeof(sBulletDNAstr);\n");
				}

				fclose(file);
			}
		}
	}

	return (return_status);
}

/* end of list */
