pack and hex

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

•  Subject
• Author
• Posted on

Hi,

before I go into my questions here's a little excerpt from a web site:
http://www.id3.org/id3v2-00

"The ID3 tag size is encoded with four bytes where the first bit (bit 7)
is set to zero in every byte, making a total of 28 bits. The zeroed bits
are ignored, so a 257 bytes long tag is represented as \$00 00 02 01."

"The reason to use 28 bits (representing up to 256MB) for size
description is that we don't want to run out of space here."

I tried to convert 00 00 02 01 from hex to decimal by using my calc and
it gave me 513 not 257

In order to get 257 I should have done 00 00 01 01, what am I doing
wrong?

is there anyway to have "pack" to get round it?

Here's a little more from the web site:

"The three character frame identifier is followed by a three byte size
descriptor, making a total header size of six bytes in every frame."

is there anyway to have "pack" to get round this? a number value into 3
bytes? \$00 \$00 \$00 ??

thanks ever so much!

Re: pack and hex

I tried this and worked well:

printf "%06X", 1024

It fred the following:

000400

Still I need to divide into 3 bytes: 00 04 00

Re: pack and hex

Yes. You missed the bit that says 'the first bit of every byte is
ignored'. 00 00 02 01 in binary is

00000000 00000000 00000010 00000001

if you the strip the first bit of each byte you get

0000000  0000000  0000010  0000001

or 0b100000001 = 257.

my \$length =
oct "0b" .
join "",
map {
scalar reverse unpack "b7", \$_
}
map chr,
0x00, 0x00, 0x02, 0x01;

You can probably do it more neatly with vec().

Ben

What did you try?

Ben

--
It will be seen that the Erwhonians are a meek and long-suffering people,
easily led by the nose, and quick to offer up common sense at the shrine of
logic, when a philosopher convinces them that their institutions are not based
on the strictest morality.  [Samuel Butler, paraphrased]       ben@morrow.me.uk

Re: pack and hex

wow that's great...is there any way to do the reverse function?

Like, I get an integer number and convert it into a 4-byte-long hex
string?

thanks

Re: pack and hex

Yes... just reverse each operation.

Convert to binary: sprintf "%028b", ...
Split into 7-bit groups: map /(.)/g, ...
Convert back to numbers: map oct("0b" . \$_), ...
Convert to bytes: map chr,
Join into a string: join "",

I'll leave you to put the pieces together. (It would have been better to
use sprintf instead of unpack above; it would be better still to use
vec() for everything.)

Is there a good reason you're not using a module like MP3::Tag for this?

Ben

--
"Faith has you at a disadvantage, Buffy."
"'Cause I'm not crazy, or 'cause I don't kill people?"
"Both, actually."
[ben@morrow.me.uk]

Re: pack and hex

I'm not trying to write/read ID3 tags. Yet, I was struck by the way they
pack everything into a binary string...

Right now I cannot figure outhow come they came up with the b7
thing...If I were to use the following as a b8:

0xFF 0xFF 0xFF 0xFF

I could numbers from 0 to 4294967295, with the b7 is a lesser.

Re: pack and hex

Larry wrote:

It's because for MP3, bytes with the high bit set are considered
special: they're treated as the start of a new audio frame. In
principle, bytes with the high bit set are illegal in meta tags.

And then, of course somebody screwed up and put raw Unicode (including
BOM, which is a 0xFF and a 0xFE byte, indicating byte endianness) in
ID3v2 meta tags. I know Winamp dares to do that. And now, we're stuck
with the consequences.

--
Bart.

Re: pack and hex

Hi,

thats what Im trying to do. I have to 2 sockets sending binary data
each other. I need to have some sort of header on top of the data. I
cannot read from the socket line by line, I can only sysread. So I
thought the header would like something like this:

(\$xx is used to indicate a byte with unknown content.)

HEADER \$XX \$XX \$XX KEY1 \$XX \$XX VALUE1 KEY2 \$XX \$XX VALUE2 ...etc...

- The first 7 bytes of the tag are always "HEADER"
- The Header size is encoded with 3 bytes
- Key ID is 4 chars long made out of the characters capital A-Z and 0-9
- Value size is encoded with 2 bytes
- Value can contain any kind of data

So, in a nutshell, Here's what I should do:

\$len = substr \$header, 6, 4;

\$len = hex2dec \$len

then I can split keys and values accordingly...

Now i was wondering if it was possible to create a structure like this:

KEY1 => VALUE1,
KEY2 => VALUE2,
KEY3 => VALUE3
]
]

returning a binary strings like this:

HEADER \$XX \$XX \$XX KEY1 \$XX \$XX VALUE1 KEY2 \$XX \$XX VALUE2 KEY3 \$xx \$xx
VALUE3

Re: pack and hex

That's how I sorted it out: http://www.theartofweb.net/txt/dmap.txt

Re: pack and hex

Larry wrote:

You're nto doign anything wrong. 0x00000201 is 513, not 257. But the
*meaning* of these bytes, using the system they use, is 257.

You can manually convert the byets to a number accodring to this scheme,
for example in a loop, effectively treating the bytes as a string
representation of a number in base 128:

my \$bytes = pack "H*", '00000201';
my \$n = 0;
foreach my \$byte (unpack 'C*', \$bytes) {
\$n *= 128;
\$n += 127 & \$byte;
}
print \$n;

This prints 257.

An alternative is to use Perl's built in support for BER compressed
integers, see perlfunc -f pack:

w    A BER compressed integer (not an ASN.1 BER, see
perlpacktut for details).  Its bytes represent an
unsigned integer in base 128, most significant digit
first, with as few digits as possible.  Bit eight (the
high bit) is set on each byte except the last.

That's almost a perfect fit: most significant byte first, base 128, high
bit not part of the value. So I just need to set the high bit for the
first 3 bytes (but not for the last), and unpack can directly decode it.
Let's try that:

my \$bytes = pack "H*", '00000201';
my \$n = unpack "w", \$bytes | pack "H*", "80808000";
print \$n;

That prints 257. Bingo.

--
Bart.