/*********************************************************************
 *                  Flowcode CAL String File
 *
 * File: PIC_CAL_String.c
 *
 * (c) 2011 Matrix Multimedia Ltd.
 * http://www.matrixmultimedia.com
 *
 * Software License Agreement
 *
 * The software supplied herewith by Matrix Multimedia Ltd (the
 * “Company”) for its Flowcode graphical programming language is
 * intended and supplied to you, the Company’s customer, for use
 * solely and exclusively on the Company's products. The software
 * is owned by the Company, and is protected under applicable
 * copyright laws. All rights are reserved. Any use in violation
 * of the foregoing restrictions may subject the user to criminal
 * sanctions under applicable laws, as well as to civil liability
 * for the breach of the terms and conditions of this licence.
 *
 * THIS SOFTWARE IS PROVIDED IN AN “AS IS” CONDITION. NO WARRANTIES,
 * WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED
 * TO, IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE APPLY TO THIS SOFTWARE. THE COMPANY SHALL NOT,
 * IN ANY CIRCUMSTANCES, BE LIABLE FOR SPECIAL, INCIDENTAL OR
 * CONSEQUENTIAL DAMAGES, FOR ANY REASON WHATSOEVER.
 *
 * Changelog:
 *
 *  date  | by | description
 * -------+----+-----------------------------------------------------
 * 010911 | BR | Created
 * 150911 | JW | Added v5 string functions
 * 270212 | BR | Fixed an issue with ToUpper where the 1st character was being returned as null
 * 120312 | BR | Fixed an issue with some of the float functions not using CAL functions
 * 250912 | BR | Fixed an issue with the ToString function which caused the contents of the input variable to be cleared to 0
 */


// Useful call for finding the length of an (uncorrupted) string
#define FCI_GETLENGTH__x(str, max, idx)         for (idx = 0; idx < max; idx++) if (str[idx] == 0) break
#define FCI_STRMOV__x(str, max, dst, dlen, ix)  for (ix = 0; ix < (max) && ix < (dlen) && str[ix] != '\0'; ix++) dst[ix] = str[ix]
#define FCI_CONCATENATE(s0, l0, s1, l1, sd, ld) FCI_SHEAD(s0, l0, s1, l1, sd, ld)


#ifndef MX_USES_UINT32
	#define MX_USES_UINT32 0
#endif
#ifndef MX_USES_SINT32
	#define MX_USES_SINT32 0
#endif


#if (MX_USES_UINT32 || MX_USES_SINT32)
	#define MX_STRF_LONG
	#define MX_ULONG MX_UINT32
	#define MX_SLONG MX_SINT32
#else
	#define MX_ULONG MX_UINT16
	#define MX_SLONG MX_SINT16
#endif


//String function definitions
MX_UINT8 FCI_GETCHAR(MX_STRING sStr1, MX_UINT8 iStr1_len, MX_UINT8 iPos);
MX_UINT8 FCI_GETLENGTH(MX_STRING sStr1, MX_UINT8 iStr1_len);
MX_UINT8 FCI_MIDSTRING(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_UINT8 iStart, MX_UINT8 iCount, MX_STRING sDst, MX_UINT8 iDst_len);
MX_UINT8 FCI_LEFTSTRING(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_UINT8 iCount, MX_STRING sDst, MX_UINT8 iDst_len);
MX_UINT8 FCI_RIGHTSTRING(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_UINT8 iCount, MX_STRING sDst, MX_UINT8 iDst_len);
MX_UINT8 FCI_TOSTRING(MX_SLONG iSrc1, MX_STRING sDst, MX_UINT8 iDst_len);
void FCI_TOLOWER(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len);
void FCI_TOUPPER(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len);
MX_UINT8 FCI_COMPARE(MX_STRING sSrc1, MX_UINT8 iSrc1_len, MX_STRING sSrc2, MX_UINT8 iSrc2_len, MX_UINT8 iNoCase);
MX_UINT8 FCI_FLOAT_TO_STRING(MX_FLOAT Number, MX_UINT8 Precision, MX_STRING String, MX_UINT8 MSZ_String);
MX_UINT8 FCI_NUMBER_TO_HEX(MX_ULONG Number, MX_STRING String, MX_UINT8 MSZ_String);
MX_SINT32 FCI_STRING_TO_INT(MX_STRING String, MX_UINT8 MSZ_String);
MX_FLOAT FCI_STRING_TO_FLOAT(MX_STRING String, MX_UINT8 MSZ_String);

