I am using Delphi 2009.
I want to view the contents of a file (in hexadecimal) inside a memo.
I'm using this code :
var
Buffer:String;
begin
Buffer := '';
AssignFile(sF,Source); //Assign file
Reset(sF);
repeat
Readln(sF,Buffer); //Load every line to a string.
TempChar:=StrToHex(Buffer); //Convert to Hex using the function
...
until EOF(sF);
end;
function StrToHex(AStr: string): string;
var
I ,Len: Integer;
s: chr (0)..255;
//s:byte;
//s: char;
begin
len:=length(AStr);
Result:='';
for i:=1 to len do
begin
s:=AStr[i];
//The problem is here. Ord(s) is giving false values (251 instead of 255)
//And in general the output differs from a professional hex editor.
Result:=Result +' '+IntToHex(Ord(s),2)+'('+IntToStr(Ord(s))+')';
end;
Delete(Result,1,1);
end;
When I declare variable "s" as char (i know that char goes up to 255) I get results hex values up to 65535!
When i declare variable "s" as byte or chr (0)..255, it outputs different hex values, comparing to any Hexadecimal Editor!
Why is that? How can I see the correct values?
Check images for the differences.
1st image: Professional Hex Editor.
2nd image: Function output to Memo.
Thank you.
Your Delphi 2009 is unicode-enabled, so Char is actually WideChar and that's a 2 byte, 16 bit unsigned value, that can have values from 0 to 65535.
You could change all your Char declarations to AnsiChar and all your String declarations to AnsiString, but that's not the way to do it. You should drop Pascal I/O in favor of modern stream-based I/O, use a TFileStream, and don't treat binary data as Char.
Console demo:
program Project26;
{$APPTYPE CONSOLE}
uses SysUtils, Classes;
var F: TFileStream;
Buff: array[0..15] of Byte;
CountRead: Integer;
HexText: array[0..31] of Char;
begin
F := TFileStream.Create('C:\Temp\test', fmOpenRead or fmShareDenyWrite);
try
CountRead := F.Read(Buff, SizeOf(Buff));
while CountRead <> 0 do
begin
BinToHex(Buff, HexText, CountRead);
WriteLn(HexText); // You could add this to the Memo
CountRead := F.Read(Buff, SizeOf(Buff));
end;
finally F.Free;
end;
end.
In Delphi 2009, a Char is the same thing as a WideChar, that is, a Unicode character. A wide character occupies two bytes. You want to use AnsiChar. Prior to Delphi 2009 (that is, prior to Unicode Delphi), Char was the same thing as AnsiChar.
Also, you shouldn't use ReadLn. You are treating the file as a text file with text-file line endings! This is a general file! It might not have any text-file line endings at all!
For an easier to read output, and looking better too, you might want to use this simple hex dump formatter.
The HexDump procedure dumps an area of memory into a TStrings in lines of two chunks of 8 bytes in hex, and 16 ascii chars
example
406563686F206F66 660D0A6966206578 #echo off..if ex
69737420257E7331 5C6E756C20280D0A ist %~s1\nul (..
0D0A290D0A ..)..
Here is the code for the dump format function
function HexB (b: Byte): String;
const HexChar: Array[0..15] of Char = '0123456789ABCDEF';
begin
result:= HexChar[b shr 4]+HexChar[b and $0f];
end;
procedure HexDump(var data; size: Integer; s: TStrings);
const
sepHex=' ';
sepAsc=' ';
nonAsc='.';
var
i : Integer;
hexDat, ascDat : String;
buff : Array[0..1] of Byte Absolute data;
begin
hexDat:='';
ascDat:='';
for i:=0 to size-1 do
begin
hexDat:=hexDat+HexB(buff[i]);
if ((buff[i]>31) and (buff[i]<>255)) then
ascDat:=ascDat+Char(buff[i])
else
ascDat:=ascDat+nonAsc;
if (((i+1) mod 16)<>0) and (((i+1) mod 8)=0) then
hexDat:=hexDat+sepHex;
if ((i+1) mod 16)=0 then
begin
s.Add(hexdat+sepAsc+ascdat);
hexdat:='';
ascdat:='';
end;
end;
if (size mod 16)<>0 then
begin
if (size mod 16)<8 then
hexDat:=hexDat+StringOfChar(' ',(8-(size mod 8))*2)
+sepHex+StringOfChar(' ',16)
else
hexDat:=hexDat+StringOfChar(' ',(16-(size mod 16))*2);
s.Add(hexDat + sepAsc + ascDat);
end;
end;
And here is a complete code example for dumping the contents of a file into a Memo field.
procedure TForm1.Button1Click(Sender: TObject);
var
FStream: TFileStream;
buff: array[0..$fff] of Byte;
nRead: Integer;
begin
FStream := TFileStream.Create(edit1.text, fmOpenRead or fmShareDenyWrite);
try
repeat
nRead := FStream.Read(Buff, SizeOf(Buff));
if nRead<>0 then
hexdump(buff,nRead,memo1.lines);
until nRead=0;
finally
F.Free;
end;
end;
string is UnicodeString in Delphi 2009. If you want to use single-byte strings use AnsiString or RawByteString.
See String types.
I'm a Delphi programmer.
I have made a program who uses dictionaries with words and expressions (loaded in program as "array of string").
It uses a search algorithm based on their "checksum" (I hope this is the correct word).
A string is transformed in integer based on this:
var
FHashSize: Integer; //stores the value of GetHashSize
HashTable, HashTableNoCase: array[Byte] of Longword;
HashTableInit: Boolean = False;
const
AnsiLowCaseLookup: array[AnsiChar] of AnsiChar = (
#$00, #$01, #$02, #$03, #$04, #$05, #$06, #$07,
#$08, #$09, #$0A, #$0B, #$0C, #$0D, #$0E, #$0F,
#$10, #$11, #$12, #$13, #$14, #$15, #$16, #$17,
#$18, #$19, #$1A, #$1B, #$1C, #$1D, #$1E, #$1F,
#$20, #$21, #$22, #$23, #$24, #$25, #$26, #$27,
#$28, #$29, #$2A, #$2B, #$2C, #$2D, #$2E, #$2F,
#$30, #$31, #$32, #$33, #$34, #$35, #$36, #$37,
#$38, #$39, #$3A, #$3B, #$3C, #$3D, #$3E, #$3F,
#$40, #$61, #$62, #$63, #$64, #$65, #$66, #$67,
#$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
#$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77,
#$78, #$79, #$7A, #$5B, #$5C, #$5D, #$5E, #$5F,
#$60, #$61, #$62, #$63, #$64, #$65, #$66, #$67,
#$68, #$69, #$6A, #$6B, #$6C, #$6D, #$6E, #$6F,
#$70, #$71, #$72, #$73, #$74, #$75, #$76, #$77,
#$78, #$79, #$7A, #$7B, #$7C, #$7D, #$7E, #$7F,
#$80, #$81, #$82, #$83, #$84, #$85, #$86, #$87,
#$88, #$89, #$8A, #$8B, #$8C, #$8D, #$8E, #$8F,
#$90, #$91, #$92, #$93, #$94, #$95, #$96, #$97,
#$98, #$99, #$9A, #$9B, #$9C, #$9D, #$9E, #$9F,
#$A0, #$A1, #$A2, #$A3, #$A4, #$A5, #$A6, #$A7,
#$A8, #$A9, #$AA, #$AB, #$AC, #$AD, #$AE, #$AF,
#$B0, #$B1, #$B2, #$B3, #$B4, #$B5, #$B6, #$B7,
#$B8, #$B9, #$BA, #$BB, #$BC, #$BD, #$BE, #$BF,
#$C0, #$C1, #$C2, #$C3, #$C4, #$C5, #$C6, #$C7,
#$C8, #$C9, #$CA, #$CB, #$CC, #$CD, #$CE, #$CF,
#$D0, #$D1, #$D2, #$D3, #$D4, #$D5, #$D6, #$D7,
#$D8, #$D9, #$DA, #$DB, #$DC, #$DD, #$DE, #$DF,
#$E0, #$E1, #$E2, #$E3, #$E4, #$E5, #$E6, #$E7,
#$E8, #$E9, #$EA, #$EB, #$EC, #$ED, #$EE, #$EF,
#$F0, #$F1, #$F2, #$F3, #$F4, #$F5, #$F6, #$F7,
#$F8, #$F9, #$FA, #$FB, #$FC, #$FD, #$FE, #$FF);
implementation
function GetHashSize(const Count: Integer): Integer;
begin
if Count < 65 then
Result := 256
else
Result := Round(IntPower(16, Ceil(Log10(Count div 4) / Log10(16))));
end;
function Hash(const Hash: LongWord; const Buf; const BufSize: Integer): LongWord;
var P: PByte;
I: Integer;
begin
P := #Buf;
Result := Hash;
for I := 1 to BufSize do
begin
Result := HashTable[Byte(Result) xor P^] xor (Result shr 8);
Inc(P);
end;
end;
function HashStrBuf(const StrBuf: Pointer; const StrLength: Integer; const Slots: LongWord): LongWord;
var P: PChar;
I, J: Integer;
begin
if not HashTableInit then
InitHashTable;
P := StrBuf;
if StrLength <= 48 then // Hash all characters for short strings
Result := Hash($FFFFFFFF, P^, StrLength)
else
begin
// Hash first 16 bytes
Result := Hash($FFFFFFFF, P^, 16);
// Hash last 16 bytes
Inc(P, StrLength - 16);
Result := Hash(Result, P^, 16);
// Hash 16 bytes sampled from rest of string
I := (StrLength - 48) div 16;
P := StrBuf;
Inc(P, 16);
for J := 1 to 16 do
begin
Result := HashTable[Byte(Result) xor Byte(P^)] xor (Result shr 8);
Inc(P, I + 1);
end;
end;
// Mod into slots
if Slots <> 0 then
Result := Result mod Slots;
end;
procedure InitHashTable;
var I, J: Byte;
R: LongWord;
begin
for I := $00 to $FF do
begin
R := I;
for J := 8 downto 1 do
if R and 1 <> 0 then
R := (R shr 1) xor $EDB88320
else
R := R shr 1;
HashTable[I] := R;
end;
Move(HashTable, HashTableNoCase, Sizeof(HashTable));
for I := Ord('A') to Ord('Z') do
HashTableNoCase[I] := HashTableNoCase[I or 32];
HashTableInit := True;
end;
The result of the HashStrBuf is "and (FHashSize - 1)" and is used as index in an "array of array of Integer" (of FHashSize size) to store the index of the string from that "array of string".
This way, when searches for a string, it's transformed in "checksum" and then the code searches in the "branch" with this index comparing this string with the strings from dictionary who have the same "checksum".
Ideally each string from dictionary should have unique checksum. But in the "real world" about 2/3 share the same "checksum" with other words. Because of that the search is not that fast.
In these dictionaries strings are composed of this characters: ['a'..'z',#224..#246,#248..#254,#154,#156..#159,#179,#186,#191,#190,#185,'0'..'9', '''']
Is there any way to improve the "hashing" so the strings would have more unique "checksums"?
Oh, one way is to increase the size of that "array of array of Integer" (FHashSize) but it cannot be increased too much because it takes a lot of Ram.
Another thing: these dictionaries are stored on HDD only as words/expressions (not the "checksums"). Their "checksum" is generated at program startup. But it takes a lot of seconds to do that...
Is there any way to speed up the startup of the program? Maybe by improving the "hashing" function, maybe by storing the "checksums" on HDD and loading them from there...
Any input would be appreciated...
PS: here is the code to search:
function TDictionary.LocateKey(const Key: AnsiString): Integer;
var i, j, l, H: Integer;
P, Q: PChar;
begin
Result := -1;
l := Length(Key);
H := HashStrBuf(#Key[1], l, 0) and (FHashSize - 1);
P := #Key[1];
for i := 0 to High(FHash[H]) do //FHash is that "array of array of integer"
begin
if l <> FKeys.ItemSize[FHash[H][i]] then //FKeys.ItemSize is an byte array with the lengths of strings from dictionary
Continue;
Q := FKeys.Pointer(FHash[H][i]); //pointer to string in dictionary
for j := 0 to l - 1 do
if (P + j)^ <> (Q + j)^ then
Break;
if j = l then
begin
Result := FHash[H][i];
Exit;
end;
end;
end;
Don't reinvent the wheel!
IMHO your hashing is far from efficient, and your collision algorithm can be improved.
Take a look for instance at the IniFiles unit, and the THashedStringList.
It's a bit old, but a good start for a string list using hashes.
There are a lot of good Delphi implementation of such, like in SuperObject and a lot of other code...
Take a look at our SynBigTable unit, which can handle arrays of data in memory or in file very fast, with full indexed searches. Or our latest TDynArray wrapper around any dynamic array of data, to implement TList-like methods to it, including fast binary search. I'm quite sure it could be faster than your hand-tuned code using hashing, if you use an ordered index then fast binary search.
Post-Scriptum:
About pure hashing speed of a string content, take a look at this function - rename RawByteString into AnsiString, PPtrInt into PPointer, and PtrInt into Integer for Delphi 7:
function Hash32(const Text: RawByteString): cardinal;
function SubHash(P: PCardinalArray): cardinal;
{$ifdef HASINLINE}inline;{$endif}
var s1,s2: cardinal;
i, L: PtrInt;
const Mask: array[0..3] of cardinal = (0,$ff,$ffff,$ffffff);
begin
if P<>nil then begin
L := PPtrInt(PtrInt(P)-4)^; // fast lenght(Text)
s1 := 0;
s2 := 0;
for i := 1 to L shr 4 do begin // 16 bytes (4 DWORD) by loop - aligned read
inc(s1,P^[0]);
inc(s2,s1);
inc(s1,P^[1]);
inc(s2,s1);
inc(s1,P^[2]);
inc(s2,s1);
inc(s1,P^[3]);
inc(s2,s1);
inc(PtrUInt(P),16);
end;
for i := 1 to (L shr 2)and 3 do begin // 4 bytes (DWORD) by loop
inc(s1,P^[0]);
inc(s2,s1);
inc(PtrUInt(P),4);
end;
inc(s1,P^[0] and Mask[L and 3]); // remaining 0..3 bytes
inc(s2,s1);
result := s1 xor (s2 shl 16);
end else
result := 0;
end;
begin // use a sub function for better code generation under Delphi
result := SubHash(pointer(Text));
end;
There is even a pure asm version, even faster, in our SynCommons.pas unit. I don't know any faster hashing function around (it's faster than crc32/adler32/IniFiles.hash...). It's based on adler32, but use DWORD aligned reading and summing for even better speed. This could be improved with SSE asm, of course, but here is a fast pure Delphi hash function.
Then don't forget to use "multiplication"/"binary and operation" for hash resolution, just like in IniFiles. It will reduce the number of iteration to your list of hashs.
But since you didn't provide the search source code, we are not able to know what could be improved here.
If you are using Delphi 7, consider using Julian Bucknall's lovely Delphi data types code, EzDsl (Easy Data Structures Library).
Now you don't have to reinvent the wheel as another wise person has also said.
You can download ezdsl, a version that I have made work with both Delphi 7, and recent unicode delphi versions, here.
In particular the unit name EHash contains a hash table implementation, which has various hashing algorithms plug-inable, or you can write your own plugin function that just does the hashing function of your choice.
As a word to the wise, if you are using a Unicode Delphi version; I would be careful about hashing your unicode strings with a code library like this, without checking how its hashing algorithms perform on your system. The OP here is using Delphi 7, so Unicode is not a factor for the original question.
I think you'll find a database (without checksums) a lot quicker. Maybe try sqlite which will give you a single file database. There are many Delphi Libraries available.
I want to do the following,but I get errors:
procedure JumpToCodeCave(CurrentLocation:DWORD;Destination:Pointer;out_JmpBack:Pointer);
var calc:DWORD;
jmppatch:Array[0..3] of byte absolute calc;
Buffer:Array[0..9] of byte;
begin
calc := (Cardinal(Destination) - $5)-(CurrentLocation + $4);
Buffer := [$90,$90,$90,$90,$E9,jmppatch,$90]; //<< Error here <<
WriteProcessmemory(Handle,Pointer(CurrentLocation),Pointer(Buffer),10,nil);
out_JmpBack^ := Currentlocation + $A;
end;
Buffer should look like this:
0x90,0x90,0x90,0xE9,jmppatch[0],jmppatch[1],jmppatch[2],jmppatch[3],0x90
The function calculates the value that should be written to jump from one address(current) to another address(CodeCave).The result is converted into bytes and written into the process,but I can't put the bytes in the Buffer like I did above.
I'm sorry for the stupid question,but I have forgotten Delphi after I began my education with C#.
Delphi doesn't support array literals like that, especially not ones that would accept a four-byte value and turn it into four one-byte values.
You can have array constants, as Kcats's answer demonstrates. You can also have open-array literals, but then you can only pass it to a function expecting an open-array parameter.
I'd do something different, in your case. Code is not just an array of bytes. It has structure, so I'd make a record and give it fields for each of the instructions in the code.
type
TPatch = packed record
Nops: array [0..3] of Byte;
JmpInst: packed record
Opcode: Byte;
Offset: LongWord;
end;
Nop: Byte;
end;
const
Nop = $90;
Jmp = $e9;
var
Buffer: TPatch;
begin
// nop; nop; nop; nop;
FillChar(Buffer.Nops, SizeOf(Buffer.Nops), Nop);
// jmp xxxx
Buffer.JmpInst.Opcode := Jmp;
Buffer.JmpInst.Offset := LongWord(Destination) - SizeOf(Buffer.JmpInst)
- (CurrentLocation + SizeOf(Buffer.Nops));
// nop
Buffer.Nop := Nop;
WriteProcessmemory(Handle, Ptr(CurrentLocation), #Buffer, SizeOf(Buffer), nil);
end;
Even if you don't do all that, note that I've changed the third parameter of WriteProcessMemory. Your Buffer variable is not a pointer, so you really can't type-cast it to be one. You need to pass the address.
There's no way to assign the way you want to. Instead, use the Move() and FillMemory() procedures:
FillMemory(#Buffer[0], 4, $90);
Buffer[4] := $E9;
Move(Calc, Buffer[5], 4);
Buffer[9] := $90;
Note that I have removed the absolute variable, as it's no longer necessary.
You can't do that. Try something like:
var
Buffer:Array[0..9] of byte = ($90,$90,$90,$90,$E9,$CC,$CC,$CC,$CC,$90);
begin
PCardinal(#buffer[5])^ := (Cardinal(Destination) - $5)-(CurrentLocation + $4);
I would like to declare a record in Delphi that contains the same layout as it has in C.
For those interested : This record is part of a union in the Windows OS's LDT_ENTRY record. (I need to use this record in Delphi because I'm working on an Xbox emulator in Delphi - see project Dxbx on sourceforge).
Anyway, the record in question is defined as:
struct
{
DWORD BaseMid : 8;
DWORD Type : 5;
DWORD Dpl : 2;
DWORD Pres : 1;
DWORD LimitHi : 4;
DWORD Sys : 1;
DWORD Reserved_0 : 1;
DWORD Default_Big : 1;
DWORD Granularity : 1;
DWORD BaseHi : 8;
}
Bits;
As far as I know, there are no bit-fields possible in Delphi. I did try this:
Bits = record
BaseMid: Byte; // 8 bits
_Type: 0..31; // 5 bits
Dpl: 0..3; // 2 bits
Pres: Boolean; // 1 bit
LimitHi: 0..15; // 4 bits
Sys: Boolean; // 1 bit
Reserved_0: Boolean; // 1 bit
Default_Big: Boolean; // 1 bit
Granularity: Boolean; // 1 bit
BaseHi: Byte; // 8 bits
end;
But alas: it's size becomes 10 bytes, instead of the expected 4.
I would like to know how I should declare the record, so that I get a record with the same layout, the same size, and the same members. Preferrably without loads of getter/setters.
TIA.
Thanks everyone!
Based on this information, I reduced this to :
RBits = record
public
BaseMid: BYTE;
private
Flags: WORD;
function GetBits(const aIndex: Integer): Integer;
procedure SetBits(const aIndex: Integer; const aValue: Integer);
public
BaseHi: BYTE;
property _Type: Integer index $0005 read GetBits write SetBits; // 5 bits at offset 0
property Dpl: Integer index $0502 read GetBits write SetBits; // 2 bits at offset 5
property Pres: Integer index $0701 read GetBits write SetBits; // 1 bit at offset 7
property LimitHi: Integer index $0804 read GetBits write SetBits; // 4 bits at offset 8
property Sys: Integer index $0C01 read GetBits write SetBits; // 1 bit at offset 12
property Reserved_0: Integer index $0D01 read GetBits write SetBits; // 1 bit at offset 13
property Default_Big: Integer index $0E01 read GetBits write SetBits; // 1 bit at offset 14
property Granularity: Integer index $0F01 read GetBits write SetBits; // 1 bit at offset 15
end;
The index is encoded as follows : (BitOffset shl 8) + NrBits. Where 1<=NrBits<=32 and 0<=BitOffset<=31
Now, I can get and set these bits as follows :
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
function RBits.GetBits(const aIndex: Integer): Integer;
var
Offset: Integer;
NrBits: Integer;
Mask: Integer;
begin
NrBits := aIndex and $FF;
Offset := aIndex shr 8;
Mask := ((1 shl NrBits) - 1);
Result := (Flags shr Offset) and Mask;
end;
procedure RBits.SetBits(const aIndex: Integer; const aValue: Integer);
var
Offset: Integer;
NrBits: Integer;
Mask: Integer;
begin
NrBits := aIndex and $FF;
Offset := aIndex shr 8;
Mask := ((1 shl NrBits) - 1);
Assert(aValue <= Mask);
Flags := (Flags and (not (Mask shl Offset))) or (aValue shl Offset);
end;
Pretty nifty, don't you think?!?!
PS: Rudy Velthuis now included a revised version of this in his excellent "Pitfalls of converting"-article.
Rudy's Delphi Corner is the best resource I know of regarding Delphi and C/C++ interoperability. His Pitfalls of conversion is pretty much a must read when using C/C++ APIs in Delphi. The chapter you'll be most interested in is Records and alignment -> Bitfields, but I urge you to read the entire thing top to bottom, twice. The other articles are definitely worth the time investment, too.
Ok, my bit manipulation is a bit rusty, so I could have reversed the bytes. But the code below gives the general idea:
type
TBits = record
private
FBaseMid : Byte;
FTypeDplPres : Byte;
FLimitHiSysEa: Byte;
FBaseHi : Byte;
function GetType: Byte;
procedure SetType(const AType: Byte);
function GetDpl: Byte;
procedure SetDbl(const ADpl: Byte);
function GetBit1(const AIndex: Integer): Boolean;
procedure SetBit1(const AIndex: Integer; const AValue: Boolean);
function GetLimitHi: Byte;
procedure SetLimitHi(const AValue: Byte);
function GetBit2(const AIndex: Integer): Boolean;
procedure SetBit2(const AIndex: Integer; const AValue: Boolean);
public
property BaseMid: Byte read FBaseMid write FBaseMid;
property &Type: Byte read GetType write SetType; // 0..31
property Dpl: Byte read GetDpl write SetDbl; // 0..3
property Pres: Boolean index 128 read GetBit1 write SetBit1;
property LimitHi: Byte read GetLimitHi write SetLimitHi; // 0..15
property Sys: Boolean index 16 read GetBit2 write SetBit2;
property Reserved0: Boolean index 32 read GetBit2 write SetBit2;
property DefaultBig: Boolean index 64 read GetBit2 write SetBit2;
property Granularity: Boolean index 128 read GetBit2 write SetBit2;
property BaseHi: Byte read FBaseHi write FBaseHi;
end;
function TBits.GetType: Byte;
begin
Result := (FTypeDplPres shr 3) and $1F;
end;
procedure TBits.SetType(const AType: Byte);
begin
FTypeDplPres := (FTypeDplPres and $07) + ((AType and $1F) shr 3);
end;
function TBits.GetDpl: Byte;
begin
Result := (FTypeDplPres and $06) shr 1;
end;
procedure TBits.SetDbl(const ADpl: Byte);
begin
FTypeDblPres := (FTypeDblPres and $F9) + ((ADpl and $3) shl 1);
end;
function TBits.GetBit1(const AIndex: Integer): Boolean;
begin
Result := FTypeDplPres and AIndex = AIndex;
end;
procedure TBits.SetBit1(const AIndex: Integer; const AValue: Boolean);
begin
if AValue then
FTypeDblPres := FTypeDblPres or AIndex
else
FTypeDblPres := FTypeDblPres and not AIndex;
end;
function TBits.GetLimitHi: Byte;
begin
Result := (FLimitHiSysEa shr 4) and $0F;
end;
procedure TBits.SetLimitHi(const AValue: Byte);
begin
FLimitHiSysEa := (FLimitHiSysEa and $0F) + ((AValue and $0F) shr 4);
end;
function TBits.GetBit2(const AIndex: Integer): Boolean;
begin
Result := FLimitHiSysEa and AIndex = AIndex;
end;
procedure TBits.SetBit2(const AIndex: Integer; const AValue: Boolean);
begin
if AValue then
FLimitHiSysEa := FLimitHiSysEa or AIndex
else
FLimitHiSysEa := FLimitHiSysEa and not AIndex;
end;
Well, you basically need to get down to the dirty with bit-manipulation.
Why, specifically, do you need to retain that structure?
If you only need to talk to a legacy program that either talks in this dialect (TCP/IP or similar), or stores data in this manner (files, etc.), then I would map a normal Delphi structure to a bit-version compatible. In other words, I would use a normally structured Delphi structure in memory, and write code to write and read that structure in a compatible manner.
If you need to save memory, I would make getters and setters that manipulate bits of internal integers or similar. This will have a performance impact, but not much more than what the original C program would have, the only difference is that the bit-manipulation would be added by compiler magic in the C version, whereas you will have to write it yourself.
If you don't have many records in memory, and don't need to talk to another program, I'd use a natural Delphi structure. Trade-off for higher performance will be more memory used.
But it all depends on your criteria.
In any case, you won't be able to talk the Delphi compiler into doing the same job for you as the C compiler.
PACKED RECORD, suggested by another here, doesn't do that, and was never meant to. It will only remove alignment padding to put integers on 32-bit boundaries and similar, but won't pack multiple fields into one byte.
Note that a common way to do this is through Delphi SETS, which are implementing internally using bit-fields. Again, you will have different code than the C variant.