Good to hear you took a look.
GregC wrote: 27 Oct 2020 23:37
1. What action invokes the StrToBytes function, and what are the returned bytes used for / where are they rendered?
ie. I understand BytesToStr being called to obtain the Data inspector table translated string value, but I can't see where the reverse conversion is needed? (StrToBytes)
The datainspector is not just a viewer, but also an editor, that's why you need the reverse operation.
Each line in the datainspector grid has an edit box on the right side, where you can not only view text, but edit it as well. Pressing enter or losing the focus of this edit box commits the changes you made and translates the string back to the corresponding byte representation of the line's datatype.
So, the bytes returned by StrToBytes will be used to modify the file/stream and replace the bytes that were initially converted to a string by BytesToStr for display in the datainspector.
GregC wrote: 27 Oct 2020 23:37
2. I'm struggling to understand the logic / interaction between SupportedByteOrders, and when ChangeByteOrder function is invoked / not invoked.
ie. Given that the host (Windows) environment is LittleEndian, I'd assumed that by specifying boBigEndian only (for SupportedByteOrders) it would then result in an initial call to ChangeByteOrder, irrespective of the GUI selected "Byte order"?
However that doesn't seem to happen, and manually switching between the GUI "Byte order" seems to still swap the BytesToStr passed byte order (even if only boBigEndian support is specified).
I think I must be misunderstanding the intent of SupportedByteOrders / ChangeByteOrder?
If you look at the Delphi example plugin for Int32 values, you will see this code:
Code: Select all
procedure TInt32Converter.ChangeByteOrder(Bytes: PByte; ByteCount: Integer;
TargetByteOrder: TByteOrder);
begin
inherited;
if (TargetByteOrder = boBigEndian) and (ByteCount >= sizeof(Int32)) then
PUInt32(Bytes)^ := ByteSwap(PUInt32(Bytes)^);
end;
The ChangeByteOrder function is best understood when thinking of it as being called before the BytesToStr function is called.
A very simplified version of BytesToStr does essentially this:
Code: Select all
function TInt32Converter.BytesToStr(Bytes: PByte; ByteCount: Integer;
IntegerDisplayOption: TIntegerDisplayOption; out ConvertedByteCount: Integer;
var ConvertedStr: string): TBytesToStrError;
begin
ConvertedStr := IntToStr(PInt32(Bytes)^);
// lots of other cases omitted
end;
Or in other words, it takes 4 bytes, casts them to a 32 bit integer and then calls an integer to string conversion function provided by the programming language/runtime library (in the case of Delphi it's IntToStr). The casting of 4 bytes to an integer on a little endian machine means that it will implicitly reorder the byte sequence (or actually, handle it as if the bytes were in reverse order).
In other words, if you expect the data to be in little endian order already, you don't need to do anything on a little endian machine. But if it is in big endian order, you need to reverse it, so the little endian machine will handle it correctly.
ChangeByteOrder is always called before any operation (also some internal to HxD only, which are not exposed to plugins),
such that the data is in little endian byte order before doing any further processing. So all you have to ensure is that it does that, independent of anything else.
But it also means that you can rely on having passed data in the right order for BytesToStr, so you can do simple integer casts, for example.
The same hold true for StrToBytes, it can just use the native byte ordering provided by the programming language/CPU and rely on ChangeByteOrder being called when necessary, before writing the data back to the file/stream.
This works because reversing the byte order two times (once before BytesToStr and then again after StrToBytes), will result in the original byte order.
If the byte order is fixed, like for UTF-8, you can simply return the data unchanged, since it never needs to be reordered. In that case you will return an empty set for
SupportedByteOrders, since data is neither little nor big endian, but a fixed byte order that is not up to interpretation.
SupportedByteOrders just says what byte orders the datatype can be in (not what you chose to support), and ChangeByteOrder takes care of reordering the data as necessary.
Why would you not just call a generic function to reverse the byte order when necessary? Because you may have a sort of structure or composed datatypes, some of which might need reordering, while others don't. Such as ASCII-strings mixed with integers.
Another example: if your data type is really a data structure like "DOS time & date", you would have code like this:
Code: Select all
procedure TDOSTimeDateConverter.ChangeByteOrder(var Bytes: TBytes;
TargetByteOrder: TByteOrder);
begin
inherited;
if (TargetByteOrder = boBigEndian) and (Length(Bytes) >= MaxTypeSize) then
begin
PDosTimeDate(@Bytes[0]).Time := ReverseBytes(PDosTimeDate(@Bytes[0]).Time);
PDosTimeDate(@Bytes[0]).Date := ReverseBytes(PDosTimeDate(@Bytes[0]).Date);
end;
end;
Here time and date are reversed individually, since they are two 16 bits integers, and not only one large 32 bit integer.
Finally, GUIDs are a good example of data in mixed endianness. Some parts are integers (which need to be reversed to match the selected endianness), others are merely byte sequences (that will never be reordered):
https://blogs.msdn.microsoft.com/opensp ... -ssinguid/
Code: Select all
typedef struct {
unsigned long Data1;
unsigned short Data2;
unsigned short Data3;
byte Data4[8];
} GUID;
Note that each data element is treated individually.
Data1 being 4 bytes long goes from Byte1Byte2Byte3Byte4 to
Byte4Byte3Byte2Byte1.
Data2 being 2 bytes long goes from Byte1Byte2 to Byte2Byte1.
Data3 being 2 bytes, is treated in the same way as Data2.
Data4 stays unaltered as it is represented as an array of 8
individual bytes.
In summary, the datatypes of programming languages already interpret a byte sequence as little endian (on x86 machines), you don't need to reverse their byte order, it will already by handled in reverse order automatically by the CPU. That's why you need to explicitly reverse byte sequences, when they are in big endian order, to compensate for this effect.
That may be confusing, since data stored in big endian order already seems to be in the "right" order: the one you read in naturally as human (at least for numbers, always from left to right). But the right order is defined by the target CPU, and only big endian machines agree with our reading order.
Finally, from another angle: if you were writing code for a big endian machine, your code would look like this instead:
Code: Select all
procedure TInt32Converter.ChangeByteOrder(Bytes: PByte; ByteCount: Integer;
TargetByteOrder: TByteOrder);
begin
inherited;
if (TargetByteOrder = boLittleEndian) and (ByteCount >= sizeof(Int32)) then
PUInt32(Bytes)^ := ByteSwap(PUInt32(Bytes)^);
end;
Note: I called the byte reversing order function ByteSwap in this example. A commonly used name but somewhat confusing. Better would be ReverseBytes, since what really happens is reordering a byte sequence ABCD to its reverse order: DCBA. (Where A, B, C, D are names of a byte, each.)
GregC wrote: 27 Oct 2020 23:37
ps. I posted these more general hxd-plugin-framework questions here, as the official news topic on hxd-plugin-framework appears to be locked (can't reply).
Yeah, the news section is not open to comment. If you feel that something is a more general question, you could start another thread, though, if you wish.
I hope this helps.