I'm trying to convert a binary string to a byte array of a specific format.
Sample binary data:
ê≤ÚEZêK
The hex version of the binary string looks like this:
00151b000000000190b2f20304455a000003900000004b0000
The Python script uses struct package and unpacks the above string (in binary) using this code:
data = unpack(">hBiiiiih",binarydata)
The desired byte array looks like this. This is also the output of the data array is:
(21, 27, 0, 26260210, 50611546, 912, 75, 0)
How can I unpack the same binary string using PHP's unpack() function and get the same output? That is, what's the >hBiiiiih equivalent in PHP?
So far my PHP code
$hex = "00151b000000000190b2f20304455a000003900000004b0000";
$bin = pack("H*",$hex);
print_r(unpack("x/c*"));
Which gives:
Array ( [*1] => 21 [*2] => 27 [*3] => 0 [*4] => 0 [*5] => 0 [*6] => 0 [*7] => 1 [*8] => -112 [*9] => -78 [*10] => -14 [*11] => 3 [*12] => 4 [*13] => 69 [*14] => 90 [*15] => 0 [*16] => 0 [*17] => 3 [*18] => -112 [*19] => 0 [*20] => 0 [*21] => 0 [*22] => 75 [*23] => 0 [*24] => 0 )
Would also appreciate links to a PHP tutorial on working with pack/unpack.
This produces the same result as does Python, but it treats signed values as unsigned because unpack() does not have format codes for signed values with endianness. Also note that the integers are converted using long, but this is OK because both have the same size.
$hex = "00151b000000000190b2f20304455a000003900000004b0000";
$bin = pack("H*", $hex);
$x = unpack("nbe_unsigned_1/Cunsigned_char/N5be_unsigned_long/nbe_unsigned_2", $bin);
print_r($x);
Array
(
[be_unsigned_1] => 21
[unsigned_char] => 27
[be_unsigned_long1] => 0
[be_unsigned_long2] => 26260210
[be_unsigned_long3] => 50611546
[be_unsigned_long4] => 912
[be_unsigned_long5] => 75
[be_unsigned_2] => 0
)
Because this data is treated as unsigned, you will need to detect whether the original data was negative, which can be done for 2 byte shorts with something similar to this:
if $x["be_unsigned_1"] >= pow(2, 15)
$x["be_unsigned_1"] = $x["be_unsigned_1"] - pow(2, 16);
and for longs using
if $x["be_unsigned_long2"] >= pow(2, 31)
$x["be_unsigned_long2"] = $x["be_unsigned_long2"] - pow(2, 32);
Related
I am trying to build a function which decodes a CS:GO match sharing code. I have seen enough examples but everyhting is in JS or C# but nothing in PHP.
I took the akiver demo manager as an example and i tried to replicate it in PHP. I am going bit blind because i have no idea what is the output on a certain points so i can only hope that the result will be what i expect it to be. I think i am on the right path, the problem comes when the bytes have to be created/interpeted/converted to the desire outcome.
The code that should be decoded is the following: 'CSGO-oPRbA-uTQuR-UFkiC-hYWMB-syBcO' ($getNextGame variable)
The result should be 3418217537907720662
My code so far:
/**
* #param $getNextGame
* #return array
*/
public function decodeDemoCode(string $getNextGame): array
{
$shareCodePattern = "/CSGO(-?[\w]{5}){5}$/";
if (preg_match($shareCodePattern, $getNextGame) === 1) {
$result = [];
$bigNumber = 0;
$matchIdBytes = $outcomeIdBytes = $tvPortIdBytes = [];
$dictionary = "ABCDEFGHJKLMNOPQRSTUVWXYZabcdefhijkmnopqrstuvwxyz23456789";
$dictionaryLength = strlen($dictionary);
$changedNextGame = str_replace(array("CSGO", "-"), "", $getNextGame);
$chars = array_reverse(str_split($changedNextGame));
foreach ($chars as $char) {
$bigNumber = ($bigNumber * $dictionaryLength) + strpos($dictionary, $char);
}
}
}
This brings me back something like that:
1.86423701402E+43 (double)
Then i have the following:
$packed = unpack("C*", $bigNumber);
$reversedPacked = array_reverse($packed);
and this brings the following back:
array(17 items)
0 => 51 (integer)
1 => 52 (integer)
2 => 43 (integer)
3 => 69 (integer)
4 => 50 (integer)
5 => 48 (integer)
6 => 52 (integer)
7 => 49 (integer)
8 => 48 (integer)
9 => 55 (integer)
10 => 51 (integer)
11 => 50 (integer)
12 => 52 (integer)
13 => 54 (integer)
14 => 56 (integer)
15 => 46 (integer)
16 => 49 (integer)
Now here i am not really sure what to do because i do not completely understand C# and i have never worked with bytes in PHP before.
Generally the return type should be an array and would look something like that:
$result = [
matchId => 3418217537907720662,
reservationId => 3418217537907720662,
tvPort => 55788
];
Thanks in advance. Any help is deeply appreciated
I have created a PHP class which makes that possible:
CS:GO ShareCode Decoder PHP
The first problem you have to solve is the returned double value. PHP has limitation when it comes to big integers. More to that here What is the maximum value for an integer in PHP.
Because of this limitation you are losing precision leading to inaccurate results. In order to solve this problem you will have to use one of these libraries GMB, BC Math. What these libraries do, is to give you back the result as a string which solves the double value you got.
So your code has to look something like that:
foreach ($chars as $char) {
$bigNumber = gmp_add(
gmp_mul($bigNumber,$dictionaryLength),
strpos($dictionary,$char)
);
}
json_encode($bigNumber);
$result = json_decode($bigNumber, true, 512, JSON_BIGINT_AS_STRING);
This will give you back the following "18642370140230194654275126136176397505221000"
You do not really need the PHP pack and unpack functions since the results can be generated without them. The next step is to convert your number to hexadecimal. You can do that with the following:
$toHex = gmp_strval(gmp_init($number, 10), 16);
Again you need to use the gmp library in order to get the desired value. What you do, is to make sure that the result is a string and then you convert your number's base from 10 to 16 which is the equivalent of hexadecimal. The results is the following:
"d6010080bdf26f2fbf0100007cf76f2f5188"
The next step is to convert the hex value to an array of byte integers. It looks like this:
$bytes = [];
$byteArray= str_split($toHex, 2);
foreach ($byteArray as $byte) {
$bytes[] = (int)base_convert($byte, 16, 10);
}
What you do here is to split the array to every two chars. The $byteArray variable looks like this (before it enters the foreach loop)
array(18 items)
0 => 'd6' (2 chars) 1 => '01' (2 chars) 2 => '00' (2 chars) 3 => '80' (2 chars)
4 => 'bd' (2 chars) 5 => 'f2' (2 chars) 6 => '6f' (2 chars) 7 => '2f' (2 chars)
8 => 'bf' (2 chars) 9 => '01' (2 chars) 10 => '00' (2 chars) 11 => '00' (2 chars)
12 => '7c' (2 chars) 13 => 'f7' (2 chars) 14 => '6f' (2 chars) 15 => '2f' (2 chars)
16 => '51' (2 chars) 17 => '88' (2 chars)
Now you will have to convert each entry into integer. Since the results are not that big anymore you can change the base of your values with the base_convert function. The base is 16 (hex) and you will have to change it back to 10. The results $bytes after the foreach loop looks like this:
array(18 items)
0 => 214 (integer) 1 => 1 (integer) 2 => 0 (integer) 3 => 128 (integer)
4 => 189 (integer) 5 => 242 (integer) 6 => 111 (integer) 7 => 47 (integer)
8 => 191 (integer) 9 => 1 (integer) 10 => 0 (integer) 11 => 0 (integer)
12 => 124 (integer) 13 => 247 (integer) 14 => 111 (integer) 15 => 47 (integer)
16 => 81 (integer) 17 => 136 (integer)
Now you have to define which bytes are responsible for each result.
$matchIdBytes = array_reverse(array_slice($bytes, 0, 8));
$reservationIdBytes = array_reverse(array_slice($bytes, 8, 8));
$portBytes = array_reverse(array_slice($bytes, 16, 2));
For the match id you will have to get the first 8 entries and the reverse the array
For the reservation id you will have to get the next 8 entries starting from the 8th entry and reverse the array
For the port you will have to get the last 2 entries and reverse the array
Now you will have to return the value
return [
'matchId' => $this->getResultFromBytes($matchIdBytes),
'reservationId' => $this->getResultFromBytes($reservationIdBytes),
'tvPort' => $this->getResultFromBytes($portBytes)
];
The getResultFromBytes() function:
**
* #param array $bytes
* #return string
*/
public function getResultFromBytes(array $bytes): string
{
$chars = array_map("chr", $bytes);
$bin = implode($chars);
$hex = bin2hex($bin);
return gmp_strval($this->gmp_hexDec($hex));
}
/**
* #param $n
* #return string
*/
public function gmp_hexDec($n): string
{
$gmp = gmp_init(0);
$multi = gmp_init(1);
for ($i=strlen($n)-1;$i>=0;$i--,$multi=gmp_mul($multi, 16)) {
$gmp = gmp_add($gmp, gmp_mul($multi, hexdec($n[$i])));
}
return $gmp;
}
Best regards
I have successfully converted the positive temperature data from below raw data
array (
'timestamp' => '2020-06-11T11:09:21.335Z',
'type' => 'Unknown',
'mac' => 'F64BB46181EF',
'bleName' => 'P RHT 900350',
'rssi' => -63,
'rawData' => '02010605166E2AC90A04166F2A240D09502052485420393030333530',
)
$cutdata = str_split($rawData,2);
$humidity_cut = hexdec($cutdata[13]);
$x_cut = $cutdata[8].$cutdata[7]; //gives 0AC9
$c_cut = hexdec($x_cut);
$temp_cut = $c_cut/100;
echo $temp_cut;exit;
But when i am getting negative temperature values it giving me issues it increase the temp value more then 600
Here is the negative Temp Raw Data
array (
'timestamp' => '2020-07-03T10:05:53.049Z',
'type' => 'Unknown',
'mac' => 'EDF2F589DCAE',
'bleName' => 'P RHT 900351',
'rssi' => -79,
'rawData' => '02010605166E2AB4FA04166F2A310D09502052485420393030333531',
)
I have asked the support team they said
You have to do a 2 complement, which is reversing all the bits, and add 1 in binary.
I'm assuming that the output is OK, but for a 32 bit number, this code checks if the high bit is set (using & 32768) and if it is, it xors the number with 65535 (all 16 bits set) to invert it and then just adds 1 (the result is then made a -ve number)...
if ( $c_cut & 32768 ) {
$c_cut = -(($c_cut ^ 65535)+1);
}
which gives -13.56 as the result.
I am trying to pass variables to the PHP pecl extension's 'trader' project's trader_stochrsi() function.
here is my use example :
$stochrsi = trader_stochrsi(array(5.5), 14, 3, 3);
var_dump($stochrsi);
I get the following read-out in the var_dump :
bool(false)
--Any thoughts as to why this may be happening?
Thanks,
GS
You are specifying 14 intervals the function must have to be able to give an RSI value, but your array only contains one interval i.e. 5.5.
You should put 15 items in your array. It will use the first 14 to calculate a value and output it for 16th interval.
For me it works after 19 elements:
array (size=19)
0 => float 1.298E-5
1 => float 1.246E-5
2 => float 1.129E-5
3 => float 1.091E-5
4 => float 1.015E-5
5 => float 1.075E-5
6 => float 1.056E-5
7 => float 1.046E-5
8 => float 1.07E-5
9 => float 1.046E-5
10 => float 1.113E-5
11 => float 1.163E-5
12 => float 1.216E-5
13 => float 1.253E-5
14 => float 1.295E-5
15 => float 1.356E-5
16 => float 1.285E-5
17 => float 1.43E-5
18 => float 1.426E-5
->>>
array (size=2)
0 =>
array (size=1)
18 => float 100
1 =>
array (size=1)
18 => float 100
I want to convert the byte array from a webservice to image. The webservice response array looks like the following but I could not figure out how to render this array back to an image. Please help me
'MemberImage' =>
array (size=151745)
0 => int 255
1 => int 216
2 => int 255
3 => int 224
4 => int 0
5 => int 16
6 => int 74
7 => int 70
8 => int 73
9 => int 70
10 => int 0
11 => int 1
12 => int 1
13 => int 1
14 => int 0
15 => int 72
16 => int 0
17 => int 72
18 => int 0
19 => int 0
20 => int 255 ...
Use pack to convert data into binary string, es:
$data = implode('', array_map(function($e) {
return pack("C*", $e);
}, $MemberImage));
// header here
// ...
// body
echo $data;
If you want to convert that array to an actual byte array (i.e. a binary string in PHP) you could use the following function...
function arrayToBinaryString(Array $arr) {
$str = "";
foreach($arr as $elm) {
$str .= chr((int) $elm);
}
return $str;
}
You could also use pack instead of the above function to do the same thing like so...
call_user_func_array('pack', array_merge(['C*'], $arr));
or in PHP 5.6+
pack('C*', ...$arr);
With that you could then - in theory - use the binary string as an image. So, for example, assuming you want to output the raw image data, and that the image is say a PNG, you would do something like the following, in conjunction with the above code...
header('Content-type: image/png');
echo arrayToBinaryString($myArray);
Just be sure to edit the Content-type header with whatever type the actual image is. If you don't know you could use something like getimagesize on the binary string to extract the MIME type from the image data.
$tmpFile = tempnam("/tmp");
$image = arrayToBinaryString($myArray);
file_put_conetnts($tmpFile, $image);
$imageData = getimagesize($tmpFile);
header("Content-type: {$imageData['mime']}");
echo $image;
unlink($tmpFile);
I'm working on it
$source = mb_convert_encoding('test', "unicode", "utf-8");
$source = unpack('C*', $source);
var_dump($source);
return:
array (size=8)
1 => int 0
2 => int 116
3 => int 0
4 => int 101
5 => int 0
6 => int 115
7 => int 0
8 => int 116
but i want this return:
array (size=8)
1 => int 116
2 => int 0
3 => int 101
4 => int 0
5 => int 115
6 => int 0
7 => int 116
8 => int 0
I want use this return in openssl function for encryption. just $source important to me, i write other code for debugging.
What can i do to solve this problem?
"Unicode" is not a real encoding; it's the name of the overarching standard and used as an alias for UTF-16BE mostly by Microsoft, and apparently PHP supports it for that reason. What you expect is UTF-16LE, so use that explicitly:
$source = mb_convert_encoding('test', 'UTF-16LE', 'UTF-8');