/******************************************************************************
 * LoadRLE / SaveRLE - Load and save binary data using RLE compression.
 *	Run-length tokens have a set MSB, while data tokens have a cleared
 *	MSB. The value of the token's remaining bits plus one indicates the
 *	length of the block. The minimum run length is three bytes, while
 *	the maximum is 128.
 *
 *	data - Array holding data to load or save.
 *	size - Size of the data array.
 *	file - The file pointer to use.
 *	return - Total number of bytes read from or written to data[].
 */

size_t LoadRLE (unsigned char data[], size_t size, FILE *file)
{
	unsigned char token;
	unsigned int length;
	size_t total = 0;

	while(size && fread(&token, 1, 1, file)){
		length = (token & ~0x80) + 1;
		if (length > size)
			return total;
		if(token & 0x80){
			if(!fread(&token, 1, 1, file))
				return total;
			memset(data, token, length);
		}else{
			if(fread(data, 1, length, file) != length)
				return total;
		}
		data += length, size -= length, total += length;
	}
	return total;
}

/*************************************/

size_t SaveRLE (unsigned char data[], size_t size, FILE *file)
{
	unsigned char token;
	unsigned int i;
	size_t total = 0;

	while(size)
	{
		/*This loop identifies blocks of repeating data:*/
		i = 2;
		while(i < size && i < 128 &&
			data[i] == data[i - 1] && data[i - 1] == data[i - 2])
			i++;
		/*If repeating data was found, save it:*/
		if(i > 2){
			token = i - 1 | 0x80;
			if(!fwrite(&token, 1, 1, file))
				return total;
			if(!fwrite(data, 1, 1, file))
				return total;
			data += i, size -= i, total += i;
		}

		/*This loop identifies blocks of non-repeating data:*/
		i = 0;
		while(i < size && i < 128 && (i + 2 > size ? 1 :
			data[i] != data[i + 1] || data[i + 1] != data[i + 2]))
			i++;
		/*If non-repeating data was found, save it:*/
		if(i){
			token = i - 1;
			if(!fwrite(&token, 1, 1, file))
				return total;
			if(fwrite(data, 1, i, file) != i)
				return total;
			data += i, size -= i, total += i;
		}
	}

	return total;
}