// Functions for contatenation
MX_UINT8 FCI_STRREV(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len);
MX_UINT8 FCI_STRMOV(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len);
void FCI_SHEAD(MX_STRING sSrc1, MX_UINT8 iSrc1_len, MX_STRING sSrc2, MX_UINT8 iSrc2_len, MX_STRING sDst, MX_UINT8 iDst_len);
void FCI_SCOPY(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len);



MX_UINT8 FCI_GETCHAR(MX_STRING sStr1, MX_UINT8 iStr1_len, MX_UINT8 iPos)
{
	MX_UINT8 tmp;
	FCI_GETLENGTH__x(sStr1, iStr1_len, tmp);

	if(iPos < tmp)
		return sStr1[iPos];
	return '\0';
}

MX_UINT8 FCI_GETLENGTH(MX_STRING sStr1, MX_UINT8 iStr1_len)
{
	MX_UINT8 tmp;
	FCI_GETLENGTH__x(sStr1, iStr1_len, tmp);
	return (tmp);
}

MX_UINT8 FCI_MIDSTRING(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_UINT8 iStart, MX_UINT8 iCount, MX_STRING sDst, MX_UINT8 iDst_len)
{
	if (iDst_len == 0) return 0;
	MX_UINT8 idx;
	FCI_GETLENGTH__x(sSrc, iSrc_len, idx);	//find the length of the source string

	if (iStart >= idx)
	{
		*sDst = '\0';						//starting position is beyond string length, so return a null string
	}
	else
	{
		if ((iStart + iCount) >= idx)		//Make sure the required length is not too big
			iCount = idx - iStart;
		if (iCount > (iDst_len))			//make sure the required length is not too big
			iCount = (iDst_len);

		sSrc += iStart;						//Move to the correct place in the source string

		for (idx = 0; idx < iCount; idx++)	//Copy the contents of the source string
		{
			*sDst = *sSrc;
			sDst++;
			sSrc++;
		}
		if (idx < iDst_len)						//add terminating null (if we can)
			*sDst = '\0';
	}
	return (idx);
}

MX_UINT8 FCI_LEFTSTRING(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_UINT8 iCount, MX_STRING sDst, MX_UINT8 iDst_len)
{
	if (iDst_len == 0) return 0;
	MX_UINT8 idx;
	FCI_GETLENGTH__x(sSrc, iSrc_len, idx);	//find the length of the source string

	if (iCount > idx)						//make sure the required length is not too big
		iCount = idx;
	if (iCount > (iDst_len))			//make sure the required length is not too big
		iCount = (iDst_len);

	for (idx = 0; idx < iCount; idx++)		//copy the required MX_UINT8acters
	{
		*sDst = *sSrc;
		sDst++;
		sSrc++;
	}
	if (idx < iDst_len)						//add terminating null (if we can)
		*sDst = '\0';
	return (idx);
}

MX_UINT8 FCI_RIGHTSTRING(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_UINT8 iCount, MX_STRING sDst, MX_UINT8 iDst_len)
{
	if (iDst_len == 0) return 0;
	MX_UINT8 idx;
	MX_UINT8 delta;

	FCI_GETLENGTH__x(sSrc, iSrc_len, idx);	//find the length of the source string

	if (iCount > idx)						//make sure the required length is not too big
		iCount = idx;
	delta = idx - iCount;					//get the offset
	if (iCount > (iDst_len))				//make sure the required length is not too big
		iCount = (iDst_len);
	sSrc += delta;							//Move to the correct place in the source string

	for(idx = 0; idx < iCount; idx++)		//copy the required MX_UINT8acters
	{
		*sDst = *sSrc;
		sDst++;
		sSrc++;
	}
	if (idx < iDst_len)						//add terminating null (if we can)
		*sDst = '\0';
	return (idx);
}

