//================================================================================================
// DESCRIPTION:	3D file saving common code
//
// AUTHOR:  Gabor Nagy
// DATE:    2026-Jul-12 17:26:05
//
// Copyright (C) 1996-2026 by Gabor Nagy. All rights reserved.
//================================================================================================

#include <E3D/3DIO.h>

#define E3dPolygonCfNORMAL  0x0001

static void _Cf_AddString(const char* PString, FILE* PFile)
{
	if(PString)
	{
		const size_t  LN = (int)strlen(PString);

		fwrite(PString, 1, LN + 1, PFile);
	}
	else fputc(0, PFile);
}


static void _ObjectSaveAllResources(EResContext* PContext, E3dIORec* P3DIORec, FILE* PFile,  const ECoreObject* PObject)
{
	uint8_t*  LPtr = P3DIORec->ResourcesBinaryImage;

	EObject_ResourcesToBinaryImage(PObject, PContext, &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated, &LPtr);

	const int  LLength = LPtr - P3DIORec->ResourcesBinaryImage;
	if(LLength) fwrite(P3DIORec->ResourcesBinaryImage, 1, LLength, PFile);
}


static void _SplineSave(FILE* PFile, E3dSpline* PSpline)
{
	E3dSpline_Save(PSpline,  E3dCOORDINATE_TYPE, 3, E3dCVsALL,  PFile, NULL);
}


//========================================================
// Collect Nodes into P3DIORec->Nodes
//========================================================
static void _NodesCollect(EResContext* PContext, E3dIORec* P3DIORec, E3dNode** PRootNodes, const EIndex PNRootNodes, const EBool PSavingScene)
{
	EIndex  LRI = 0;for(;LRI < PNRootNodes;LRI++)
	{
// Walk the Nodes to initialize the P3DIORec->Nodes array
//
		E3dNode*  LNode = PRootNodes[LRI];E3dNode*  LNodeAfterBranch = E3dNode_BranchGetNodeAfter(LNode);for(;LNode != LNodeAfterBranch;LNode = LNode->Next)
		{
			EList_PointerAddAChk(P3DIORec->Nodes, 256, LNode);
		}
	}

// Add Models of 3DWindow Cameras (these are not necessarily instantiated in the Scene)
//
	if(PSavingScene)
	{
		EIndex  LPI = 0;for(;LPI < E3dp_Panels.Count;LPI++)
		{
			E3dPanel*  LPanel = E3dp_Panels.Items[LPI];
			EIndex  LWI = 0;for(;LWI < LPanel->Windows.Count;LWI++)
			{
				E3dWindow*  L3DWindow = LPanel->Windows.Items[LWI];
				{
					EIndex  LVI = 0;const EIndex  LVN = L3DWindow->Views.Count;for(;LVI < LVN;LVI++)
					{
						E3dView*  LView = L3DWindow->Views.Items[LVI];
						EList_PointerAddAChk(P3DIORec->Nodes, 256, LView->CameraModel);
					}
				}
			}
		}
	}
}


static int _AddNode(EResContext* PContext, E3dIORec* P3DIORec, FILE* PFile, const EBool PHardWiredResources, E3dNode* PNode, E3dNode* PParentNode)
{
	{
		EIndex  LIndex = EList_PointerGetIndex(P3DIORec->Nodes, PNode);

if(E3dDebugNODE)
{
	printf("%s() %s:%d  %s[%s] %lld%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, PNode->Name, (long long)LIndex, ECodeNormal);fflush(stdout);
}
		if(LIndex < 0) return(-1);
	}

	ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)PNode, TRUE);

