Upgrade for file of records and backward compatability - delphi

I have such file:
file of record
Str: string[250];
RecType: Cardinal;
end;
but after some time using of this file my customer found, that Str never be bigger than 100 chars and also he need additional fields.
In new version we have such file:
file of packed record
Str: string[200];
Reserved: array[1..47] of Byte;
NewFiled: Cardinal;
RecType: Cardinal;
end;
This record have the same size, in previous record between Str and RecType was one unused byte when aligned to 8 bytes.
Question: what happened, when this new file will be readed from old code? He need backward compatability.
Old code reading sample:
var
FS: TFileStream;
Rec: record
Str: string[250];
RecType: Cardinal;
end;
...
// reading record by record from file:
FS.Read(Rec, SizeOf(Rec));

The old school pascal string use the first byte of the string (index 0) to store the length of the string.
Let's look at the memory of this record:
byte 0 1 2 3 4 5 6 7 8 9 10 11 12 13 ........ 243..246 247..250
value 10 65 66 67 68 69 70 71 72 73 74 0 200 130 NewField RecType
From byte 11 to 242, the memory can contain garbage, it is simply ignored by the program (never shown) as this takes the value 10 at the byte 0 as the length of the string, so the string becomes 'ABCDEFGHIJ'
This ensures the old program reading a file created with the most recent version will never see garbage at the end of the strings, since the view of that strings will be limited to the actual size of the string and that memory positions are just ignored.
You have to double check if the old program does not change the values stored in case it writes the records back to the file. I think it is also safe, but I'm just not sure and have no Delphi at hand to test.

Related

Is it Possible to convert a set of numbers to their ASCII Char values?

I can convert a string into the Ascii values with spaces in between.
Example: "I like carrots"
To: 73 32 108 105...
But I'm trying to figure out a way to take those NUMBERS and convert them BACK into their ascii Chars.
I'm using the latest version of Delphi (embarcadero) and am new to coding so pls help me out :)
You can use the Ord function in every character.
The Chr function returns the character for specified ASCII value.
Example
var
s: string;
c: char;
s:='I like carrots';
for c in s do
begin
Writeln(ord(c));
end;
Here is the code:
var
S1 : String;
S2 : String;
A : TStringDynArray; // Add System.Types in uses
N : String;
C : Char;
begin
// 'I like carrots' in ascii code separated by a space
S1 := '73 32 108 105 107 101 32 99 97 114 114 111 116 115';
A := SplitString(S1, ' '); // Add System.StrUtils in uses
S2 := '';
for N in A do
S2 := S2 + Char(StrToInt(N));
ShowMessage(S2);
end;
I don't know the complete problem but you could add code to check of valid input string.
I don't know much about delphi, but take a look at this link: http://www.delphibasics.co.uk/Article.asp?Name=Text
In the Assigning to and from character variables section it demonstrates a function like so:
fromNum := Chr(65); // Assign using a function.
If that doesn't work, you could consider building a large mapping of int->char. The beginnings of which is in that above website also near the top.

missed data received using Serial comport 411f delphi 7