MX_UINT8 FCI_TOSTRING(MX_SLONG iSrc1, MX_STRING sDst, MX_UINT8 iDst_len)
{
	MX_UINT8 tmp1;

	MX_SLONG iSrc = iSrc1;

	#ifdef MX_STRF_LONG
		unsigned long top = 1000000000; 	// Maximum power of 10 less than 4294967296
	#else
		unsigned short top = 10000; 		// Maximum power of 10 less than 65536
	#endif

	if (iDst_len == 0) return 0;

	MX_UINT8 idx = 0;

	//copy source 1 into destination:
	//minus sign
	if (iSrc < 0)
	{
		sDst[0] = '-';
		idx++;
		iSrc = -iSrc;
	}

	tmp1 = 0;    // Nothing added yet
	if (iSrc == 0) // Force showing zero
	{
		tmp1 = 1;
		top = 1;
	}
	while (( idx < iDst_len) && (top > 0))
	{
		#ifdef MX_STRF_LONG
		if (((unsigned long)iSrc >= top) || (tmp1))
		#else
		if (((unsigned)iSrc >= top) || (tmp1))
		#endif
		{
			#ifdef MX_STRF_LONG
			tmp1 = (unsigned long)iSrc / top;	// Top digit
			#else
			tmp1 = (unsigned)iSrc / top;	// Top digit
			#endif
			sDst[idx] = '0' + tmp1;			// Add digit to buffer
			idx++;
			iSrc -= tmp1 * top;				// Remove digit
			tmp1 = 1;						// Add zeros now
		}
		top /= 10;							// Go to next digit
	}

	if (idx < iDst_len)						//add terminating null (if we can)
		sDst[idx] = '\0';
	return (idx);
}



void FCI_TOLOWER(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len)
{
	if (iDst_len == 0) return;
	MX_UINT8 idx;
	MX_UINT8 ch;

	if (iSrc_len < iDst_len) iSrc_len = iDst_len; // Bound so cant overflow dest
	//copy source into destination and change to lower case
	for (idx=0; idx<iSrc_len; idx++)
	{
		ch = sSrc[idx];
		if ((ch >= 'A') && (ch <= 'Z'))
		{
			ch ^= 0x20;
		}
		sDst[idx] = ch;
		if (ch == 0)
		{
			break;
		}
	}
	if (idx < iDst_len)						//add terminating null (if we can)
		sDst[idx] = '\0';
}

void FCI_TOUPPER(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len)
{
	if (iDst_len == 0) return;
	MX_UINT8 idx;
	MX_UINT8 ch;

	if (iSrc_len < iDst_len) iSrc_len = iDst_len; // Bound so cant overflow dest
	//copy source into destination and change to upper case
	for (idx=0; idx<iSrc_len; idx++)
	{
		ch = sSrc[idx];
		if ((ch >= 'a') && (ch <= 'z'))
		{
			ch ^= 0x20;
		}
		sDst[idx] = ch;
		if (ch == 0)
		{
			break;
		}
	}
	if (idx < iDst_len)						//add terminating null (if we can)
		sDst[idx] = '\0';
}

MX_UINT8 FCI_COMPARE(MX_STRING sSrc1, MX_UINT8 iSrc1_len, MX_STRING sSrc2, MX_UINT8 iSrc2_len, MX_UINT8 iNoCase)
{
	MX_UINT8 iRetVal = 0;
	MX_UINT8 idx;
	MX_UINT8 ch1, ch2;

	for (idx=0; idx < iSrc1_len; idx++)	//compare a byte from the strings
	{
		if (idx < iSrc2_len)
		{
			ch1 = *sSrc1;
			ch2 = *sSrc2;

			if (iNoCase)
			{
				if ((ch1 >= 'a') && (ch1 <= 'z'))
					ch1 = (ch1 & 0xDF);

				if ((ch2 >= 'a') && (ch2 <= 'z'))
					ch2 = (ch2 & 0xDF);

			}

			if (ch1 < ch2)
			{
				return(255);
			}
			else if (ch1 > ch2)
			{
				return(1);
			}

			sSrc1++;
			sSrc2++;
		}
		else
		{
			if (*sSrc1 == 0)
				return (0);				//end of source1 as well, so ok
			else
				return (1);				//reached the end of iSrc2, but still more of iSrc1
		}
	}

	if (iSrc1_len == iSrc2_len)			//reached the end of iSrc1.  If we're also at the end of iSrc2, then return 0
		return (0);
	else
	{
		if (*sSrc2 == 0)
			return (0);					//end of source1 as well, so ok
		else
			return (255);				//not at end of source2, so return -1
	}
}


