PDA

View Full Version : Access Violation when loading from TStream



Ultra
24-10-2004, 05:41 PM
Hi, I've got a very annoying problem which I can't seem to solve by myself. :(

I want to load a list of images of my own format (nothing fancy at all as you'll see). I get no error when saving (but there may very well be something in there) however when loading I get a strange AV error that I've not been able to find the cause of.

Anyway I suck at describing things so here's the code that should be the cause of the problem:

TDXImageList saving and loading:


procedure TDXImageList.SaveToStream(Stream: TStream);
var
i, len: Integer;
begin
len := FList.Count;
Stream.WriteBuffer(len, SizeOf(len));
for i := 0 to len -1 do
TDXImage(FList.Items[i]).SaveToStream(Stream);
end;

procedure TDXImageList.LoadFromStream(Stream: TStream);
var
i, len: Integer;
img: TDXImage;
begin
Clear; // clears of any images that already existed
Stream.ReadBuffer(len, SizeOf(len));
for i := 0 to len -1 do
begin
img := TDXImage.Create(Self);
img.LoadFromStream(Stream);
end;
end;


TDXImage saving and loading:


procedure TDXImage.SaveToStream(Stream: TStream);
begin
FImage.SaveToStream(Stream);
Stream.WriteBuffer(FWidth, SizeOf(FWidth));
Stream.WriteBuffer(FHeight, SizeOf(FHeight));
Stream.WriteBuffer(FTileWidth, SizeOf(FTileWidth));
Stream.WriteBuffer(FTileHeight, SizeOf(FTileHeight));
Stream.WriteBuffer(FTransparent, SizeOf(FTransparent));
Stream.WriteBuffer(FTransColor, SizeOf(FTransColor));
WriteStr(FName, Stream);
end;

procedure TDXImage.LoadFromStream(Stream: TStream);
begin
if Assigned(FImage) then FImage.Free;
if Assigned(FImageList) then
FImage := TMyImage.Create(FImageList.FHWnd)
else
FImage := TMyImage.Create(0);

FImage.LoadFromStream(Stream);
Stream.ReadBuffer(FWidth, SizeOf(FWidth));

Stream.ReadBuffer(FHeight, SizeOf(FHeight));
Stream.ReadBuffer(FTileWidth, SizeOf(FTileWidth));
Stream.ReadBuffer(FTileHeight, SizeOf(FTileHeight));
Stream.ReadBuffer(FTransparent, SizeOf(FTransparent));
Stream.ReadBuffer(FTransColor, SizeOf(FTransColor));
ReadStr(FName, Stream);
end;


TMyImage saving and loading;


procedure TMyImage.SaveToStream(Stream: TStream);
begin
Stream.WriteBuffer(FWidth, SizeOf(FWidth));
Stream.WriteBuffer(FHeight, SizeOf(FHeight));
Stream.WriteBuffer(FPixels[0, 0], SizeOf(FPixels[0, 0])*FWidth*FHeight);
end;

procedure TMyImage.LoadFromStream(Stream: TStream);
begin
Stream.ReadBuffer(FWidth, SizeOf(FWidth));
Stream.ReadBuffer(FHeight, SizeOf(FHeight));
SetLength(FPixels, FWidth, FHeight);
Stream.ReadBuffer(FPixels[0, 0], SizeOf(FPixels[0, 0])*FWidth*FHeight);
end;


Save/Load a string from a stream:


procedure ReadStr(var s: String; Stream: TStream);
var
len: Integer;
begin
Stream.Read(len, SizeOf(len));
SetLength(s, len);
Stream.Read(s[1], SizeOf(s[1]) * len);
end;

procedure WriteStr(const s: String; Stream: TStream);
var
len: Integer;
begin
len := Length(s);
Stream.Write(len, SizeOf(len));
Stream.Write(s[1], SizeOf(s[1]) * len);
end;

Robert Kosek
24-10-2004, 07:22 PM
TStream is an abstract class, use a "TFileStream" or "TMemoryStream" instead when calling the functions. IE ReadStr(thestring,thefile); so to speak.


procedure ReadStr(var s: String; Stream: TStream);
var
len: Integer;
begin
Stream.Read(len, SizeOf(len));
SetLength(s, len);
Stream.Read(pchar(s)^, len);
end;

procedure WriteStr(const s: String; Stream: TStream);
var
len: Integer;
begin
len := Length(s);
Stream.Write(len, SizeOf(len));
Stream.Write(pchar(s)^, len);
end;

This should fix your problems. It was the only error I really noticed. Remember to place before you load you image an:

If imagesize = 0 then {blank image code} to prevent errors.

If you still don't get it I'll post source to a small set of apps I made. Just so you know I am not a good commenter.

Ok, another thing I noticed. Your using:
Stream.ReadBuffer(FWidth, SizeOf(FWidth));
Stream.ReadBuffer(FHeight, SizeOf(FHeight));
SetLength(FPixels, FWidth, FHeight);
Stream.ReadBuffer(FPixels[0, 0], SizeOf(FPixels[0, 0])*FWidth*FHeight);

use this instead:
Stream.ReadBuffer(FWidth,SizeOf(Integer)); // IE the SIZEOF the data type, not the variable
//...
Stream.ReadBuffer(FPixels[0, 0], SizeOf(YourDataType{ie integer, cardinal, record}));


Robert

Ultra
24-10-2004, 08:53 PM
TStream is an abstract class, use a "TFileStream" or "TMemoryStream" instead when calling the functions. IE ReadStr(thestring,thefile); so to speak.


Actually I use a TFileStream. By using a TStream in then SaveTo/LoadFromStream methods I can use whatever TStream descendant I want without any problems. :)




Stream.Read(pchar(s)^, len);



This might work (I haven't tried it) but so does my other method. I've never had any problems using Stream.Read(s[1], len), might be different on different versions (I use Delphi 6). Anybody know if it works on Free Pascal?



If imagesize = 0 then {blank image code} to prevent errors.


Hmm... not really sure where I'd put that or if I need it. Even without any image data it should (hopefully ;) ) load without problems.



If you still don't get it I'll post source to a small set of apps I made. Just so you know I am not a good commenter.


Hehe, who is? :D



Ok, another thing I noticed. Your using:
Stream.ReadBuffer(FWidth, SizeOf(FWidth));
Stream.ReadBuffer(FHeight, SizeOf(FHeight));
SetLength(FPixels, FWidth, FHeight);
Stream.ReadBuffer(FPixels[0, 0], SizeOf(FPixels[0, 0])*FWidth*FHeight);

use this instead:
Stream.ReadBuffer(FWidth,SizeOf(Integer)); // IE the SIZEOF the data type, not the variable
//...
Stream.ReadBuffer(FPixels[0, 0], SizeOf(YourDataType{ie integer, cardinal, record}));



There shouldn't be any difference between SizeOf(<VarType>) and SizeOf(<VarName>). i use the latter because it allows me to change the type of VarName without having to worry about changing alot of SizeOf()'s.

Thanks for trying to help though. :D

Robert Kosek
24-10-2004, 09:13 PM
Actually I use a TFileStream. By using a TStream in then SaveTo/LoadFromStream methods I can use whatever TStream descendant I want without any problems. I actualy was trying to say this.

Well I'm a hobbiest and don't exactly know the best ways (I learned from books and by simply trying things).

Writing a string as a pchar pointer:
Stream.Write(pchar(s)^, len);
Writing on a perchar basis:
Stream.Write(s[1], SizeOf(s[1]) * len);

I learned the writing strings and streams while messing with custom made solid archives (my own format). It took a while to come to the best method, yet I don't know if it's really the "best".