I have a Delphi 7 code that receives sensor values from an DSP TMS32F28069. The value received by Delphi is Hex file data. For example I send data:
F1;01;01;07;00;00;0A;00;00;00;00;F7
from DSP.
I use Comport 411f and actually when I use windows 10 64 bit english version everything is fine. But when I use windows chinese 64 bit, the data that received sometimes fine sometimes change. I have try on several notebook using windows 7 64 bit chinese version, and it has the same problem. The received files on windows 7 64 bit chinese version showing:
F1;01;01;01;00;00;00;F7;00;00;F7;00.or F1;01;07;01;00;0A;00;00;F7;F7;00;00
and always change.This is the code I wrote in Delphi 7:
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
p:integer;
r:array[1..12]of integer;
h:array[1..12]of String;
begin
comport1.Open;
for p:=1 to 12 do
begin
comport1.Read(r[p],1);
h[p]:= IntToHex((r[p]),2);
sMemo3.Text:= h[1]+';'+h[2]+';'+h[3]+';'+h[4]+';'+h[5]+';'+h[6]+';'+h[7]+';'+h[8]+';'+h[9]+';'+h[10]+';'+h[11]+';'+h[12];//Show data Receive on Memo4//
end;
end;
Please give me any suggestion why this happened on windows 7 64 bit chinese version? because when I use windows 7 64 bit english version, it was also work fine.
Thank you
Remove comport1.Open - it is undoubtedly opened if RxChar event occurs
Local integer array is filled with some crap. comport1.Read(r[p],1); fills only one byte. So use byte array
You output full data array after every byte - it is strange method.
When event fires, port buffer contains Count bytes - so read real number of bytes. Better approach - accumulate received info in global array (or ansistring) and treat it when 12 bytes are received.
Buffer: AnsiString;
...
procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer);
var
sa: AnsiString;
ByteBuf: array[1..12] of Byte;
begin
SetLength(sa, Count);
comport1.Read(sa[1], Count);
Buffer := Buffer + sa;
while Length(Buffer) >= 12 do begin
Move(Buffer[1], ByteBuf, 12);
TreatData(ByteBuf);
Delete(Buffer, 1, 12);
end;
end;
procedure TreatData(bb: array of Byte);
//treat and output here

Enumeration set size in x64

I found that a SizeOf(set) i different in 32-bit and 64-bit, the example below shows 5 byte for 32-bit and 8 for 64-bit. But i found nothing information about changes in SizeOf(sets) for 64-bit. Is there any Embarcadero documentation about it or compiler directive to get a similar results on 32 and 64-bit.
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses System.SysUtils;
type
{ Enumeration of properties}
TProperty1 = (p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14,
p15, p16, p17, p18, p19, p20, p21, p22, p23, p24, p25, p26, p27, p28,
p29, p30, p31, p32, p33, p34, p35, p36, p37);
TProperties1 = set of TProperty1;
begin
WriteLn(SizeOf(TProperties1));
ReadLn;
end.
To answer your question. I couldn't find anything on the Embarcadero site regarding the differences or a compiler directive to change the behavior. My research indicates the following:
Sets have the following sizes in bytes in 32 bit:
Up to 8 elements - 1 Byte
9 to 16 elements - 2 Bytes
17 to 32 elements - 4 Bytes
From this point onwards it adds adds bytes as needed, one at a time. So 33 to 40 elements uses 5 bytes and 41 to 48 elements uses 6 bytes.
In 64 bit mode, things are slightly different:
Up to 8 elements - 1 Byte
9 to 16 elements - 2 Bytes
17 to 32 elements - 4 Bytes
33 to 64 elements - 8 Bytes
From this point onwards it adds adds bytes as needed, one at a time. So 65 to 72 elements uses 9 bytes and 73 to 80 elements uses 10 bytes.
To get around this you are going to need to either use something like WriteSet in TWriter.WriteProperty and TReader.ReadSet or you can do something like this:
procedure SaveSetToStream(aStream: TStream; const aSet: TProperties1);
var
streamData: array[0..7] of byte;
begin
Assert(SizeOf(aSet) <= SizeOf(streamData), 'Set is too large to save. Increase the array length.');
FillChar(streamData, SizeOf(streamData), 0);
Move(aSet, streamData, SizeOf(aSet));
aStream.Write(streamData, SizeOf(streamData));
end;
function ReadFromStream(aStream: TStream): TProperties1;
var
streamData: array[0..7] of byte;
begin
Assert(SizeOf(Result) <= SizeOf(streamData), 'Set is too large to load. Increase the array length.');
aStream.Read(streamData, SizeOf(streamData));
Move(streamData, Result, SizeOf(Result));
end;
Another workaround, to make sure a 32 bit machine can read a stream from a 64 bit machine and vice-versa is to create a function
function SizeCheck( const p : integer ) : integer;
begin
if p in [5..8 ] then Result := 8 else Result := p; // adjust for 64 bit set sizes
end;
and then use
Stream.Write(set, SizeCheck(SizeOf(set)));
Obviously only use for sets.

I am searching for a Perl-like split-function in Delphi