MX_UINT8 FCI_FLOAT_TO_STRING(MX_FLOAT Number, MX_UINT8 Precision, MX_STRING String, MX_UINT8 MSZ_String)
{
	MX_SINT16 whole;
	MX_UINT8 str_length;
	MX_UINT8 idx;
	MX_UINT8 stringidx = 0;
	MX_FLOAT real, temp;
	MX_UINT8 old;
	MX_UINT8 temp_string[10];

	#ifdef _BOOSTC									//BoostC Floating Point

		float_rounding_mode = 3;					//Always round to zero
		whole = float32_to_int32(Number);			//Convert to whole integer
		temp = float32_from_int32(whole);			//Convert whole number back to floating point

		if (float32_ge(Number,0))
		{
			real = float32_sub(Number, temp);		//Subtract whole number from floating point number
		}
		else
		{
			if (whole == 0)							//Fix -0 bug
			{
				String[stringidx] = '-';
				stringidx = stringidx + 1;
			}

			real = float32_sub(temp, Number);		//Subtract floating point number from whole number
		}
	#endif

	#ifdef HI_TECH_C								//HiTECH Floating Point

		whole = (int)Number;						//Convert to whole integer
		temp = whole;								//Convert whole number back to floating point

		if (Number >= 0)
		{
			real = Number - temp;					//Subtract whole number from floating point number
		}
		else
		{
			if (whole == 0)							//Fix -0 bug
			{
				String[stringidx] = '-';
				stringidx = stringidx + 1;
			}

			real = temp - Number;					//Subtract floating point number from whole number
		}
	#endif

	str_length = FCI_TOSTRING(whole, temp_string, 5);		//Convert integer numbers to strings

	for (idx=0; idx<str_length; idx++)						//Copy whole part of number to string
	{
		if(stringidx < MSZ_String)
		{
			String[stringidx] = temp_string[idx];
			stringidx = stringidx + 1;
		}
	}

	if(stringidx < MSZ_String)						//Add decimal point to string
	{
		String[stringidx] = '.';
		stringidx = stringidx + 1;
	}

	for (idx = 0; idx < Precision; idx++)			//Output Real Part Of Number
	{
		if (stringidx >= MSZ_String)
			break;

		#ifdef _BOOSTC								//BoostC Floating Point
			real = float32_mul(real, 10);
			whole = float32_to_int32(real);			//Convert to whole integer
			temp = float32_from_int32(whole);		//Convert whole number back to floating point
			real = float32_sub(real, temp);			//Subtract whole number from floating point number
		#endif
		#ifdef HI_TECH_C							//HiTECH Floating Point
			real = real * 10;
			whole = (int)real;						//Convert to whole integer
			temp = whole;							//Convert whole number back to floating point
			real = real - temp;						//Subtract whole number from floating point number
		#endif

		String[stringidx] = '0' + whole;
		stringidx = stringidx + 1;
	}

	if(stringidx < MSZ_String)									//Add null termination to string
	{
		String[stringidx] = 0;
	}

	return stringidx;
}