//if(PNode->RefCnt < 2) { printf("%s() %s:%d  [%s] RefCnt %d\n", __FUNCTION__, __FILE__, __LINE__, PNode->Name, PNode->RefCnt);fflush(stdout); }

	_Cf_AddString(PNode->Name, PFile);

	if(PHardWiredResources)  // Old way
	{
		uint8_t  LCh = 0;
		if(PNode->Visible)                    LCh |= 0x01;
		if(PNode->SchemObject.BranchExpanded) LCh |= 0x02;
		if(PNode->OutputsEnabled)             LCh |= 0x04;
		if(PNode->BranchOutputsEnabled)       LCh |= 0x08;
		_AddChar(LCh);

		_AddChar((char)PNode->Selection);

		_AddUInt32(PNode->SubCameras);
		_AddChar(CfModelNORMAL);
	}
	else
	{
if(E3dDebugNODE)
{
	printf("%s() %s:%d <%s>  Resources at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, PNode->Name, ECodeCyan, (long long)ftell(PFile), ECodeNormal);fflush(stdout);
}
		_ObjectSaveAllResources(PContext, P3DIORec, PFile,  (ECoreObject*)PNode);
	}


	if(EObject_IsSubClass(PNode, E3dModel))
	{
		E3dModel*  LModel = (E3dModel*)PNode;

		_AddChar(LModel->TransformSet.EulerRotation.Order);
	}
	else _AddChar(0);


// Parent's index. This is sufficient to restore all hierarchy links.
//
	{
		EIndex  LParentIndex;
		if(PParentNode == NULL) LParentIndex = -1;
		else if((LParentIndex = EList_PointerGetIndex(P3DIORec->Nodes, (void*)(PParentNode))) < 0) return(-1);
		_AddInt32(LParentIndex);
	}

	{
		E3dModel*     LModel = (E3dModel*)PNode;
		const EIndex  LObjN = LModel->Objects.Count;

if(E3dDebugNODE)
{
	printf("%s() %s:%d  [%s] Object indices at %s%08llx%s %lld\n", __FUNCTION__, __FILE__, __LINE__, PNode->Name, ECodeCyan, (long long)ftell(PFile), ECodeNormal, (long long)LObjN);
	fflush(stdout);
}
		_AddInt32(LObjN);

		EIndex  LObjI = 0;E3dObject**  LObjects = LModel->Objects.Items;for(;LObjI < LObjN;LObjI++)
		{
			E3dObject*  LObject = LObjects[LObjI];if(LObject)
			{
				int  LIdx = EList_PointerGetIndex_s(PContext->AllObjects, LObject);
//printf("%s() %s:%d  <%s> %lld\n", __FUNCTION__, __FILE__, __LINE__, LObject->Name, (long long)LIdx);fflush(stdout);
				if(LIdx >= 0) _AddInt32(LIdx);else _AddInt32(-1);
			}
			else
			{
				_AddInt32(-1);  // LModel->NumOfObjects > 0, but LModel->Objects[] has a NULL pointer in it!
			}
		}
	}


// Save transforms
//
	if(EObject_IsSubClass(PNode, E3dModel))
	{
		const E3dModel*  LModel = (E3dModel*)PNode;
		const E3dTransformSet*  LTransformSet = &LModel->TransformSet;

if(E3dDebugNODE)
{
	printf("%s() %s:%d  [%s] Transforms at %s%08llx%s  %lld\n",  __FUNCTION__, __FILE__, __LINE__,  PNode->Name, ECodeCyan, (long long)ftell(PFile), ECodeNormal,
	 (long long)LTransformSet->Transforms_Count);
	fflush(stdout);
}
		
		{
			const EIndex  LN = LTransformSet->Transforms_Count;_AddInt32(LN);if(LN)
			{
				uint8_t*  LBuffer = EMalloc(256);EIndex  LBytesAllocated = 256;

				EIndex  LI = 0;for(;LI < LN;LI++)
				{
					E3dTransform*  LTransform = LTransformSet->Transforms[LI];

					ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)LTransform, TRUE);

					const EIndex  LClassIndex = EList_ClassGetIndex_s(PContext->Classes, (ECoreClass*)LTransform->Class);if(LClassIndex >= 0)
					{

if(E3dDebugNODE)
{
	printf("%s() %s:%d  [%s] Transform at %s%08llx%s  %lld  Class (%s) Index %lld\n",  __FUNCTION__, __FILE__, __LINE__,  PNode->Name, ECodeCyan, (long long)ftell(PFile), ECodeNormal,
	 (long long)LI, LTransform->Class->Name, (long long)LClassIndex);
	fflush(stdout);
}
						_AddInt16(LClassIndex);

						{
							uint8_t*  LPtr = LBuffer;

							const int  LLen = ERes_ToBinaryImage(PContext, &LTransform->Class->Resources, LTransform,  &LBuffer, &LBytesAllocated,  &LPtr);
							if(LLen >= 0) fwrite(LBuffer, sizeof(char), LLen, PFile);
						}
					}
				}
			}
		}


if(E3dDebugNODE)
{
	printf("%s() %s:%d  [%s] TransformSet at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, PNode->Name, ECodeCyan, (long long)ftell(PFile), ECodeNormal);fflush(stdout);
}
		_AddFloat32(LModel->TransformSet.Translation.X);
		_AddFloat32(LModel->TransformSet.Translation.Y);
		_AddFloat32(LModel->TransformSet.Translation.Z);
		_AddFloat32(LModel->TransformSet.Scaling.X);
		_AddFloat32(LModel->TransformSet.Scaling.Y);
		_AddFloat32(LModel->TransformSet.Scaling.Z);
		_AddFloat32(LModel->TransformSet.EulerRotation.Angles.X);
		_AddFloat32(LModel->TransformSet.EulerRotation.Angles.Y);
		_AddFloat32(LModel->TransformSet.EulerRotation.Angles.Z);
	}


/*
	_AddFloat32(LModel->SchemTranslation.X);
	_AddFloat32(LModel->SchemTranslation.Y);
	_AddFloat32(LModel->SchemTranslation.Z);
*/




// Save Components
//
	if(PNode->Components)
	{
		EIndex        LTotalSize = 0;
		const EIndex  LNumOfComponents = PNode->Components_Count;

// Compute total size for Components
//
		{
			EIndex  LI = 0;for(;LI < LNumOfComponents;LI++)
			{
				E3dNodeComponent*  LInfo = PNode->Components[LI];if(LInfo->InternalFlags & EComponentSAVE)
				{
					LTotalSize++;  // Flags, Index (ID) size in bytes
//					if(LInfo->InternalFlags & EComponentANIMATABLE) LTotalSize += 4;
					LTotalSize += 4;

					E3dNodeComponentClass*  LClass = LInfo->Class;
					const size_t            LNameLen = strlen(LClass->Name) + 1;

					LTotalSize += LNameLen;

					const EIndex  LIV = EObject_ResourcesBinaryImageSize(PContext, (ECoreObject*)LInfo);
					if(LIV > 0) LTotalSize += LIV;
				}
			}
		}

if(E3dDebugNODE)
{
	printf("%s() %s:%d  [%s] NodeInfo %08llx at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, PNode->Name, (long long)LTotalSize, ECodeCyan, (long long)ftell(PFile), ECodeNormal);
	fflush(stdout);
}
		_AddInt32(LTotalSize);

		{
			EIndex  LI = 0;for(;LI < LNumOfComponents;LI++)
			{
				E3dNodeComponent*  LInfo = PNode->Components[LI];

				if(LInfo->InternalFlags & EComponentSAVE)
				{
//					ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)LInfo, (LInfo->InternalFlags & EComponentANIMATABLE) != 0);
					ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)LInfo, TRUE);

					E3dNodeComponentClass*  LClass = LInfo->Class;
					const size_t            LNameLen = strlen(LClass->Name) + 1;

					fwrite(LClass->Name, 1, LNameLen, PFile);

					const int  LIV = EObject_ResourcesToBinaryImage((const ECoreObject*)LInfo, PContext,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated, NULL);

//printf("%s() %s:%d  [%s] NodeInfo %08llx at %08llx\n", __FUNCTION__, __FILE__, __LINE__, PNode->Name, (long long)LTotalSize, (long long)ftell(PFile));fflush(stdout);
					if(LIV) fwrite(P3DIORec->ResourcesBinaryImage, 1, LIV, PFile);
				}
			}
		}
	}
	else _AddInt32(0);  // No Node->Components -> TotalSize = 0