I know there a many ways to split a String, so that you'll get a StringList.
But my problem is that I want to split every character of the string.
That means the following String:
'That is my Example String'
should be converted to an array/Stringlist or what so ever:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
T h a t i s m y E x a m p l e S t r i n g
In Perl or Java the delimiter field of the split-function is just be let empty like
for example:
Perl: #string=split("",$string);
Java: String[] myStringArray=myString.split("");
What would be the best way for Delphi to manage this?
Usually there is no real need for such function in Delphi, because Delphi string behaves as char array, every element is accessible by index, and char is assignment compatible with string. Thereby you can use s[i] instead of splited[i] in almost all cases.
If you do need special function to fill a list, it may look like
procedure SplitStringEx(const s: string; Splitted: TStrings);
var
i: Integer;
begin
Splitted.Clear;
for i := 1 to Length(s) do
{possible variant for fresh Delph versions
to take zero-based strings into account:}
//for i := Low(s) to High(s) do
Splitted.Add(s[i])
end;
usage
SplitStringEx('abc', Memo1.Lines);

.wav sample data value starting index?

I've been use this code to read .wav data in delphi and i've been comparing the result with value i got from matlab function wavread. From which i can say that matlab function can automatically recognize which one is the sample data value, but not with delphi (but both matlab and delphi code result is the same). Since my delphi code cannot recognize the sample data value, i look through the array and found out that the index where sample data value starting were different each .wav file. For an example i test some .wav file and get this:
classic1.wav the sample data value starting on wavedata[].Data[] index number 40
classic2.wav the sample data value starting on wavedata[].Data[] index number 35
i got above conclusion by looking at the result y,[y, Fs, nbits, opts]=wavread('classic1.wav','double'); then i go to the result delphi return in an array checking it value and find the exact same value starting in the index 40 for classic1.wav and 35 for classic2.wav. And i want to know if there is a way i can know the starting index of sample data value of each .wav file?
EDIT : i have corrected the record similar to the reference given, it's perfectly right with the header(from ChunkID to Subchunk2size) but i still got confused by the sample data following it because no change from the previous result.
type
TWaveHeader = packed record
Marker_RIFF: array [0..3] of char;
ChunkSize: cardinal;
Marker_WAVE: array [0..3] of char;
Marker_fmt: array [0..3] of char;
SubChunkSize: cardinal;
FormatTag: word;
NumChannels: word;
SampleRate: longint;
ByteRate: longint;
BlockAlign:word;
BitsPerSample: word;
Marker_data: array [0..3] of char;
DataBytes: longint;
end;
TChannel = record
Data : array of smallint;
end;
You're obviously not skipping all of the header fields correctly. Wav files can have some optional header information, so though the actual sample values normally start at byte 44, this is not always the case.
See here for example: https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
One way to skip directly to the sample data (after reading whatever parts of the header you require) is the scan the file (four bytes at a time) for the ascii string of "data" (64 61 74 61 hex) and then read the 4 bytes that immediately follow that, which (as cardinal or longword) represents the total number of bytes to read. The actual samples follow immediately after that cardinal.
Edit:
As expected, looking at the files Classic1.wav and Classic2.wav in a hex editor it's clear that they both have some metadata. At location 36 in each file, instead of finding the SubchunkID of "data" you instead find "LIST". The four bytes following this give the size of this additional data. This is what you have to skip over in order to get to the music sample data.
For example, Classic1.wav has 148 bytes of extra data starting at offset 44. This places Subchunk2ID at offset 192 and Subchunk2Size at offset 196, meaning that the first sample starts at offset 200 in the file.
Classic2.wav has 128 bytes of extra data starting at offset 44. This places Subchunk2ID at offset 172 and Subchunk2Size at offset 176, meaning that the first sample starts at offset 180 in the file.
Here is Classic2.wav in a very basic hex editor:
Rather than doing all of the file I/O manually, you should use the Win32 Multimedia API functions instead - mmioOpen(), mmioDescend(), mmioAscend(), mmioRead(), etc. Let them do all of the hard work for you. Your code will be easier to manage and read, as you will be able to focus more on the content of the individual chunks while letting the API handle the low-level details of finding each chunk for you.

Resources