MX_UINT8 FCI_NUMBER_TO_HEX(MX_ULONG Number, MX_STRING String, MX_UINT8 MSZ_String)
{
	MX_UINT8 stringidx;
	MX_UINT8 idx = 0;
	MX_UINT8 temp;
	MX_UINT8 digit[8] = {0,0,0,0,0,0,0,0};

	#ifdef MX_STRF_LONG
		MX_UINT32 num = Number;
	#else
		MX_UINT16 num = Number;
	#endif

	//Check that output string is long enough to store result
	if((Number > 0xFFFF) && (MSZ_String < 10))
		return 0;

	if((Number > 0xFF) && (MSZ_String < 6))
		return 0;

	if((Number <= 0xFF) && (MSZ_String < 4))
		return 0;

	//Initialise start of string with hexadecimal indicator
	String[0] = '0';
	String[1] = 'x';

	//Calculate hex values
	do
	{
		temp = num % 16;
		num = num >> 4;

		if(temp > 9)
		{
			temp = temp - 10;
			digit[idx] = temp + 'A';
		}
		else
			digit[idx] = temp + '0';

		idx = idx + 1;
	} while (num > 0);

	//Copy Hex values over to output string
	for (stringidx = 2; stringidx < (idx + 2); stringidx++)
		String[stringidx] = digit[idx - (stringidx - 1)];

	//Add null termination to string
	if(stringidx < MSZ_String)
	{
		String[stringidx] = 0;
	}

	return stringidx;
}


MX_SINT32 FCI_STRING_TO_INT(MX_STRING String, MX_UINT8 MSZ_String)
{
	MX_UINT8 bNegative = 0;
	MX_UINT8 idx = 0;
	MX_SINT32 RetVal = 0;

	//Is number negative
	if(String[0] == '-')
	{
		bNegative = 1;
		idx = 1;
	}

	//While string contains a valid number
	while (idx < MSZ_String && String[idx] >= '0' && String[idx] <= '9')
	{
		//Pull MX_UINT8acter from string and add to running total
		RetVal = (long) RetVal * 10;
		RetVal = (long) RetVal + (String[idx] - '0');
		idx = idx + 1;
	}

	if (bNegative)
		RetVal = (long) 0 - RetVal;

	return RetVal;
}


MX_FLOAT FCI_STRING_TO_FLOAT(MX_STRING String, MX_UINT8 MSZ_String)
{
	MX_FLOAT RetVal = 0;
	MX_FLOAT real_divider = 1;
	MX_UINT32 whole, real;			//Changed from INT 23/07/10
	MX_UINT8 idx = 0;
	MX_UINT8 idx2 = 0;
	MX_UINT8 offset = 0;
	MX_UINT8 bNegative = 0;
	MX_UINT8 Comp_String[8];

	//Is number negative
	if(String[0] == '-')
	{
		bNegative = 1;
		offset = 1;
	}

	//Scan for decimal point
	for(idx=offset; idx<MSZ_String; idx++)
	{
		if(String[idx] == '.')
			break;
	}

	//Convert whole portion of number from string
	whole = FCI_STRING_TO_INT (String, idx);

	//No decimal point found
	if(idx == MSZ_String)
	{
		#ifdef _BOOSTC
			RetVal = float32_from_int32(whole);
		#endif
		#ifdef HI_TECH_C
			RetVal = (int)whole;
		#endif
		return RetVal;
	}

	//Collect real portion of number into new string
	offset = idx + 1;
	for(idx=offset; idx<MSZ_String; idx++)
	{
		if(String[idx] >= '0' && String[idx] <= '9')
		{
			Comp_String[idx - offset] = String[idx];
			#ifdef _BOOSTC
				real_divider = float32_mul(real_divider, 0.1);
			#endif
			#ifdef HI_TECH_C
				real_divider = real_divider * 0.1;
			#endif
		}
		else
			break;
	}

	//Convert real portion of number from string
	real = FCI_STRING_TO_INT (Comp_String, (idx - offset));

	//Adjust weighting of values and combine into one float variable
	#ifdef _BOOSTC

		RetVal = float32_from_int32(real);
		RetVal = float32_mul(RetVal, real_divider);
		real_divider = float32_from_int32(whole);
		RetVal = float32_add(RetVal, real_divider);

		if (bNegative)
			RetVal = float32_sub(0, RetVal);

	#endif
	#ifdef HI_TECH_C

		RetVal = (MX_FLOAT)real;
		RetVal = RetVal * real_divider;
		real_divider = (MX_FLOAT)whole;
		RetVal = RetVal + real_divider;

		if (bNegative)
			RetVal = 0 - RetVal;
	#endif

	return RetVal;
}



