Changes between Initial Version and Version 1 of Develop/LoadSaveSystem


Ignore:
Timestamp:
Oct 8, 2016, 1:27:13 PM (8 years ago)
Author:
pulkomandy
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Develop/LoadSaveSystem

    v1 v1  
     1= How Grafx2 load and saves files =
     2
     3Adding a new file format to Grafx2 is quite easy. Let's see how to do it.
     4
     5The first step is to declare your format. Open *const.h*, this is the file where
     6all our constants are defined. Increment NB_KNOWN_FORMATS, NB_FORMATS_LOAD and
     7NB_FORMATS_SAVE. Beware, they are defined twice because we have an optional
     8dependancy on libpng. In this file you must also add your format to the
     9*FILE_FORMATS* enum, in the form FORMAT_EXT (replacing ext with the extension,
     10of course). Don't add them after PNG, don't add them before PKM.
     11
     12The second step is to declare the format so the program makes use of it. This is
     13done in loadsave.c. Declare your format handler functions. They have no
     14parameters and return no value. Everything is done with global vars. You should
     15provide three functions:
     16
     17  * Test_EXT tells if you can load the file,
     18  * Load_EXT loads a picture,
     19  * Save_EXT saves a picture.
     20
     21You have to register your format in the File_formats table. The fields are:
     22  * extension (lowercase, wildcards `*` and ? allowed)
     23  * Test function
     24  * Load function
     25  * Save function
     26  * Save all picture (should be 1 most of the time, set it to 0 for formats not saving all the data, for example .PAL saves only the palette). You should put a 0 if your format use a lossy compression, like jpeg.
     27  * Comment allowed. Put an 1 if your format can save a comment in the file. 0 else.
     28
     29The last step is to code the three functions. Here are the global vars you will
     30probably need to use.
     31
     32== Test function ==
     33Use Get_full_filename to get the filename we are asking you to test.
     34Load your file, do some reading on it to check it is coherent (checking the
     35header is enough). Set the global var File_error to 0 if you think you can load
     36the file, or 1 else.
     37Don't forget to close your file and free things you allocated (if you did. is
     38your format so difficult to identify ?)
     39
     40{{{#!c
     41void Test_EXT(void)
     42{
     43        FILE* file;
     44        char filename[MAX_PATH_CHARACTERS];
     45        long file_size;
     46
     47        Get_full_filename(filename,0);
     48
     49        file = fopen(filename,"rb");
     50
     51        if(file)
     52        {
     53                // Do some more tests to see if everything is ok
     54                if((/*...*/)
     55                        File_error = 0;
     56                else
     57                        File_error = 1;
     58                fclose(file)
     59        }
     60        else
     61                File_error = 1;
     62}
     63}}}
     64
     65== Load function ==
     66This one is a bit more tricky. You have to handle some things for our preview
     67window. You can still set File_error while loading the file if something goes wrong, that's why you don't have to go too far in the Test function. Get the filename the same way with Get_full_filename, read the data.
     68You have to set some variables and call some functions, of course.
     69
     70{{{#!c
     71void Load_EXT(void)
     72{
     73        FILE* file;
     74        char filename[MAX_PATH_CHARACTERS];
     75        long file_size;
     76
     77        Get_full_filename(filename,0);
     78
     79        file = fopen(filename,"rb");
     80
     81        if(file)
     82        {
     83                int file_size = File_length_file(file);
     84                T_Palette pal;
     85                word width, height;
     86                word x, y;
     87                byte* buffer
     88
     89                // read the header
     90
     91                Init_preview(width, height, filesize, FORMAT_EXT); // Do this as soon as you can
     92
     93                Main_image_width = width ;
     94                Main_image_height = height;
     95
     96                buffer = malloc(width);
     97
     98                // Read one line at a time to avoid useless i/o.
     99                // But you can do otherwise if your format isn't friendly enough.
     100                for(y=0; y<height; y++)
     101                {
     102                        Read_bytes(file,buffer,width);
     103                        for(x=0; x<width; x++)
     104                                Pixel_load_function(x,y,buffer[x]);
     105                }
     106
     107                free(buffer);
     108
     109                memcpy(Main_palette,pal); // this set the software palette for grafx2
     110                Set_palette(Main_palette); // this set the hardware palette for SDL
     111                Remap_fileselector(); // Always call it if you change the palette
     112               
     113                File_error = 0;
     114                fclose(file);
     115        }
     116        else
     117                File_error = 1;
     118}
     119}}}
     120
     121So, to sum up :
     122  * Set Main_image_width and Main_image_height to the appropriate value (the maximum image size is 10 000x10 000 pixels),
     123  * Call Init_preview(width, height, filesize, FORMAT_EXT),
     124  * Load your palette as {byte R, byte G, byte B}[256] in Main_palette then call Set_palette(Main_palette),
     125  * Load the image data calling Pixel_load_function(x,y,color) for each pixel, in any order you want.
     126
     127== Save function ==
     128It's simpler than the load, as you don't have to handle a preview.
     129As usual, get the filename, open the file, write everything, then close the file. Set File_error if something went bad (permission denied on fopen for example). Don't forget to close file and free any buffer.
     130
     131== Helper function and endianness ==
     132As you've seen in the examples, we dont use fread and fwrite directly. There is a good reason: these functions don't care about endianness.
     133We provide functions that do it properly. They all return 1 if everything went fine and 0 if they fail.
     134
     135{{{#!c
     136int Read_bytes(FILE *file, void *dest, size_t size);
     137int Write_bytes(FILE *file, void *dest, size_t size);
     138
     139int Read_byte(FILE *file, byte *dest);
     140int Write_byte(FILE *file, byte b);
     141
     142int Read_word_le(FILE *file, word *dest);
     143int Write_word_le(FILE *file, word w);
     144int Read_dword_le(FILE *file, dword *dest);
     145int Write_dword_le(FILE *file, dword dw);
     146
     147int Read_word_be(FILE *file, word *dest);
     148int Write_word_be(FILE *file, word w);
     149int Read_dword_be(FILE *file, dword *dest);
     150int Write_dword_be(FILE *file, dword dw);
     151}}}
     152
     153Read_bytes read a number of bytes without any endianness processing. Use that to process the main pixel data of your image if applicable, to avoid many io requests to the OS.
     154
     155Read_byte reads one single byte. No endianness problem.
     156
     157The `*_`le functions read and write little-endian as used on x86, while `*_`be read and write big endian as on 68000. Check your format info to see what you need to use. Note some functions in our current loadsave.c loads and save a lot of things (for example a whole header with read/write_bytes() and then parse it. Please don't do that. It's not clean, it's error prone, and you will encounter struct-packing headaches. Dont use `__`attribute(`__`packed`__`), the linux/sparc version will crash if you do.