I’ve found out that sometimes when i read a file that contains byte array data, the readFloat method of Android’s ByteArray class reads either a really big or minus that.
When i further investigated this, i saw that the bytes are written backwards!
2 things to notice:
when i target HTML5 / Flash it works as expected. so this is probably a bug in Android’s code.
This PATCH function fixed the problem:
public static function readFloat(ba:ByteArray):Float
{ #if android
return readReversedFloat(ba); #else
return ba.readFloat(); #end
}
private static function readReversedFloat(ba:ByteArray):Float
{
var temp:ByteArray = new ByteArray();
var b1:ByteArray = new ByteArray();
var b2:ByteArray = new ByteArray();
var b3:ByteArray = new ByteArray();
var b4:ByteArray = new ByteArray();
ba.readBytes(b1, 0, 1);
ba.readBytes(b2, 0, 1);
ba.readBytes(b3, 0, 1);
ba.readBytes(b4, 0, 1);
Looking at the source, it appears we are using the Haxe getFloat method of Bytes, where in many other functions we use our own code, and respect endianness:
Finally, I had some time to further investigate the issue, here are some of my personal conclusions:
In my tests, this happens only with readFloat().
This happens when i load an external ByteArray (Written in a flash application for example)
I looked into openfl.utils.ByteArrayData’s method readFloat, it looks like this (there’s no notice to Endian…) :
public function readFloat ():Float {
if (position + 4 > __length) {
throw new EOFError ();
}
position += 4;
return getFloat (position - 4);
}
While readInt takes notice of endian:
public function readInt ():Int {
var ch1 = readUnsignedByte ();
var ch2 = readUnsignedByte ();
var ch3 = readUnsignedByte ();
var ch4 = readUnsignedByte ();
if (endian == LITTLE_ENDIAN) {
return (ch4 << 24) | (ch3 << 16) | (ch2 << 8) | ch1;
} else {
return (ch1 << 24) | (ch2 << 16) | (ch3 << 8) | ch4;
}
}
and i compared writeFloat of as3 and openfl, openfl reads and writes float as LITTLE_ENDIAN, changing the endian value of the ByteArray does not effect the read value.
Am I missing something? how do i make readFloat take note of the endian value?
Just came across this issue as well with the HTML5 target and looking at the docs for haxe Bytes it says:
getFloat (pos:Int):Float
Returns the IEEE single precision value at given position (in low endian encoding). Result is unspecified if reading outside of the bounds
So it using haxe Bytes does look like it returns in low endian format all the time and this causes an error when using the big-endian format.
Taking a quick look now at what can be done to fix it. Will keep you posted on any outcome.
Do you think it would be similar to the readInt code, but with a Float return type? Perhaps we can use the current code if the endianness matches, or otherwise fall to more complex code like readInt does
I think I found another bug in readFloat() in android and next.
My code runs fine in windows (next/legacy), flash and android legacy. But when I use next in android, as soon as my code tries reading a float from a bytearray the app crashes. Im using little endian. Does next use another implementation of bytearray?
The crash stack is:
01-20 01:51:15.810 276 276 F DEBUG : #00 pc 00abbf20 /data/app/uy.gg.EntitySystemBase-1/lib/arm/libApplicationMain.so (__hxcpp_align_get_float32(unsigned char*, int)+24)
01-20 01:51:15.810 276 276 F DEBUG : #01 pc 00abbff0 /data/app/uy.gg.EntitySystemBase-1/lib/arm/libApplicationMain.so (__hxcpp_memory_get_float(Array, int)+52)
01-20 01:51:15.810 276 276 F DEBUG : #02 pc 00abe01c /data/app/uy.gg.EntitySystemBase-1/lib/arm/libApplicationMain.so (openfl::utils::ByteArrayData_obj::readFloat()+700)
The good news is that I made my code run the bad news is that I dont know what is going on and the solution is more a hack.
So at least in my case the bytearray is created in an adobe air app and then loaded by openfl.
I made a simple example in openfl writing and reading floats on a bytearray and it work, so I try writing the same bytes generated by flash and reading them as floats and it work again.
So I made a buffer, I write the bytes to the buffer and read them as floats.
buffer.position = 0;
for (i in 0...numberFloatReads*4)
{
buffer.writeByte(data.readUnsignedByte());
}
buffer.position = 0;
buffer.readFloat();....
So Im thinking that the error may come from some inconsistency between how Assets.getBytes imports the data and how openfl is using it. Still dont know why in windows it dosent crash, maybe the cast is diferent on android?
Still the problem with endian remains, luckly I have my data in little endian, but apparently you cant use big endian and floats without doing a little hack.