Legacy GM help figuring out buffer save/load system

D

Dengar

Guest
im creating a buffer save/load system that saves sprites and variables to a single external file.

below is what I have sofar. its just doing variables and it works

but how do I handle sprites? I understand I have to draw sprites to a surface and use buffer_get_surface and buffer_set_surface somehow. also ive read that I can only put 1 surface in a buffer? so im guessing I must draw all my sprites to the surface?

and I think my final issue is there will be an unknown number of variables and sprites that ill be adding/reading from the buffer, how does that work, im guessing I can somehow detect how many values are in the buffer and then simply loop through the buffer that many times and read them?

Code:
///save
var f;
 f = get_save_filename("play_data|*.pldat", "");
 if f != ""
    {
    buff = buffer_create(16384, buffer_grow, 2);
    buffer_write(buff, buffer_string, "vari 1");
    buffer_write(buff, buffer_string, "vari 2");
    buffer_write(buff, buffer_string, "vari 3");
    buffer_save(buff, f);
    buffer_delete(buff);
    }
Code:
///load
var f;
 f = get_open_filename("play_data|*.pldat", "");
 if f != ""
    {
    buff = buffer_load(f);
    var cmd = buffer_read(buff, buffer_string );
    show_message(cmd);
    var cmdd = buffer_read(buff, buffer_string );
    show_message(cmdd);
    var cmddd = buffer_read(buff, buffer_string );
    show_message(cmddd);
    buffer_delete(buff);
    }
 
Last edited by a moderator:

TheouAegis

Member
You shouldn't have anything be unknown - you are coding the project yourself. That said, you would write data sizes to the buffer before writing the data, so when you read from the buffer you will know how big each section is. If it's questionable what data type will be there, save section headers before the data.

Be aware you can't modify the data in the buffer if it contains a surface.
 
D

Dengar

Guest
You shouldn't have anything be unknown
I was thinking things like inventory. but Itd probably be far easier to just lump inventory into a single value instead of a value for each inventory item.

Be aware you can't modify the data in the buffer if it contains a surface.
I don't think id need to modify it if im just creating it to save, or loading that saved one.
but what exacly do you mean by "modify"?
 
Last edited by a moderator:

TheouAegis

Member
I was thinking things like inventory. but Itd probably be far easier to just lump inventory into a single value instead of a value for each inventory item.


I don't think id need to modify it if im just creating it to save, or loading that saved one.
but what exacly do you mean by "modify"?
I just mean change any values in it. There's some weird issues with surface buffers in GMS1 that they never tried to fix.
 
D

Dengar

Guest
I just mean change any values in it.
right now the only way ive gotten it to work is by creating the buffer(grow) size sprite_height*sprite_width*4 ,then I add the surface, and then I add the string data.
it works however the pixels in the top left corner of the sprite vanish and the more string data I add the more vanishes. im assuming im somehow over writing the surface, ive tried buffer_seek to get it to add after the surface but it doesn't work. it just makes the string data blanks and periods. is this what your talking about?
 
H

Homunculus

Guest
Why do you need to save sprites to begin with? Are those not available anymore when you restart the game?
 
D

Dengar

Guest
Why do you need to save sprites to begin with?
its so I can load them into a game that didn't have them when it was compiled.


i think i have a cheaty way to get around my problem. if im saving a 32x32 sprite, i create a surface 32x64 and save the sprite to the bottom 32x32 of the surface, then add my string data. that way the string data overwrites the blank section at the top of the surface and my sprites remain intact. i feel like this is a cheap workaround
 
Last edited by a moderator:
H

Homunculus

Guest
Before assuming it’s gm’s fault i’d like to see the code you use when adding and reading sprites in the buffer.

It seems to me that buffer_get_surface doesn’t move the seek position, and therefore when you write your data you are in fact overwriting the image data with your values. Try moving the seek position manually after adding the image.

Edit: it is also worth noting that depending on your specific requirements there are definitely other options. You could serialize the image in base64 and save it as a string in the buffer.

Another option, if for example the sprites are created from a URL, is to save them to a local cache directory as images, and whenever you require that sprite again you check the cache first before trying to download.
 
Last edited by a moderator:
D

Dengar

Guest
Before assuming it’s gm’s fault i’d like to see the code you use when adding and reading sprites in the buffer.
I don't think this is a problem with gamemaker, I think im not doing it correctly.

It seems to me that buffer_get_surface doesn’t move the seek position, and therefore when you write your data you are in fact overwriting the image data with your values. Try moving the seek position manually after adding the image.
ive tried that but if I add surface, seek to the end, and then add strings. when I try to read the string data it just comes back as blanks and periods.


heres my exact code.
Code:
//---save-----
var f;
 f = get_save_filename("play_data|*.pldat", "");
 if f != ""
    {
    buff = buffer_create(32 * 32 * 4, buffer_grow, 1);
    surf=surface_create(32,32);
   
   
   
    //images
    surface_set_target(surf);
    draw_clear_alpha(c_black, 0);
    draw_sprite(sprite0,0,0,0); //draw to surface
    buffer_get_surface(buff, surf, 0, 0, 0);
   
    //variables
    //buffer_seek(buff, buffer_seek_end, 0);
    buffer_write(buff, buffer_string, "vari 1");
    buffer_write(buff, buffer_string, "vari 2");
    buffer_write(buff, buffer_string, "vari 3");
   
   
   
   
    buffer_save(buff, f);
    buffer_delete(buff);
    surface_reset_target();
    surface_free(surf);
    }