/*=----------------------------------------------------------------------=*\
   Use :Move but do not null-terminate sSrc to sDst, max len iSrc_len
       :This differs from FCI_STRMOV() in that the bytes are copied from the
       :end of the string to the start
       :Returns length of copy
\*=----------------------------------------------------------------------=*/
MX_UINT8 FCI_STRREV(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len)
{
  MX_UINT8 ix, len;
  FCI_GETLENGTH__x(sSrc, iSrc_len, len);
  if (len > iDst_len)
  {
    len = iDst_len;
  }
  ix = len;
  while (ix > 0)
  {
    ix--;
    sDst[ix] = sSrc[ix];
  }
  // No null terminate
  // Return length of string
  return len;
}


/*=----------------------------------------------------------------------=*\
   Use :Move but do not null-terminate sSrc to sDst, max len iSrc_len
       :Returns length of copy
\*=----------------------------------------------------------------------=*/
MX_UINT8 FCI_STRMOV(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len)
{
  MX_UINT8 ix;
  ix = 0;
  while (ix < iSrc_len && ix < iDst_len && sSrc[ix] != '\0')
  {
    sDst[ix] = sSrc[ix];
    ix++;
  }
  // No null terminate
  // Return length of string
  return ix;
}

/*=----------------------------------------------------------------------=*\
   Use :Concatenate sDst = (sSrc1 + sSrc2)
       :sDst is only null terminated if the concat doesnt overflow
       :
\*=----------------------------------------------------------------------=*/
void FCI_SHEAD(MX_STRING sSrc1, MX_UINT8 iSrc1_len, MX_STRING sSrc2, MX_UINT8 iSrc2_len, MX_STRING sDst, MX_UINT8 iDst_len)
{
  MX_UINT8 len, strt;

  len = 0;
  if (sSrc2 == sDst)
  {
    FCI_GETLENGTH__x(sSrc1, iSrc1_len, strt);
    if (strt < iDst_len)
    {
      // Copy second, in reverse
      // len = FCI_STRREV(sSrc2, iSrc2_len, sDst + strt, iDst_len - strt);
      FCI_GETLENGTH__x(sSrc2, iSrc2_len, len);
      if (len > (iDst_len - strt))
      {
        len = (iDst_len - strt); // Length of string to copy to
      }
      iSrc2_len = strt + len; // Use no longer needed var as an offset
      strt = len;
      while (strt > 0)
      {
        strt--;
        iSrc2_len--;
        sDst[iSrc2_len] = sSrc2[strt];
      }
      // Move first
      FCI_STRMOV__x(sSrc1, iSrc1_len, sDst, iDst_len, strt);
      len += strt;
    }
    else
    {
      // Copy first
      FCI_STRMOV__x(sSrc1, iSrc1_len, sDst, iDst_len, len);
    }
  }
  else
  {
    // Copy first
    FCI_STRMOV__x(sSrc1, iSrc1_len, sDst, iDst_len, len);
    // Copy second
    if (len < iDst_len)
    {
      sSrc1 = sDst + len; // make temp copies in no longer used variables
      iSrc1_len = iDst_len - len;
      FCI_STRMOV__x(sSrc2, iSrc2_len, sSrc1, iSrc1_len, strt);
      len += strt;
    }
  }
  // Terminate (only if can)
  if (len < iDst_len)
  {
    sDst[len] = '\0';
  }
}


/*=----------------------------------------------------------------------=*\
   Use :Copy and null-terminate sSrc to sDst, max len iSrc_len
       :sDst is only null terminated if the concat doesnt overflow
       :
\*=----------------------------------------------------------------------=*/
void FCI_SCOPY(MX_STRING sSrc, MX_UINT8 iSrc_len, MX_STRING sDst, MX_UINT8 iDst_len)
{
  MX_UINT8 len;

  // Move first
  FCI_STRMOV__x(sSrc, iSrc_len, sDst, iDst_len, len);
  // Terminate (only if can)
  if (len < iDst_len)
  {
    sDst[len] = '\0';
  }
}



