Is There A Simple Method To Match An Array Of Bytes In An Byte Array?
Solution 1:
Both PNG and JPG have a specified header, and they're both a specific size. You need to read just the bytes for the header (being careful to check against the byte array size so you don't go out of bounds). In the case of the PNG you first check if you have 4 bytes to read, then get those first 4 bytes, comparing against that 0x89, 0x50, 0x4E, 0x47 sequence. For JPG the first 3 bytes will be 0xFF 0xD8 0xFF.
Also keep in mind that the AS3 Loader class can just take the bytes for either one and figure it out for you.
Solution 2:
See if this example helps you. The idea is to find a candidate byte and then check if following bytes makes up rest-of-search item. So for bytes CA FE BA BE
we search only for byte 0xCA
and whenever one (out of many) is found we then check if it's followed by bytes 0xFE
+0xBA
+0xBE
...
Here's a complete program to study...
The main function you want is :
indexOfBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int
Which returns an int
of the offset of search sequence's beginning. I've thrown in a checkImageFormat( bytes );
function where bytes
are either .jpg or .png bytes. This will return a String
saying whether if "jpg" or "png".
package
{
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.utils.ByteArray;
import flash.utils.*;
import flash.display.Loader;
import flash.net.URLRequest;
import flash.net.URLLoaderDataFormat;
import flash.events.*;
import flash.net.URLStream;
publicclassCheck_Bytes_Image_v1extendsMovieClip{
publicvar bytes :ByteArray = new ByteArray();
publicvar stream :URLStream = new URLStream();
publicvar loader :Loader = new Loader();
publicvar formatFound : Boolean = false;
publicvar str_Format : String = "";
publicvar str_Search : String = "";
publicvar tempStr : String = "";
publicvar tempUInt : uint = 0;
publicvar bytePos : int = 0;
publicvar searchString:String = "";;
publicvar searchArray:Array = newArray();
publicfunctionCheck_Bytes_Image_v1()
{
//# add canvas
addChild(loader);
//# load image bytes via URLStream
stream.addEventListener(ProgressEvent.PROGRESS, onStreamProgress);
stream.addEventListener(Event.COMPLETE, completeHandler);
stream.load(new URLRequest("image1.png")); //image1.png
}
publicfunctiononStreamProgress(evt:ProgressEvent):void{ evt.target.readBytes(bytes, bytes.length, evt.target.bytesAvailable); }
publicfunctioncompleteHandler(evt:Event):void{
//bytes_toString(bytes); //if you want to check hex output//# update a string with format type
str_Format = checkImageFormat( bytes );
trace("Image format is : " + str_Format );
searchString = "89 50 4E 47"; //use any known sequential bytes in your Array
bytePos = indexOfBytes( bytes, searchString );
trace("bytes begin at : " + bytePos);
bytes.position = 0; //reset to starting pos
loader.loadBytes(bytes); //decode & display image
trace("loaded image now...");
//bytes.clear(); //empty the bytes
}
//# findBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):intpublicfunctionindexOfBytes (byteArray:ByteArray, value:String, startPosition:int = 0, endPosition:int = 0, endian:String = null):int{
str_Search = value;
trace("str_Search v1 is " + str_Search);
//# Clean up possible hex notation
str_Search = str_Search.split("0x").join("");
str_Search = str_Search.split(".").join("");
str_Search = str_Search.split("/").join("");
//# Clean up hex (remove non Alpha-Numeric, white spaces, new line, etc)
str_Search = str_Search.replace(/\W+/g, ""); //# clear all non Alpha-Numeric chars
str_Search.toUpperCase(); //# convert to uppercase
trace("str_Search v2 is " + str_Search);
tempUInt = 0; searchArray = []; //# resets//# convert input string into two-digits-per-slot arraywhile ( tempUInt < str_Search.length)
{
searchArray.push(str_Search.substring(tempUInt, Math.min(tempUInt+2, str_Search.length) ) );
tempUInt += 2;
}
trace("searchArray length is : " + searchArray.length);
//# search bytes for same hex vlues//# Resets for WHILE loopif ( startPosition != 0) { bytePos = startPosition; }
else { bytePos = 0; }
tempUInt = 0;
while (byteArray.bytesAvailable)
{
//# safety check : Stop at limit of bytes rangeif ( endPosition != 0 ) //# Don't go over end pos if not null
{
if ( byteArray.position >= endPosition )
{
trace("reached end position of search : stopping at: " + byteArray.position );
break;
}
}
else//# Don't go over total bytes length
{
if ( byteArray.position > (byteArray.length - searchArray.length) )
{
trace("reached ENDing bytes of search : stopping at: " + byteArray.position );
break;
}
} //# end safety check...//# Find search values
byteArray.position = bytePos;
tempStr = byteToHex( byteArray.readUnsignedByte() );
tempUInt = 0; //reset for TRUE countif ( tempStr == searchArray[0] )
{
//# tempUInt to count how many times it was true//# if total true's == search array length then we found match//trace("-------------------------------------------");for (var i:int = 0; i <= searchArray.length-1; i++)
{
bytePos = byteArray.position-1;
if ( byteToHex( byteArray[bytePos+i] ) == searchArray[i] )
{
//trace("bytes [" +bytePos+ " + " +i+ "] is : " + byteToHex( byteArray[bytePos+i] ) );//trace("search array [" +i+ "] is : " + searchArray[i] );
tempUInt++; //# add +1 for each matching entry for both arrays
}
}
trace("tempUInt match is : " + tempUInt + " || Expected TRUE count : " + searchArray.length );
if ( tempUInt == searchArray.length )
{
tempUInt = byteArray.position - 1;
trace("match FOUND for search at pos : " + tempUInt);
break;
}
else
{
tempUInt = int(-1);
trace("match NOT found for search... current pos : " + ( byteArray.position-1) );
}
}
bytePos++;
} //# end WHILE loopreturn tempUInt;
}
publicfunctionbyteToHex ( input:uint ) : String{
tempStr = input.toString(16);
if(tempStr.length < 2) { tempStr = "0" + tempStr; } //# zero padding if 1 char onlyreturn tempStr.toUpperCase();
}
publicfunctioncheckImageFormat ( inputBA : ByteArray ) : String{
//# If you need to READ as specific Endianness of format//# default in AS3 is BIG ENDIAN type//inputBA.endian = Endian.LITTLE_ENDIAN;//inputBA.endian = Endian.BIG_ENDIAN;
formatFound = false; bytePos = 0;
while( formatFound == false) //* keep going until BREAK action
{
if ( formatFound == true ){ trace("## Found format..."); break; }
inputBA.position = bytePos; //* reset positions
tempUInt = inputBA.readUnsignedByte(); //* READ byte = check byte value as Int
bytePos = inputBA.position; //* update Pos to new offset move caused by READ action//# Check for JPG (begins xFF xD8 xFF xE0)if (tempUInt == 0xFF)
{
inputBA.position = bytePos-1; //* go back one byte to include FF in next check
tempUInt = inputBA.readUnsignedInt(); //* READ 4 bytes at once (expect FF-D8-FF-E0)//trace ("tempUInt check : " + tempUInt);if (tempUInt == 0xFFD8FFE0 ) //* check IF EQUAL to expected value
{
inputBA.position += 2; //skip 2 bytes ahead to expected pos of JFIF
tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytesif (tempUInt == 0x4A464946) //* if expected JFIF (as hex) value
{
str_Format = "jpg";
trace("found JPG start + JFIF header..."); formatFound = true;
}
}
} //# end Check for JPG//# Check for PNG (begins x89 x50 x4E x47)if (tempUInt == 0x89)
{
inputBA.position = bytePos-1; //* go back one byte to include 89 in next check
tempUInt = inputBA.readUnsignedInt(); //* READ 4 bytes at once (expect 89-50-4E-47)//trace ("tempUInt check : " + tempUInt);if (tempUInt == 0x89504E47 ) //* check IF EQUAL to expected value
{
tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytesif (tempUInt == 0x0D0A1A0A) //* if also expected value
{
inputBA.position += 4; //skip 4 bytes ahead to expected pos of IHDR
tempUInt = inputBA.readUnsignedInt(); //* now check next 4 bytesif (tempUInt == 0x49484452) //* if expected IHDR (as hex) value
{
str_Format = "png";
trace("found PNG start + IHDR header..."); formatFound = true;
}
}
}
} //# end Check for PNG//# Increment Pos for next byte value checking at top of While loop
bytePos++;
} //# end WHILE loopreturn str_Format;
}
publicfunctionbytes_toString ( ba:ByteArray ) : String{
//trace("checking args : " + args);
tempStr = ""; var str_Hex:String = ""; var len:uint = ba.length;
ba.position = 0;
for (var i:uint = 0; i < len; i++)
{
tempStr=ba.readUnsignedByte().toString(16);
if(tempStr.length<2) { tempStr="0"+tempStr; } //# zero-padding if 1 char only
str_Hex += tempStr ;
}
return str_Hex.toUpperCase();
}
publicfunctionbytes_toInt( ...args ) : int{
var temp_conv_ba : ByteArray = new ByteArray();
for (var i:uint = 0; i < args.length; i++)
{ temp_conv_ba[i] = args[i]; }
var int_fromBytes:int = int("0x" + bytes_toString(temp_conv_ba) );
return int_fromBytes;
}
} //# end class
} //# end package
Solution 3:
I've put together this method but it fails to find the first value in the PNG array but the other values it finds just fine.
var png:Array = [0x89,0x50,0x4E,0x47];
var pngIndex:int = getIndexOfValueInByteArray(byteArray, png, 0, 4);
/**
* Gets the position where either a single character or an array of hexidecimal values are found in a byte array
* */publicfunctiongetIndexOfValueInByteArray(byteArray:ByteArray, value:*, startPosition:int = 0, endPosition:int = 0, endian:String = null):int{
var byte:uint;
var byteString:String;
var position:int;
var matchIndex:int;
var searchArray:Array;
var searchByte:int;
var searchByteString:String;
var found:Boolean;
var endOfFile:Boolean;
var endIndex:uint;
var debug:Boolean;
var firstByte:uint;
var firstByteString:String;
var startIndex:uint;
var searchArrayLength:int;
var compareAsString:Boolean;
debug = true;
if (value is String) {
searchArray = String(value).split("");
compareAsString = true;
}
else {
searchArray = ArrayUtil.toArray(value);
}
if (endian) {
byteArray.endian = endian;
}
if (startPosition>-1) {
byteArray.position = startPosition;
}
if (searchArray && searchArray.length) {
firstByte = searchArray[0];
firstByteString = compareAsString ? searchArray[0] : String.fromCharCode(firstByte);
searchArrayLength = searchArray.length;
}
else {
return -1;
}
while (byteArray.bytesAvailable) {
byte = byteArray.readByte();
if (!compareAsString && byte==firstByte) {
debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;
for (var j:int = 1; j < searchArrayLength; j++) {
byte = byteArray.readByte();
searchByte = searchArray[j];
debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;
if (byte==searchByte) {
if (j==searchArrayLength-1) {
found = true;
matchIndex = byteArray.position;
startIndex = matchIndex - searchArrayLength;
endIndex = matchIndex;
debug ? trace("Match found at " + startIndex):void;
break;
}
}
if (byteArray.bytesAvailable==0) {
endOfFile = true;
break;
}
}
}
elseif (compareAsString && String.fromCharCode(byte)==firstByteString) {
debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;
for (j = 1; j < searchArrayLength; j++) {
byteString = String.fromCharCode(byteArray.readByte());
searchByteString = searchArray[j];
debug ? trace("Byte:0x" + byte.toString(16) + " " + searchByteString):void;
if (byteString==searchByteString) {
if (j==searchArrayLength-1) {
found = true;
matchIndex = byteArray.position;
startIndex = matchIndex - searchArrayLength;
endIndex = matchIndex;
debug ? trace("Match found at " + startIndex):void;
break;
}
}
if (byteArray.bytesAvailable==0) {
endOfFile = true;
break;
}
}
}
else {
debug ? trace("Byte:0x" + byte.toString(16) + " " + String.fromCharCode(byte)):void;
}
if (found || endOfFile || (endPosition!=0 && byteArray.position>endPosition)) {
break;
}
}
if (found) {
debug?trace("Found at position " + startIndex + ". It ends at " + endIndex):0;
}
else {
debug?trace("Could not find what the value you're looking for in this here byte array"):0;
matchIndex = -1;
}
return matchIndex;
}
var png:Array = [0x89, 0x50, 0x4E, 0x47];
var pngIndex:int = getIndexOfValueInByteArray(byteArray, png, 0, 5);
The values traced out are:
Byte:0xffffff89Byte:0x50Byte:0x4eByte:0x47
If I set byte
as int
instead of uint
it prints out:
Byte:0x-77Byte:0x50Byte:0x4eByte:0x47
When I use a JPEG and set byte
as uint
it prints out this value:
var jpg:Array = [0xFF, 0xD8, 0xFF];
var jpgIndex:int = getIndexOfValueInByteArray(byteArray, jpg, 0, 5);
Byte:0xffffffffByte:0xffffffd8Byte:0xffffffffByte:0xffffffe0
It looks like it matches the last of values.
UPDATE: I'm going to just pass in the 0xFFFFFF89 instead of 0x89. That seems to work on Mac. Don't know how or why. I updated the function to print out the hexidecimal character and string character if it translates to a string (sometimes it's empty).
Post a Comment for "Is There A Simple Method To Match An Array Of Bytes In An Byte Array?"