if(E3dDebugNODE) { printf("\n");fflush(stdout); }

	return(0);
}


static int _NodesSave(EResContext* PContext, E3dIORec* P3DIORec, FILE* PFile, const EBool PHardWiredResources,  E3dNode** PRootNodes, const EIndex PNRootNodes,
 const EBool PSaveHierarchies, const EBool PSavingScene)
{
	_AddInt32((int)PNRootNodes);

if(E3dDebugNODE)
{
	printf("%s() %s:%d  %sat %08llx%s PNRootNodes %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal, (int)PNRootNodes);fflush(stdout);
}

	EListAI(, E3dNode*, LSavedNodes);

	EIndex  LRI = 0;for(;LRI < PNRootNodes;LRI++)
	{
		E3dNode*  LRootNode = PRootNodes[LRI];
		E3dNode*  LNodeAfterBranch = E3dNode_BranchGetNodeAfter(LRootNode);
		EIndex    LNNodesInHierarchy = 0;

//printf("%s() %s:%d  PSaveHierarchies %d\n", __FUNCTION__, __FILE__, __LINE__, PSaveHierarchies);fflush(stdout);
		if(PSaveHierarchies)
		{
// Count Nodes in this hierarchy or branch
//
			{ E3dNode*  LNode = LRootNode;for(;LNode != LNodeAfterBranch;LNode = LNode->Next) LNNodesInHierarchy++; }

			_AddInt32(LNNodesInHierarchy);

if(E3dDebugNODE)
{
	printf("%s() %s:%d  %sat %08llx%s LNNodesInHierarchy %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal, (int)LNNodesInHierarchy);
	fflush(stdout);
}
			E3dNode*  LNode = LRootNode;for(;LNode != LNodeAfterBranch;LNode = LNode->Next) 
			{
//printf("%s() %s:%d [%s]\n", __FUNCTION__, __FILE__, __LINE__, LNode->Name);fflush(stdout);
				const int  LR = _AddNode(PContext, P3DIORec, PFile, PHardWiredResources,  LNode, LNode == LRootNode ? NULL : LNode->Parent);
				if(LR < 0)
				{
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
					EFreeChk(LSavedNodes);
					return(-1);
				}

				EList_PointerAddAChk(LSavedNodes, 16, LNode);
			}
		}
		else
		{
			_AddInt32(1);  // Only one Node in this hierarchy
			const int  LR = _AddNode(PContext, P3DIORec, PFile, PHardWiredResources,  LRootNode, NULL);
			if(LR < 0)
			{
printf("%s() %s:%d  %sIMPLEMENT ME%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal);fflush(stdout);
				EFreeChk(LSavedNodes);
				return(-1);
			}

			EList_PointerAddAChk(LSavedNodes, 64, LRootNode);
		}
	}


// Add independent Nodes, e.g. Models of 3DWindow Cameras that are not instantiated in the Scene.
//
	if(PSavingScene)
	{
		EListAI(, E3dNode*, LNodes);

		{
			EIndex  LPI = 0;for(;LPI < E3dp_Panels.Count;LPI++)
			{
				E3dPanel*  LPanel = E3dp_Panels.Items[LPI];
				EIndex  LWI = 0;for(;LWI < LPanel->Windows.Count;LWI++)
				{
					E3dWindow*  L3DWindow = LPanel->Windows.Items[LWI];E3dpWindow*  L3DpWindow = E_DynamicCast(L3DWindow, E3dpWindow);if(L3DpWindow)
					{
#define _M_DoNode(m)\
do {\
	E3dNode*  lNode = (E3dNode*)(L3DpWindow->m##View->CameraModel);\
	if(EList_PointerGetIndex(LSavedNodes, lNode) < 0) EList_PointerAddAChk(LNodes, 16, lNode);\
} while(0)

						_M_DoNode(Perspective);
						_M_DoNode(Top);
						_M_DoNode(Front);
						_M_DoNode(Right);
						_M_DoNode(Schematics);
						_M_DoNode(Animation);
						_M_DoNode(Texture);

#undef _M_DoNode
					}
				}
			}
		}


		EFreeChk(LSavedNodes);

// Number of independent Node hierarchies
//
if(E3dDebugNODE)
{
	printf("\n%s() %s:%d  %sat %08llx%s Independent Nodes %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal, (int)LNodes_Count);fflush(stdout);
}
		_AddInt32((int)LNodes_Count);

		if(LNodes)
		{
			EIndex  LI = 0;for(;LI < LNodes_Count;LI++)
			{
				_AddInt32(1);  // Only one Node in this hierarchy
				const int  LR = _AddNode(PContext, P3DIORec, PFile, PHardWiredResources,  LNodes[LI], NULL);

				if(LR < 0) return(-1);
			}
			EFree(LNodes);
		}
	}
	else
	{
		EFreeChk(LSavedNodes);
		_AddInt32(0);  // Number of independent Node hierarchies = 0
	}

	return(0);
}


static void _CollectAndSaveMaterials(EResContext* PContext, E3dIORec* P3DIORec, FILE* PFile,  E3dMaterial** PMaterials, const int PCount,
 const char* PShaderChunkID,  const char* PMaterialChunkID)
{
	if(PMaterials == NULL) return;

	EListAI(, E3dShader*, LShaders);

// Collect and save Shaders
//
	if(PShaderChunkID)
	{
		{
			EIndex  LI = 0;for(;LI < PCount;LI++)
			{
				E3dMaterial*	LMaterial = PMaterials[LI];

				if(LMaterial->Shader)
				{
					EList_PointerAddAChk(LShaders, 16, LMaterial->Shader);
				}
			}
		}

		if(LShaders_Count)
		{
			int  LChunkLen = 0;

			{
				EIndex  LI = 0;for(;LI < LShaders_Count;LI++)
				{
					E3dShader*       LShader = LShaders[LI];
					E3dShaderClass*  LClass = LShader->Class;
					const size_t     LNameLen = strlen(LClass->Name) + 1;

					LChunkLen += LNameLen;
					const int  LIV = ERes_BinaryImageSize(PContext, &LClass->Resources, LShader);
					if(LIV > 0) LChunkLen += LIV;
				}
			}

			_ChunkHeaderSave(PFile, PShaderChunkID, LChunkLen, 0);
			EM_fwriteInt32_be(LShaders_Count, PFile);

			{
				EIndex  LI = 0;for(;LI < LShaders_Count;LI++)
				{
					E3dShader*       LShader = LShaders[LI];
					E3dShaderClass*  LClass = LShader->Class;
					const int        LNameLen = (int)strlen(LClass->Name) + 1;

					ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)LShader, FALSE);

					const int  LIV = ERes_ToBinaryImage(PContext, &LClass->Resources, LShader,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated,  NULL);

// Save shader class name
//
//printf("Saving class %s\n", LClass->Name);fflush(stdout);
					fwrite(LClass->Name, 1, LNameLen, PFile);
					if(LIV) fwrite(P3DIORec->ResourcesBinaryImage, 1, LIV, PFile);
				}
			}
		}
	}

	if(PCount)
	{
		if(PMaterialChunkID) _ChunkHeaderSave(PFile, PMaterialChunkID, 0, 0);

if(E3dDebugMATERIAL)
{
	printf("%s() %s:%d at %s%08llx%s  PCount %d\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal, PCount);fflush(stdout);
}
		EM_fwriteInt32_be((int)PCount, PFile);

		{
			EIndex  LMtC = 0;for(;LMtC < PCount;LMtC++)
			{
				E3dMaterial*  LMaterial = PMaterials[LMtC];

				EList_PointerAddAChk(P3DIORec->Materials, 64, LMaterial);

if(E3dDebugMATERIAL)
{
	printf("%s() %s:%d at %s%08llx%s  [%s%s%s]\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal, ECodeGreen, LMaterial->Name, ECodeNormal);
	fflush(stdout);
}
				ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)LMaterial, FALSE);
if(E3dDebugMATERIAL)
{
	printf("%s() %s:%d at %s%08llx%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal);fflush(stdout);
}

				{
					const int  LSize = ERes_ToBinaryImage(PContext, &E3dMaterial_resources, LMaterial,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated,  NULL);
					fwrite(P3DIORec->ResourcesBinaryImage, LSize, 1, PFile);
				}

if(0 && E3dDebugMATERIAL)
{
	printf("%s() %s:%d at %s%08llx%s  Shader\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal);fflush(stdout);
}
				if(PShaderChunkID)
				{
					EIndex  LIndex = EList_PointerGetIndex(LShaders, LMaterial->Shader);if(LIndex < 0) LIndex = -1;
					EM_fwriteInt32_be(LIndex, PFile);
				}
				else
				{
					fputc(CfMaterialSIMPLE, PFile);
if(E3dDebugMATERIAL)
{
	printf("%s() %s:%d at %s%08llx%s  Shader\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal);fflush(stdout);
}
					if(LMaterial->Shader)
					{
						const E3dShader*       LShader = LMaterial->Shader;
						const E3dShaderClass*  LShaderClass = LShader->Class;

						const int  LNameLen = (int)strlen(LShaderClass->Name) + 1;
						fwrite(LShaderClass->Name, 1, LNameLen, PFile);

						const int  LIV = ERes_ToBinaryImage(PContext, &LShaderClass->Resources, LShader,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated,  NULL);
						if(LIV) fwrite(P3DIORec->ResourcesBinaryImage, 1, LIV, PFile);
					}
				}

if(E3dDebugMATERIAL)
{
	printf("%s() %s:%d at %s%08llx%s  Shader\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)ftell(PFile), ECodeNormal);fflush(stdout);
}
				EM_fwriteInt32_be((int)LMaterial->Textures.Count, PFile);
				EIndex  LTxI = 0;const EIndex  LTxN = LMaterial->Textures.Count;for(;LTxI < LTxN;LTxI++)
				{
					E3d2DTexture*  LTexture = LMaterial->Textures.Items[LTxI];

					ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)LTexture, TRUE);

					{
						const int	 LSize = ERes_ToBinaryImage(PContext, &E3dTexture_resources, LTexture,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated,  NULL);
						fwrite(P3DIORec->ResourcesBinaryImage, LSize, 1, PFile);
					}

// Save TextureShader, if any
//
					if(LTexture->Shader && LTexture->Shader->Class)
					{
						E3d2DTextureShaderClass*  LClass = LTexture->Shader->Class;
						const int  LNameLen = (int)strlen(LClass->Name) + 1;
						fwrite(LClass->Name, 1, LNameLen, PFile);

						const int	 LSize = ERes_ToBinaryImage(PContext, &LClass->Resources, LTexture->Shader,
						 &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated,  NULL);
						fwrite(P3DIORec->ResourcesBinaryImage, LSize, 1, PFile);
					}
					else fputc(0, PFile);

					EM_fwriteInt32_be(LTexture->Blenders_Count, PFile);

					{
						E3dTextureBlender*  LBlender = LTexture->Blenders;
						EIndex  LBC = 0;const EIndex  LBN = LTexture->Blenders_Count;for(;LBC < LBN;LBC++, LBlender++)
						{
							EM_fwriteFloat32_be(LBlender->Factor, PFile);
							EM_fwriteUInt32_be(LBlender->Channels, PFile);
						}
					}

					{
						int32_t  LNImageFrames = LTexture->ImageFrames_Count;
						EM_fwriteUInt32_be(LNImageFrames, PFile);
						if(LNImageFrames > 0)
						{
							int  LI = 0;for(;LI < LNImageFrames;LI++)
							{
								const E3dImage*  LE3dImage = LTexture->ImageFrames[LI];
								EM_fwriteFloat64_be(LE3dImage->TimeStamp, PFile);
								if(LE3dImage->FileName) fwrite(LE3dImage->FileName, 1, strlen(LE3dImage->FileName) + 1, PFile);
								else fputc(0, PFile);
							}
						}
					}
				}
			}
		}
	}

	EFreeChk(LShaders);
}


//
// E3dTODO : use a buffer for a VertexNode and do a single fwrite per VertexNode, or for every N VertexNode
//
#define _M_AddVertexNode(pVertexNode, pVertexAttributes, pNVertexAttributes) do\
{\
	const EIndex  lIndex = pVertexNode->VertexID;switch(PVertexIndexBits)\
	{\
		case 8:   *LPtr++ = lIndex;break;\
		case 16:  EM_PutInt16I_be(lIndex, LPtr);break;\
		case 32:  EM_PutInt32I_be(lIndex, LPtr);break;\
	}\
\
	if(lIndex >= 0)\
	{\
		EIndex  lVAC = 0;const EResource*  lVA = pVertexAttributes;for(;lVAC < pNVertexAttributes;lVAC++, lVA++)\
		{\
			const E3dVertexSize  lOffset = LVAOffsets[lVAC];\
			switch(lVA->Type)\
			{\
				case EResFLOAT32:\
					{\
if(0)\
{\
	printf("%s() %s:%d  %s%08llx%s  %sFLOAT32%s  %s %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - LOutBuffer), ECodeNormal,\
	 ECodeGreen, ECodeNormal, (char*)lVA->Semantic, (long long)lVA->Ext.Count);\
	fflush(stdout);\
}\
						const float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
						EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI++) EM_PutFloat32I_be(LArray[lI], LPtr);\
					}\
				break;\
\
				case EResFLOAT32_2:\
					{\
if(0)\
{\
	printf("%s() %s:%d  %s%08llx%s  %sFLOAT32_2%s  %s %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - LOutBuffer), ECodeNormal,\
	 ECodeGreen, ECodeNormal, (char*)lVA->Semantic, (long long)lVA->Ext.Count);\
	fflush(stdout);\
}\
						const float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
						EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI += 2) { EM_PutFloat32I_be(LArray[lI], LPtr);EM_PutFloat32I_be(LArray[lI + 1], LPtr); }\
					}\
				break;\
\
				case EResFLOAT32_3:\
					{\
if(0)\
{\
	printf("%s() %s:%d  %s%08llx%s %sFLOAT32_3%s  %s %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - LOutBuffer), ECodeNormal,\
	 ECodeGreen, ECodeNormal, (char*)lVA->Semantic, (long long)lVA->Ext.Count);\
	fflush(stdout);\
}\
						const float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
						EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI += 3)\
						{ EM_PutFloat32I_be(LArray[lI], LPtr);EM_PutFloat32I_be(LArray[lI + 1], LPtr);EM_PutFloat32I_be(LArray[lI + 2], LPtr); }\
					}\
				break;\
\
				case EResFLOAT32_4:\
					{\
if(0)\
{\
	printf("%s() %s:%d  %s%08llx%s  %sFLOAT32_4%s %s %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, (long long)(LPtr - LOutBuffer), ECodeNormal,\
	 ECodeGreen, ECodeNormal, (char*)lVA->Semantic, (long long)lVA->Ext.Count);\
	fflush(stdout);\
}\
						const float*  LArray = (float*)((uint8_t*)pVertexNode + lOffset);\
						EIndex  lI = 0;const EIndex  lN = lVA->Ext.Count;for(;lI < lN;lI += 4)\
						{ EM_PutFloat32I_be(LArray[lI], LPtr);EM_PutFloat32I_be(LArray[lI + 1], LPtr);EM_PutFloat32I_be(LArray[lI + 2], LPtr);EM_PutFloat32I_be(LArray[lI + 3], LPtr); }\
					}\
				break;\
\
				case EResSTRUCT:\
if(0)\
{\
	printf("%s() %s:%d  %s%08llx%s %sSTRUCT%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeCyan, (long long)(LPtr - LOutBuffer), ECodeNormal,  ECodeGreen, ECodeNormal);\
	fflush(stdout);\
}\
					if(lVA->Semantic == E3dVtxAttribute_ST)\
					{\
/*printf("%s() %s:%d  %sST%s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, ECodeNormal);fflush(stdout);*/\
						const E3dST*  lST = (E3dST*)((uint8_t*)pVertexNode + lOffset);\
						EM_PutFloat32I_be(lST->S, LPtr);\
						EM_PutFloat32I_be(lST->T, LPtr);\
/*\
						EM_PutFloat32I_be(lST->Tangent.X, LPtr);\
						EM_PutFloat32I_be(lST->Tangent.Y, LPtr);\
						EM_PutFloat32I_be(lST->Tangent.Z, LPtr);\
*/\
					}\
					else\
					{\
						char  s[256];printf("%s() %s:%d  %sIMPLEMENT ME%s Type 0x%04x (%s)\n", __FUNCTION__, __FILE__, __LINE__, ECodeYellow, ECodeNormal,\
						 lVA->Type, ERes_TypeToString(lVA->Type, s, 256));fflush(stdout);\
					}\
				break;\
\
				default:\
					{\
						char  s[256];printf("%s() %s:%d  %sIMPLEMENT ME%s Type 0x%04x (%s)\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeYellow, ECodeNormal,\
						 lVA->Type, ERes_TypeToString(lVA->Type, s, 256));fflush(stdout);\
					}\
				break;\
			}\
		}\
	}\
} while(0)


//================================================================================
// Save core PolyGroup data such as primitive list and VertexAttribute info
//================================================================================
static void _PolyGroupCoreSave(EResContext* PContext, E3dIORec* P3DIORec, FILE* PFile, const E3dVertexGeometry* PVertexGeometry, const E3dPolyGroup* PPolyGroup,
 const uint16_t PPolygonContentFlags, const uint8_t PVertexIndexBits)
{
	if(PPolyGroup == NULL) return;

if(E3dDebugPOLYGROUP) { printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)ftell(PFile));fflush(stdout); }
	_ObjectSaveAllResources(PContext, P3DIORec, PFile,  (ECoreObject*)PPolyGroup);

	const E3dVertexNodeSize  LVertexNodeSize = PPolyGroup->VertexNodeSize;

	{
		EIndex  LI = EList_PointerGetIndex(P3DIORec->Materials, (void*)(PPolyGroup->Material));if(LI < 0) LI = -1;EM_fwriteInt32_be(LI, PFile);

		const EIndex  LVAN = PPolyGroup->VertexAttributes.Count;
		const EIndex  LNCustomVertexAttributes = LVAN - E3dPolyGroup_DefaultVertexAttributes_Count >= 0 ? LVAN - E3dPolyGroup_DefaultVertexAttributes_Count: 0;

//		const EResource*  LVAs = PPolyGroup->VertexAttributes.Items + E3dPolyGroup_DefaultVertexAttributes_Count;const EIndex  LNVAs = LNCustomVertexAttributes;

// Pre-filter the VertexAttribute list, to make the per-VertexNode loop faster
//
		EResource*  LVAs;ENewN(LNCustomVertexAttributes, LVAs);EIndex  LNVAs = 0;

		E3dVertexSize*  LVAOffsets;ENewN(LNCustomVertexAttributes, LVAOffsets);

		{
			EIndex  LVAI = 0;const EResource*  LVA = PPolyGroup->VertexAttributes.Items + E3dPolyGroup_DefaultVertexAttributes_Count;EResource*  LDst = LVAs;
			for(;LVAI < LNCustomVertexAttributes;LVAI++, LVA++)
			{
				if(LVA->Semantic != E3dVtxAttribute_EdgeIndex)
				{
					LVAOffsets[LNVAs] = sizeof(E3dVertexNodeCore) + EResource_OffsetByIndex(&PPolyGroup->VertexAttributes,  LVAI);
					*LDst++ = *LVA;LNVAs++;
				}
			}
		}

		EM_fwriteUInt16_be(LNVAs, PFile);

if(E3dDebugPOLYGROUP)
{
	printf("%s() %s:%d  <%s%s%s> VertexAttributes %lld  CustomVA %lld  LNVA %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, PPolyGroup->Name, ECodeNormal,
	 (long long)LVAN, (long long)LNCustomVertexAttributes, (long long)LNVAs);fflush(stdout);
}

		if(LNVAs > 0)
		{
			EIndex  LVAI = 0;const EResource*  LVA = LVAs;for(;LVAI < LNVAs;LVAI++, LVA++)
			{
				_Cf_AddString(LVA->Semantic, PFile);
				_Cf_AddString(LVA->Name, PFile);
				EM_fwriteUInt16_be(LVA->Type, PFile);
				EM_fwriteUInt16_be(LVA->Ext.Count, PFile);
if(E3dDebugPOLYGROUP) { printf("%s() %s:%d    %s%s%s %s\n", __FUNCTION__, __FILE__, __LINE__, ECodeGreen, (char*)LVA->Semantic, ECodeNormal, LVA->Name);fflush(stdout); }
			}
if(E3dDebugPOLYGROUP) { printf("\n");fflush(stdout); }
		}
		else LVAOffsets = NULL;


// Save Polygons
//
ETime  LPrevTime = E_GetMicroseconds();
		{
			const EIndex  LPN = PPolyGroup->Polygons.Count;EM_fwriteUInt32_be(LPN, PFile);if(LPN > 0)
			{
				const EIndex  LPrimBaseSize = sizeof(float) * 3 + 4 + 4;  // Polygon normal, flags, NumOfVertexNodes
				EIndex        LVNMax, LNPrimsPerWrite = 32;

				{
					EIndex  LPI = 1;const E3dPolygon*  LPolygon = PPolyGroup->Polygons.Items;LVNMax = LPolygon->NumOfVertexNodes;LPolygon++;for(;LPI < LPN;LPI++, LPolygon++)
					{
						const EIndex  LVN = LPolygon->NumOfVertexNodes;
						if(LVNMax < LVN) LVNMax = LVN;
					}
				}

				uint8_t*  LOutBuffer = EMalloc((LPrimBaseSize + LVertexNodeSize * LVNMax) * LNPrimsPerWrite);
				EIndex    LOutBufferSize = (LPrimBaseSize + LVertexNodeSize * LVNMax) * LNPrimsPerWrite;
				uint8_t*  LPtr = LOutBuffer;

				{
					EIndex  LPI = 0;const E3dPolygon*  LPolygon = PPolyGroup->Polygons.Items;for(;LPI < LPN;LPI++, LPolygon++)
					{
						const int     LVN = LPolygon->NumOfVertexNodes;
						const EIndex  LSize = LPrimBaseSize + LVertexNodeSize * LVN;
						const EIndex  LBytesRemainingInOutBuffer = LOutBufferSize - (LPtr - LOutBuffer);
						if(LBytesRemainingInOutBuffer < LSize) { fwrite(LOutBuffer, 1, LPtr - LOutBuffer, PFile);LPtr = LOutBuffer; }

// Save Polygon normal
//
if(E3dDebugPOLYGON)
{
//printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)ftell(PFile) + (long long)(LPtr - LOutBuffer));fflush(stdout);
	printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - LOutBuffer));fflush(stdout);
}
						if(PPolygonContentFlags & E3dPolygonCfNORMAL)
						{
							EM_PutFloat32I_be(LPolygon->Normal.X, LPtr);
							EM_PutFloat32I_be(LPolygon->Normal.Y, LPtr);
							EM_PutFloat32I_be(LPolygon->Normal.Z, LPtr);
						}
if(E3dDebugPOLYGON) { printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - LOutBuffer));fflush(stdout); }
						EM_PutUInt16I_be(LPolygon->Flags, LPtr);
if(E3dDebugPOLYGON) { printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - LOutBuffer));fflush(stdout); }

						switch(PVertexIndexBits)
						{
							case 8:   *LPtr++ = LVN;break;
							case 16:  EM_PutUInt16I_be(LVN, LPtr);break;
							case 32:  EM_PutUInt32I_be(LVN, LPtr);break;
						}
if(E3dDebugPOLYGON) { printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - LOutBuffer));fflush(stdout); }

						EIndex  LVI = 0;const E3dVertexNode*  LVertexNode = LPolygon->VertexNodes;for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize))
						{
if(E3dDebugPOLYGON) { printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - LOutBuffer));fflush(stdout); }
							_M_AddVertexNode(LVertexNode,  LVAs, LNVAs);
if(E3dDebugPOLYGON) { printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)(LPtr - LOutBuffer));fflush(stdout); }
						}
					}
					fwrite(LOutBuffer, 1, LPtr - LOutBuffer, PFile);
				}
				EFree(LOutBuffer);
			}
		}
{
	const ETime  LNow = E_GetMicroseconds();char  s[256];if(LNow - LPrevTime > 10000)
	{
		printf("%s() %s:%d  <%s%s%s>/<%s%s%s>%s%s%s\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeCyan, PVertexGeometry->Name, ECodeNormal,  ECodeGreen, PPolyGroup->Name, ECodeNormal,
		 ECodeGreen, EStr_MicrosecondsToString(LNow - LPrevTime, s, TRUE), ECodeNormal);
		fflush(stdout);
		LPrevTime = LNow;
	}
}

// Save TriangleStrips
//
		{
			const EIndex  LPN = PPolyGroup->TriangleStrips.Count;
			EM_fwriteUInt32_be(LPN, PFile);
			if(LPN > 0)
			{
if(E3dDebugTRIANGLE_STRIP)
{
	printf("%s() %s:%d  %sTriStrips%s %lld  PVertexIndexBits %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LPN, (long long)PVertexIndexBits);
	fflush(stdout);
}
				const EIndex  LPrimBaseSize = 8;  // NumOfVertices
				EIndex        LVNMax, LNPrimsPerWrite = 32;

				{
					EIndex  LPI = 1;const E3dTriangleStrip*  LTriangleStrip = PPolyGroup->TriangleStrips.Items;LVNMax = LTriangleStrip->NumOfVertices;LTriangleStrip++;for(;LPI < LPN;LPI++, LTriangleStrip++)
					{
						const EIndex  LVN = LTriangleStrip->NumOfVertices;
						if(LVNMax < LVN) LVNMax = LVN;
					}
				}

				uint8_t*  LOutBuffer = EMalloc((LPrimBaseSize + LVertexNodeSize * LVNMax) * LNPrimsPerWrite);if(LOutBuffer)
				{
					EIndex    LOutBufferSize = (LPrimBaseSize + LVertexNodeSize * LVNMax) * LNPrimsPerWrite;
					uint8_t*  LPtr = LOutBuffer;
					EIndex  LPI = 0;const E3dTriangleStrip*  LTriangleStrip = PPolyGroup->TriangleStrips.Items;for(;LPI < LPN;LPI++, LTriangleStrip++)
					{
						const int  LVN = LTriangleStrip->NumOfVertices;

						const EIndex  LSize = LPrimBaseSize + LVertexNodeSize * LVN;
						const EIndex  LBytesRemainingInOutBuffer = LOutBufferSize - (LPtr - LOutBuffer);
						if(LBytesRemainingInOutBuffer < LSize) { fwrite(LOutBuffer, 1, LPtr - LOutBuffer, PFile);LPtr = LOutBuffer; }

if(E3dDebugTRIANGLE_STRIP)
{
	printf("%s() %s:%d  %sTriStrip%s %4lld  Verts %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LPI, (long long)LVN);fflush(stdout);
}

						switch(PVertexIndexBits)
						{
							case 8:   *LPtr++ = LVN;break;
							case 16:  EM_PutUInt16I_be(LVN, LPtr);break;
							case 32:  EM_PutUInt32I_be(LVN, LPtr);break;
						}

						EIndex  LVI = 0;const E3dVertexNode*	LVertexNode = LTriangleStrip->VertexNodes;for(;LVI < LVN;LVI++, EStruct_Inc(LVertexNode, LVertexNodeSize))
						{
if(E3dDebugTRIANGLE_STRIP)
{
	printf("%s() %s:%d    %sVertex%s %4lld  %lld\n",  __FUNCTION__, __FILE__, __LINE__,  ECodeGreen, ECodeNormal, (long long)LVI, (long long)LVertexNode->VertexID);fflush(stdout);
}
							_M_AddVertexNode(LVertexNode,  LVAs, LNVAs);
						}
					}
					fwrite(LOutBuffer, 1, LPtr - LOutBuffer, PFile);
					EFree(LOutBuffer);
				}
			}
		}
	}


//printf("%s() %s:%d at %08llx\n", __FUNCTION__, __FILE__, __LINE__, (long long)ftell(PFile));fflush(stdout);

// Save TextureMappers
//
	if(PPolyGroup->TextureMappers.Count > 0)
	{
		int  LTotalSize = 0;
		{
			const int  LN = PPolyGroup->TextureMappers.Count;

			{
				int  LI = 0;for(;LI < LN;LI++)
				{
					E3dTextureMapper*       LMapper = PPolyGroup->TextureMappers.Items[LI];
					E3dTextureMapperClass*  LClass = LMapper->Class;
					const int               LNameLen = strlen(LClass->Name) + 1;

					if(LClass->Resources.Count > 0)
					{
						const int  LIV = ERes_BinaryImageSize(PContext, &LClass->Resources, LMapper);
						if(LIV > 0) LTotalSize += LNameLen + LIV;
					}
					else LTotalSize += LNameLen;
				}
			}

			EM_fwriteUInt32_be(LTotalSize, PFile);

			{
				int  LI = 0;for(;LI < LN;LI++)
				{
					E3dTextureMapper*       LMapper = PPolyGroup->TextureMappers.Items[LI];
					E3dTextureMapperClass*  LClass = LMapper->Class;
					const int               LNameLen = strlen(LClass->Name) + 1;

//printf("Saving TextureMapper %s\n", LClass->Name);fflush(stdout);
					if(LClass->Resources.Count > 0)
					{
						const int  LIV = ERes_ToBinaryImage(PContext, &LClass->Resources, LMapper, &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated, NULL);
						LTotalSize += LNameLen + LIV;

// Save class name
//
						fwrite(LClass->Name, 1, LNameLen, PFile);
						if(LIV) fwrite(P3DIORec->ResourcesBinaryImage, 1, LIV, PFile);
					}
					else
					{
						LTotalSize += LNameLen;
						fwrite(LClass->Name, 1, LNameLen, PFile);
					}
				}
			}
		}
	}
	else EM_fwriteUInt32_be(0, PFile);

// Save TextureMapperRefs
//
	{
		const int  LN = PPolyGroup->TextureMapperRefs.Count;EM_fwriteUInt16_be(LN, PFile);
		EIndex  LI = 0;for(;LI < LN;LI++)
		{
			E3dTextureMapper*  LMapper = PPolyGroup->TextureMapperRefs.Items[LI];
			int  LIndex = EList_PointerGetIndex_s(PPolyGroup->TextureMappers, LMapper);if(LIndex >= 0)
			{
				EM_fwriteUInt16_be(LIndex, PFile);
			}
			else
			{
				LIndex = EList_PointerGetIndex_s(PVertexGeometry->TextureMappers, LMapper);if(LIndex >= 0)
				{
					const uint16_t  LV = (uint16_t)LIndex | E3dTextureMapperFROM_MESH;
					EM_fwriteUInt16_be(LV, PFile);
				}
				else EM_fwriteUInt16_be(0, PFile);
			}
		}
	}
}


static void _AnimationSave(EResContext* PContext, E3dIORec* P3DIORec, FILE* PFile,  E3dAnimation* PAnimation)
{
	ERes_AddObjectFlagsAndID(PFile, PContext, (ECoreObject*)PAnimation, TRUE);

	const E3dAnimationClass*  LClass = PAnimation->Class;
	_Cf_AddString(LClass->Name, PFile);
	const long  LSizePos = ftell(PFile);EM_fwriteInt32_be(0, PFile);  // Save 0 for size here, then seek back and set it once we know the actual size

	{
		const int  LIV = EObject_ResourcesToBinaryImage((ECoreObject*)PAnimation, PContext,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated, NULL);
		if(LIV > 0) fwrite(P3DIORec->ResourcesBinaryImage, 1, LIV, PFile);
	}

	if(PAnimation->Removable)
	{
		EM_fwriteInt32_be(PAnimation->Channels_Count, PFile);
		const EResourceList*  LChannelResources = &LClass->ChannelResources;

if(E3dDebugANIMATION)
{
	printf("%s() %s:%d  %08llx %s  Channels: %llx  classes:%lld\n",  __FUNCTION__, __FILE__, __LINE__,  (long long)ftell(PFile), PAnimation->Class->Name,
	 (long long)PAnimation->Channels_Count, (long long)PContext->Classes.Count);
	fflush(stdout);
}
		int  LCC = 0;const int	LCN = PAnimation->Channels_Count;for(;LCC < LCN;LCC++)
		{
			E3dAnimationChannel*  LChannel = PAnimation->Channels[LCC];

if(E3dDebugANIMATION)
{
	ECoreObject*  LTargetObject = LChannel->TargetObject;ECoreObject*  LTargetSubObject = LChannel->TargetSubObject;
	printf("%s() %s:%d    LChannel %p  TargetObject [%s%p%s:(%s):%s]  TargetSubObject [%s%p%s:(%s):%s]\n",  __FUNCTION__, __FILE__, __LINE__,  LChannel,
	 ECodeCyan, LTargetObject, ECodeNormal, LTargetObject ? LTargetObject->Class->Name : "", LTargetObject ? LTargetObject->Name : "",
	 ECodeCyan, LTargetSubObject, ECodeNormal, LTargetSubObject ? LTargetSubObject->Class->Name : "", LTargetSubObject ? LTargetSubObject->Name : "");
	fflush(stdout);
}

//		const int	LIV = EObject_ResourcesToBinaryImage(ECoreObject* LObject, PContext, TRUE,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated, NULL);

			{
				const uint16_t  LFormatFlags = PContext->FormatFlags;PContext->FormatFlags = EResCLASS_NAME|EResNAMES|EResTYPES;
				const int	LIV = ERes_ToBinaryImage(PContext, LChannelResources, LChannel,  &P3DIORec->ResourcesBinaryImage, &P3DIORec->ResourcesBinaryImageBytesAllocated, NULL);
				PContext->FormatFlags = LFormatFlags;
				if(LIV > 0) fwrite(P3DIORec->ResourcesBinaryImage, 1, LIV, PFile);

if(E3dDebugANIMATION)
{
	printf(" LTargetObject %lld  LTargetSubObject %lld  LBinarySize %d\n", (long long)EList_PointerGetIndex_s(PContext->ReferencedObjects, LChannel->TargetObject),
	 (long long)EList_PointerGetIndex_s(PContext->ReferencedObjects, LChannel->TargetSubObject), LIV);
	fflush(stdout);
}
			}
		}
	}

// Seek back and set the size of this Animation
//
	const long  LPos = ftell(PFile), LSize = LPos - LSizePos - 4;
	fseek(PFile, LSizePos, SEEK_SET);EM_fwriteInt32_be(LSize, PFile);
	fseek(PFile, LPos, SEEK_SET);
}