Code:
//----load------
var f;
 f = get_open_filename("play_data|*.pldat", "");
 if f != ""
    {
    buff = buffer_load(f);
    surf=surface_create(32,32);
   
   
    //images
    surface_set_target(surf);
    draw_clear_alpha(c_black, 0);
    buffer_set_surface(buff, surf, 0, 0, 0);
   
    //spr_custom = sprite_create_from_surface(surf, 0, 32, 32, 32, false, true, 0, 0);
    spr_up = sprite_create_from_surface(surf, 0, 0, 32, 32, false, true, 0, 0);
    sprite_index=spr_up;
   
   
    //variables
    var cmd = buffer_read(buff, buffer_string );
    show_message(cmd);
    var cmdd = buffer_read(buff, buffer_string );
    show_message(cmdd);
    var cmddd = buffer_read(buff, buffer_string );
    show_message(cmddd);
   
   
   
   
    buffer_delete(buff);
    surface_reset_target();
    surface_free(surf);
    }
 
Last edited by a moderator:
H

Homunculus

Guest
Posting some code could help. I just tested and I'm definitely able to write a sprite followed by a string and a number, and read it back.
 
Last edited by a moderator:

TheouAegis

Member
When you create the buffer and then write the image data to the buffer, d
there are two arguments which should pad the surface buffer. Did you try messing with those?
 
D

Dengar

Guest
Posting some code could help. I just tested and I'm definitely able to write a sprite followed by a string and a number, and read it back.
I just posted the exact code im using. im confused, did you test my code or create your own, if you created your own then howd you do it to avoid my problem? if you used mine, did you get the same issue with the string data overwriting the surface?

there are two arguments which should pad the surface buffer. Did you try messing with those?
probably not, where are you talking?
 
H

Homunculus

Guest
I swear the code wasn’t there when i replied. Odd.

Anyways:

- you definitely need that buffer seek in the save script, or you’ll overwrite the surface with your strings

- you also need a buffer seek in the load script, after reading the surface, or you’ll be reading the surface instead of your strings.

Other than that it is more or less the same thing i did in my test
 
D

Dengar

Guest
- you definitely need that buffer seek in the save script, or you’ll overwrite the surface with your strings
gotcha.

- you also need a buffer seek in the load script, after reading the surface, or you’ll be reading the surface instead of your strings.
this is probably the part im missing. how do I seek to the end of the surface?

im testing it now, and since im creating a surface 32*32*4. I tried buffer_seek_start with an offset of 32*32*4 and it seems to be working, is this correct?
 
H

Homunculus

Guest
You don’t, unless you know the size beforehand. To test just seek from the start to 32*32*4.

Afterwards you need to define you own buffer format, with sprites being preceded by a header telling their width and height, so you can 1. Create the surface 2. Copy the amount of bytes taken by the surface to another temporary buffer (since buffer_set_surface doesn’t allow reading a specific amount of bytes) 3. Read the surface 4. Seek to the end of the surface data and continue reading the original buffer

Sorry but I have to ask: can’t you simply save the images to the hard drive and just store their path into the buffer?

EDIT: You may want to test if gm is smart enough to compute the amout of bytes to read based on the surface size. The manual does say “Copies ALL the data from the buffer”, but it doesn’t make sense to copy more data than the surface can fit. Try without using the temp buffer, if it works, great!
Maybe @Nocturne can enlighten us on this?
 
Last edited by a moderator:
H

Homunculus

Guest
Edited the post with some more info that could help simpify the process.

I’d also consider the following structure instead of adding the sprites directly from a surface:

- save the sprites to disk to a temporary folder in the sandbox
- add them back into your buffer using buffer_load_ext, along with header data containing just the image size (no need to store width and height)
- finally, save the buffer and delete the temporary folder

When reading, you just have to “extract” the sprites to disk again in a temp folder and simply use sprite_add.

This has the advantage of resulting in a much smaller file size, sinze you are storing png images. Surfaces on the other hand store raw pixel data, which can make your buffer HUGE very easily. It MAY be a bit slower due to hard drive writes / reads being involved though.
 
D

Dengar

Guest
you need to define you own buffer format, with sprites being preceded by a header telling their width and height
how do headers work? im looking through the manual now but im not finding anything
 
H

Homunculus

Guest
By headers I simply mean writing the image information (width & height) before the actual surface
 

TheouAegis

Member
buffer_get_surface(buffer, surface, mode, offset, modulo);

I don't rhink you can write to the end of the file, only the end of the lines (defined by the surface) or the very start of the buffer. Changing the "offset" argument would let you add a header at the start. Not sure what the "modulo" argument would be useful for. Also not sure how buffer width is retained...
 
H

Homunculus

Guest
I was actually referring to buffer_set_surface. The documentation says that it "Copies all data from the buffer", but it's clearly misleading since in fact it copies just as much data as required to recreate the surface image.

The documentation should also mention in my opinion that both buffer_get_surface and buffer_set_surface do not change the seek position.
 
D

Dengar

Guest
By headers I simply mean writing the image information (width & height) before the actual surface
Changing the "offset" argument would let you add a header at the start.
Gotcha, im having a little trouble offsetting the surface. I can pull the u8 value before the surface and the string data after the surface, but the surface doesn't come out right. I think these are all the spots im trying to offset.

Code:
buff = buffer_create(32 * 32 * 4 + 8, buffer_grow, 1);
buffer_write(buff, buffer_u8, 32);
buffer_get_surface(buff, surf, 0, 8, 0);

buffer_set_surface(buff, surf, 0, 8, 0);
buffer_seek(buff, buffer_seek_start, 32*32*4+8);
 
Last edited by a moderator:

Simon Gust

Member
To my knowledge. The only way for buffer_get_surface and buffer_set_surface to work is that the buffer is the size of the surface.
 
Top