diff --git a/src/Makefile b/src/Makefile index 7559d1f3..9d769a6e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -810,6 +810,7 @@ OBJS = main.o init.o graph.o $(APIOBJ) misc.o special.o \ transform.o pversion.o factory.o $(PLATFORMOBJ) \ loadsave.o loadsavefuncs.o \ pngformat.o motoformats.o stformats.o c64formats.o cpcformats.o \ + ifformat.o \ fileformats.o miscfileformats.o libraw2crtc.o \ brush_ops.o buttons_effects.o layers.o \ oldies.o tiles.o colorred.o unicode.o gfx2surface.o \ @@ -822,6 +823,7 @@ TESTSOBJS = $(patsubst %.c,%.o,$(wildcard tests/*.c)) \ miscfileformats.o fileformats.o oldies.o libraw2crtc.o \ loadsavefuncs.o packbits.o tifformat.o c64load.o 6502.o \ pngformat.o motoformats.o stformats.o c64formats.o cpcformats.o \ + ifformat.o \ op_c.o colorred.o \ unicode.o \ io.o realpath.o version.o pversion.o \ diff --git a/src/fileformats.c b/src/fileformats.c index 7a7a7f4b..2e955821 100644 --- a/src/fileformats.c +++ b/src/fileformats.c @@ -75,6 +75,8 @@ #define MIN(a,b) ((a)<(b)?(a):(b)) #endif +void Draw_IFF_line(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes); + //////////////////////////////////// IMG //////////////////////////////////// // -- Tester si un fichier est au format IMG -------------------------------- @@ -217,2325 +219,6 @@ void Save_IMG(T_IO_Context * context) } -//////////////////////////////////// IFF //////////////////////////////////// -/** @defgroup IFF Interchange File Format - * @ingroup loadsaveformats - * Interchange File Format (Electronic Arts) - * - * This is the "native" format of Amiga. - * We support ILBM/PBM/ACBM - * @{ - */ -typedef struct -{ - word Width; - word Height; - word X_org; // Inutile - word Y_org; // Inutile - byte BitPlanes; - byte Mask; // 0=none, 1=mask, 2=transp color, 3=Lasso - byte Compression; // 0=none, 1=packbits, 2=vertical RLE - byte Pad1; // Inutile - word Transp_col; // transparent color for masking mode 2 - byte X_aspect; // Inutile - byte Y_aspect; // Inutile - word X_screen; - word Y_screen; -} T_IFF_Header; - -typedef struct -{ - byte operation; // 0=normal body, 1=XOR, 2=Long Delta, 3=Short Delta, - // 4=Generalized Long/Short Delta, 5=Byte Vertical Delta, - // 7=short/long vertical delta, 74=Eric Graham's compression - byte mask; // for XOR mode only - word w,h; // for XOR mode only - word x,y; // for XOR mode only - dword abstime; - dword reltime; - byte interleave; - byte pad0; - dword bits; -} T_IFF_AnimHeader; - -/// Test if a file is in IFF format -void Test_IFF(FILE * IFF_file, const char *sub_type) -{ - char format[4]; - char section[4]; - dword dummy; - - File_error=1; - - if (! Read_bytes(IFF_file,section,4)) - return; - if (memcmp(section,"FORM",4)) - return; - - if (! Read_dword_be(IFF_file, &dummy)) - return; - // On aurait pu vérifier que ce long est égal à la taille - // du fichier - 8, mais ça aurait interdit de charger des - // fichiers tronqués (et déjà que c'est chiant de perdre - // une partie du fichier il faut quand même pouvoir en - // garder un peu... Sinon, moi je pleure :'( !!! ) - if (! Read_bytes(IFF_file,format,4)) - return; - - if (!memcmp(format,"ANIM",4)) - { - dword size; - - // An ANIM header: need to check that it encloses an image - do - { - if (! Read_bytes(IFF_file,section,4)) - return; - if (memcmp(section,"FORM",4)) - return; - if (! Read_dword_be(IFF_file, &size)) - return; - if (! Read_bytes(IFF_file,format,4)) - return; - if (fseek(IFF_file, size - 4, SEEK_CUR) < 0) - return; - } - while(0 == memcmp(format, "8SVX", 4)); // skip sound frames - } - else if(memcmp(format,"DPST",4) == 0) - { - if (! Read_bytes(IFF_file,section,4)) - return; - if (memcmp(section, "DPAH", 4) != 0) - return; - if (! Read_dword_be(IFF_file, &dummy)) - return; - fseek(IFF_file, dummy, SEEK_CUR); - if (! Read_bytes(IFF_file,section,4)) - return; - if (memcmp(section,"FORM",4)) - return; - if (! Read_dword_be(IFF_file, &dummy)) - return; - if (! Read_bytes(IFF_file,format,4)) - return; - } - if ( memcmp(format,sub_type,4)) - return; - - // If we reach this part, file is indeed ILBM/PBM or ANIM - File_error=0; -} - -void Test_PBM(T_IO_Context * context, FILE * f) -{ - (void)context; - Test_IFF(f, "PBM "); -} -void Test_LBM(T_IO_Context * context, FILE * f) -{ - (void)context; - Test_IFF(f, "ILBM"); -} -void Test_ACBM(T_IO_Context * context, FILE * f) -{ - (void)context; - Test_IFF(f, "ACBM"); -} - - -// -- Lire un fichier au format IFF ----------------------------------------- - -typedef struct T_IFF_PCHG_Palette { - struct T_IFF_PCHG_Palette * Next; - short StartLine; - T_Components Palette[1]; -} T_IFF_PCHG_Palette; - -/// Skips the current section in an IFF file. -/// This function should be called while the file pointer is right -/// after the 4-character code that identifies the section. -static int IFF_Skip_section(FILE * file) -{ - dword size; - - if (!Read_dword_be(file,&size)) - return 0; - if (size&1) - size++; - if (fseek(file,size,SEEK_CUR)) - return 0; - return 1; -} - -/// Wait for a specific IFF chunk -static byte IFF_Wait_for(FILE * file, const char * expected_section) -{ - // Valeur retournée: 1=Section trouvée, 0=Section non trouvée (erreur) - byte section_read[4]; - - if (! Read_bytes(file,section_read,4)) - return 0; - while (memcmp(section_read,expected_section,4)) // Sect. pas encore trouvée - { - if (!IFF_Skip_section(file)) - return 0; - if (! Read_bytes(file,section_read,4)) - return 0; - } - return 1; -} - -// Les images ILBM sont stockés en bitplanes donc on doit trifouiller les bits pour -// en faire du chunky - -/// -/// Decodes the color of one pixel from the ILBM line buffer. -/// Planar to chunky conversion -/// @param buffer Planar buffer -/// @param x_pos Position of the pixel in graphic line -/// @param real_line_size Width of one bitplane in memory, in bytes -/// @param bitplanes Number of bitplanes -static dword Get_IFF_color(const byte * buffer, word x_pos, word real_line_size, byte bitplanes) -{ - byte shift = 7 - (x_pos & 7); - int address,masked_bit,plane; - dword color=0; - - for(plane=bitplanes-1;plane>=0;plane--) - { - address = (real_line_size * plane + x_pos) >> 3; - masked_bit = (buffer[address] >> shift) & 1; - - color = (color << 1) + masked_bit; - } - - return color; -} - -/// chunky to planar -void Set_IFF_color(byte * buffer, word x_pos, byte color, word real_line_size, byte bitplanes) -{ - byte shift = 7 - (x_pos & 7); - int address, plane; - - for(plane=0;plane> 3; - buffer[address] |= (color&1) << shift; - color = color >> 1; - } -} - -// ----------------------- Afficher une ligne ILBM ------------------------ -/// Planar to chunky conversion of a line -static void Draw_IFF_line(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes) -{ - short x_pos; - - if (bitplanes > 8) - { - for (x_pos=0; x_posWidth; x_pos++) - { - // Default standard deep ILBM bit ordering: - // saved first -----------------------------------------------> saved last - // R0 R1 R2 R3 R4 R5 R6 R7 G0 G1 G2 G3 G4 G5 G6 G7 B0 B1 B2 B3 B4 B5 B6 B7 - dword rgb = Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); - Set_pixel_24b(context, x_pos,y_pos, rgb, rgb >> 8, rgb >> 16); // R is 8 LSB, etc. - } - } - else for (x_pos=0; x_posWidth; x_pos++) - { - Set_pixel(context, x_pos,y_pos,Get_IFF_color(buffer, x_pos,real_line_size, bitplanes)); - } -} - -/// decode pixels with palette changes per line (copper list:) -static void Draw_IFF_line_PCHG(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes, const T_IFF_PCHG_Palette * PCHG_palettes) -{ - const T_IFF_PCHG_Palette * palette; - short x_pos; - - palette = PCHG_palettes; // find the palette to use for the line - if (palette == NULL) - return; - while (palette->Next != NULL && palette->Next->StartLine <= y_pos) - palette = palette->Next; - - for (x_pos=0; x_posWidth; x_pos++) - { - dword c = Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); - Set_pixel_24b(context, x_pos,y_pos, palette->Palette[c].R, palette->Palette[c].G, palette->Palette[c].B); - } -} - -/// Decode a HAM line to 24bits pixels -static void Draw_IFF_line_HAM(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes, const T_IFF_PCHG_Palette * PCHG_palettes) -{ - short x_pos; - byte red, green, blue, temp; - const T_Components * palette; - - if (PCHG_palettes == NULL) - palette = context->Palette; - else - { - // find the palette to use for the line - while (PCHG_palettes->Next != NULL && PCHG_palettes->Next->StartLine <= y_pos) - PCHG_palettes = PCHG_palettes->Next; - palette = PCHG_palettes->Palette; - } - red = palette[0].R; - green = palette[0].G; - blue = palette[0].B; - if (bitplanes == 6) - { - for (x_pos=0; x_posWidth; x_pos++) // HAM6 - { - temp=Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); - switch (temp & 0x30) - { - case 0x10: // blue - blue=(temp&0x0F)*0x11; - break; - case 0x20: // red - red=(temp&0x0F)*0x11; - break; - case 0x30: // green - green=(temp&0x0F)*0x11; - break; - default: // Nouvelle couleur - red=palette[temp].R; - green =palette[temp].G; - blue =palette[temp].B; - } - Set_pixel_24b(context, x_pos,y_pos,red,green,blue); - } - } - else - { - for (x_pos=0; x_posWidth; x_pos++) // HAM8 - { - temp=Get_IFF_color(buffer,x_pos,real_line_size, bitplanes); - switch (temp >> 6) - { - case 0x01: // blue - blue= (temp << 2) | ((temp & 0x30) >> 4); - break; - case 0x02: // red - red= (temp << 2) | ((temp & 0x30) >> 4); - break; - case 0x03: // green - green= (temp << 2) | ((temp & 0x30) >> 4); - break; - default: // Nouvelle couleur - red=palette[temp].R; - green =palette[temp].G; - blue =palette[temp].B; - } - Set_pixel_24b(context, x_pos,y_pos,red,green,blue); - } - } -} - -/// Decode PBM data -/// -/// Supports compressions : -/// - 0 uncompressed -/// - 1 compressed -static void PBM_Decode(T_IO_Context * context, FILE * file, byte compression, word width, word height) -{ - byte * line_buffer; - word x_pos, y_pos; - word real_line_size = (width+1)&~1; - - switch (compression) - { - case 0: // uncompressed - line_buffer=(byte *)malloc(real_line_size); - for (y_pos=0; ((y_pos127) - { - if(Read_byte(file, &color)!=1) - { - File_error=28; - break; - } - do { - Set_pixel(context, x_pos++,y_pos,color); - } - while(temp_byte++ != 0); - } - else - do - { - if(Read_byte(file, &color)!=1) - { - File_error=29; - break; - } - Set_pixel(context, x_pos++,y_pos,color); - } - while(temp_byte-- > 0); - } - } - break; - default: - Warning("PBM only supports compression type 0 and 1"); - File_error = 50; - } -} - -/// Decode LBM data -/// -/// Supports packings : -/// - 0 uncompressed -/// - 1 packbits (Amiga) -/// - 2 Vertical RLE (Atari ST) -static void LBM_Decode(T_IO_Context * context, FILE * file, byte compression, byte Image_HAM, - byte stored_bit_planes, byte real_bit_planes, const T_IFF_PCHG_Palette * PCHG_palettes) -{ - int plane; - byte * buffer; - short x_pos, y_pos; - // compute row size - int real_line_size = (context->Width+15) & ~15; // size in bit for one bit plane - int plane_line_size = real_line_size >> 3; // size in byte for one bit plane - int line_size = plane_line_size * stored_bit_planes; // size in byte for all bitplanes - - switch(compression) - { - case 0: // uncompressed - buffer=(byte *)malloc(line_size); - if (buffer == NULL) - { - File_error=1; - Warning("Failed to allocate memory for IFF decoding"); - return; - } - for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) - { - if (Read_bytes(file,buffer,line_size)) - { - if (Image_HAM > 1) - Draw_IFF_line_HAM(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); - else if (PCHG_palettes) - Draw_IFF_line_PCHG(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); - else - Draw_IFF_line(context, buffer, y_pos,real_line_size, real_bit_planes); - } - else - File_error=21; - } - free(buffer); - break; - case 1: // packbits compression (Amiga) - buffer=(byte *)malloc(line_size); - if (buffer == NULL) - { - File_error=1; - Warning("Failed to allocate memory for IFF decoding"); - return; - } - for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) - { - for (x_pos=0; ((x_pos 127 => repeat (257-temp_byte) the next byte - // temp_byte <= 127 => copy (temp_byte + 1) bytes - if(temp_byte == 128) // 128 = NOP ! - { - Warning("NOP in packbits stream"); - } - else if (temp_byte>127) - { - if(Read_byte(file, &color)!=1) - { - File_error=23; - break; - } - do - { - if (x_pos=line_size || !Read_byte(file, &(buffer[x_pos++]))) - { - File_error=25; - break; - } - } - while (temp_byte-- > 0); - } - if (!File_error) - { - if (Image_HAM > 1) - Draw_IFF_line_HAM(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); - else if (PCHG_palettes) - Draw_IFF_line_PCHG(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); - else - Draw_IFF_line(context, buffer, y_pos,real_line_size,real_bit_planes); - } - } - free(buffer); - break; - case 2: // vertical RLE compression (Atari ST) - buffer=(byte *)calloc(line_size*context->Height, 1); - if (buffer == NULL) - { - File_error=1; - Warning("Failed to allocate memory for IFF decoding"); - return; - } - for (plane = 0; plane < stored_bit_planes && !File_error; plane++) - { - dword section_size; - word cmd_count; - word cmd; - signed char * commands; - word count; - - y_pos = 0; x_pos = 0; - if (!IFF_Wait_for(file, "VDAT")) - { - if (plane == 0) // don't cancel loading if at least 1 bitplane has been loaded - File_error = 30; - break; - } - Read_dword_be(file,§ion_size); - Read_word_be(file,&cmd_count); - cmd_count -= 2; - commands = (signed char *)malloc(cmd_count); - if (!Read_bytes(file,commands,cmd_count)) - { - File_error = 31; - break; - } - section_size -= (cmd_count + 2); - for (cmd = 0; cmd < cmd_count && x_pos < plane_line_size && section_size > 0; cmd++) - { - if (commands[cmd] <= 0) - { // cmd=0 : load count from data, COPY - // cmd < 0 : count = -cmd, COPY - if (commands[cmd] == 0) - { - Read_word_be(file,&count); - section_size -= 2; - } - else - count = -commands[cmd]; - while (count-- > 0 && x_pos < plane_line_size && section_size > 0) - { - Read_bytes(file,buffer+x_pos+y_pos*line_size+plane*plane_line_size,2); - section_size -= 2; - if(++y_pos >= context->Height) - { - y_pos = 0; - x_pos += 2; - } - } - } - else if (commands[cmd] >= 1) - { // cmd=1 : load count from data, RLE - // cmd >1 : count = cmd, RLE - byte data[2]; - if (commands[cmd] == 1) - { - Read_word_be(file,&count); - section_size -= 2; - } - else - count = (word)commands[cmd]; - if (section_size == 0) - break; - Read_bytes(file,data,2); - section_size -= 2; - while (count-- > 0 && x_pos < plane_line_size) - { - memcpy(buffer+x_pos+y_pos*line_size+plane*plane_line_size,data,2); - if(++y_pos >= context->Height) - { - y_pos = 0; - x_pos += 2; - } - } - } - } - if(cmd < (cmd_count-1) || section_size > 0) - Warning("Early end in VDAT chunk"); - if (section_size > 0) - fseek(file, (section_size+1)&~1, SEEK_CUR); // skip bytes - free(commands); - } - if (!File_error) - { - for (y_pos = 0; y_pos < context->Height; y_pos++) - { - Draw_IFF_line(context,buffer+line_size*y_pos,y_pos,real_line_size,real_bit_planes); - } - } - free(buffer); - break; - default: - Warning("Unknown IFF compression"); - File_error = 32; - } -} - -/// Decode RAST chunk (from Atari ST pictures) -static void RAST_chunk_decode(T_IO_Context * context, FILE * file, dword section_size, T_IFF_PCHG_Palette ** PCHG_palettes) -{ - int i; - T_Components palette[16]; - T_IFF_PCHG_Palette * prev_pal = NULL; - T_IFF_PCHG_Palette * new_pal = NULL; - - // 17 words per palette : 1 for line, 16 for the colors - while (section_size >= 34) - { - word line, value; - - Read_word_be(file, &line); - for (i = 0; i < 16; i++) - { - Read_word_be(file, &value); // Decode STE Palette - palette[i].R = ((value & 0x0700) >> 7 | (value & 0x0800) >> 11) * 0x11; - palette[i].G = ((value & 0x0070) >> 3 | (value & 0x0080) >> 7) * 0x11; - palette[i].B = ((value & 0x0007) << 1 | (value & 0x0008) >> 3) * 0x11; - } - section_size -= 34; - - if (prev_pal == NULL || (line > prev_pal->StartLine && (0 != memcmp(palette, prev_pal->Palette, sizeof(T_Components)*3)))) - { - new_pal = malloc(sizeof(T_IFF_PCHG_Palette) + sizeof(T_Components) * 16); - if (new_pal == NULL) - { - File_error = 2; - return; - } - memcpy(new_pal->Palette, palette, sizeof(T_Components) * 16); - new_pal->StartLine = line; - new_pal->Next = NULL; - if (prev_pal != NULL) - { - prev_pal->Next = new_pal; - prev_pal = new_pal; - } - else if (line == 0) - { - prev_pal = new_pal; - *PCHG_palettes = prev_pal; - } - else // line > 0 && prev_pal == NULL - { - // create a palette for line 0 - prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + sizeof(T_Components) * 16); - if (prev_pal == NULL) - { - File_error = 2; - return; - } - memcpy(prev_pal->Palette, context->Palette, sizeof(T_Components) * 16); - prev_pal->StartLine = 0; - prev_pal->Next = new_pal; - *PCHG_palettes = prev_pal; - prev_pal = new_pal; - } - } - } -} - -/// Sets 32 upper colors of EHB palette -static void IFF_Set_EHB_Palette(T_Components * palette) -{ - int i, j; /// 32 colors in the palette. - for (i=0; i<32; i++) /// The next 32 colors are the same with values divided by 2 - { - j=i+32; - palette[j].R = palette[i].R>>1; - palette[j].G = palette[i].G>>1; - palette[j].B = palette[i].B>>1; - } -} - -/// Load IFF picture (PBM/ILBM/ACBM) or animation -void Load_IFF(T_IO_Context * context) -{ - FILE * IFF_file; - T_IFF_Header header; - T_IFF_AnimHeader aheader; - char format[4]; - char section[4]; - byte temp_byte; - dword nb_colors = 0; // number of colors in the CMAP (color map) - dword section_size; - short x_pos; - short y_pos; - short line_size = 0; // Taille d'une ligne en octets - short plane_line_size = 0; // Size of line in bytes for 1 plane - short real_line_size = 0; // Taille d'une ligne en pixels - unsigned long file_size; - dword dummy; - int iff_format = 0; - int plane; - dword AmigaViewModes = 0; - enum PIXEL_RATIO ratio = PIXEL_SIMPLE; - byte * buffer; - byte bpp = 0; - byte Image_HAM = 0; - T_IFF_PCHG_Palette * PCHG_palettes = NULL; - int current_frame = 0; - byte * previous_frame = NULL; // For animations - byte * anteprevious_frame = NULL; - word frame_count = 0; - word frame_duration = 0; - word vdlt_plane = 0; // current plane during Atari ST animation decoding - - memset(&aheader, 0, sizeof(aheader)); - - File_error=0; - - if ((IFF_file=Open_file_read(context))) - { - file_size=File_length_file(IFF_file); - - // FORM + size(4) - Read_bytes(IFF_file,section,4); - Read_dword_be(IFF_file,&dummy); - - Read_bytes(IFF_file,format,4); - if (!memcmp(format,"ANIM",4)) - { - // Skip a bit, brother - Read_bytes(IFF_file,section,4); - Read_dword_be(IFF_file,§ion_size); - Read_bytes(IFF_file,format,4); - while (0 == memcmp(format, "8SVX", 4)) - { - GFX2_Log(GFX2_DEBUG, "IFF : skip sound %.4s %u bytes\n", format, section_size); - if (fseek(IFF_file, section_size - 4, SEEK_CUR) < 0) - break; - Read_bytes(IFF_file,section,4); - Read_dword_be(IFF_file,§ion_size); - Read_bytes(IFF_file,format,4); - } - } - else if(memcmp(format,"DPST",4)==0) - { - // read DPAH - while (File_error == 0) - { - if (!(Read_bytes(IFF_file,section,4) - && Read_dword_be(IFF_file,§ion_size))) - File_error = 1; - if (memcmp(section, "FORM", 4) == 0) - { - Read_bytes(IFF_file,format,4); - break; - } - else if (memcmp(section, "DPAH", 4) == 0) // Deluxe Paint Animation Header - { - word version; - Read_word_be(IFF_file, &frame_duration); - Read_word_be(IFF_file, &frame_count); - Read_word_be(IFF_file, &version); - section_size -= 6; - Set_frame_duration(context, (frame_duration * 50) / 3); // convert 1/60th sec to msec - } - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // skip unread bytes - } - } - if (memcmp(format,"ILBM",4) == 0) - iff_format = FORMAT_LBM; - else if(memcmp(format,"PBM ",4) == 0) - iff_format = FORMAT_PBM; - else if(memcmp(format,"ACBM",4) == 0) - iff_format = FORMAT_ACBM; - else - { - GFX2_Log(GFX2_WARNING, "Unknown IFF format '%.4s'\n", format); - File_error=1; - } - - - { - byte real_bit_planes = 0; - byte stored_bit_planes = 0; - while (File_error == 0 - && Read_bytes(IFF_file,section,4) - && Read_dword_be(IFF_file,§ion_size)) - { - if (memcmp(section, "FORM", 4) == 0) - { - // special - Read_bytes(IFF_file, format, 4); - if (memcmp(format, "VDLT", 4) == 0) // Vertical DeLTa - { // found in animation produced by DeluxePaint for Atari ST - if (frame_count != 0 && current_frame >= (frame_count - 1)) - break; // file contains the Delta from the last frame to the 1st one to allow looping, skip it - - real_line_size = (context->Width+15) & ~15; - plane_line_size = real_line_size >> 3; // 8bits per byte - line_size = plane_line_size * real_bit_planes; - - if (previous_frame == NULL) - { - previous_frame = calloc(line_size * context->Height,1); - for (y_pos=0; y_posHeight; y_pos++) - { - const byte * pix_p = context->Target_address + y_pos * context->Pitch; - // Dispatch the pixel into planes - for (x_pos=0; x_posWidth; x_pos++) - { - Set_IFF_color(previous_frame+y_pos*line_size, x_pos, *pix_p++, real_line_size, real_bit_planes); - } - } - } - - Set_loading_layer(context, ++current_frame); - Set_frame_duration(context, (frame_duration * 50) / 3); // convert 1/60th sec to msec - vdlt_plane = 0; - } - continue; - } - if (memcmp(section, "BMHD", 4) == 0) // BitMap HeaDer - { - if (!((Read_word_be(IFF_file,&header.Width)) - && (Read_word_be(IFF_file,&header.Height)) - && (Read_word_be(IFF_file,&header.X_org)) - && (Read_word_be(IFF_file,&header.Y_org)) - && (Read_byte(IFF_file,&header.BitPlanes)) - && (Read_byte(IFF_file,&header.Mask)) - && (Read_byte(IFF_file,&header.Compression)) - && (Read_byte(IFF_file,&header.Pad1)) - && (Read_word_be(IFF_file,&header.Transp_col)) - && (Read_byte(IFF_file,&header.X_aspect)) - && (Read_byte(IFF_file,&header.Y_aspect)) - && (Read_word_be(IFF_file,&header.X_screen)) - && (Read_word_be(IFF_file,&header.Y_screen))) ) - { - File_error = 1; - break; - } - GFX2_Log(GFX2_DEBUG, "IFF BMHD : origin (%u,%u) size %ux%u BitPlanes=%u Mask=%u Compression=%u\n", - header.X_org, header.Y_org, header.Width, header.Height, - header.BitPlanes, header.Mask, header.Compression); - GFX2_Log(GFX2_DEBUG, " Transp_col=#%u aspect ratio %u/%u screen size %ux%u\n", - header.Transp_col, header.X_aspect, header.Y_aspect, - header.X_screen, header.Y_screen); - real_bit_planes = header.BitPlanes; - stored_bit_planes = header.BitPlanes; - if (header.Mask==1) - stored_bit_planes++; - Image_HAM=0; - if (header.X_aspect != 0 && header.Y_aspect != 0) - { - if ((10 * header.X_aspect) <= (6 * header.Y_aspect)) - ratio = PIXEL_TALL; // ratio <= 0.6 - else if ((10 * header.X_aspect) <= (8 * header.Y_aspect)) - ratio = PIXEL_TALL3; // 0.6 < ratio <= 0.8 - else if ((10 * header.X_aspect) < (15 * header.Y_aspect)) - ratio = PIXEL_SIMPLE; // 0.8 < ratio < 1.5 - else - ratio = PIXEL_WIDE; // 1.5 <= ratio - } - bpp = header.BitPlanes; - if (bpp <= 8 && bpp > 0) - { - unsigned int i; - // Set a default grayscale palette : if the - // ILBM/PBM file has no palette, it should be assumed to be a - // Gray scale picture. - if (Config.Clear_palette) - memset(context->Palette,0,sizeof(T_Palette)); - nb_colors = 1 << bpp; - for (i = 0; i < nb_colors; i++) - context->Palette[i].R = - context->Palette[i].G = - context->Palette[i].B = (i * 255) / (nb_colors - 1); - } - } - else if (memcmp(section, "ANHD", 4) == 0) // ANimation HeaDer - { - // http://www.textfiles.com/programming/FORMATS/anim7.txt - Read_byte(IFF_file, &aheader.operation); - Read_byte(IFF_file, &aheader.mask); - Read_word_be(IFF_file, &aheader.w); - Read_word_be(IFF_file, &aheader.h); - Read_word_be(IFF_file, &aheader.x); - Read_word_be(IFF_file, &aheader.y); - Read_dword_be(IFF_file, &aheader.abstime); - Read_dword_be(IFF_file, &aheader.reltime); - Read_byte(IFF_file, &aheader.interleave); - Read_byte(IFF_file, &aheader.pad0); - Read_dword_be(IFF_file, &aheader.bits); - section_size -= 24; - - if ((aheader.bits & 0xffffffc0) != 0) // invalid ? => clearing - aheader.bits = 0; - if (aheader.operation == 0) // ANHD for 1st frame (BODY) - Set_frame_duration(context, (aheader.reltime * 50) / 3); // convert 1/60th sec to msec - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes - } - else if (memcmp(section, "DPAN", 4) == 0) // Deluxe Paint ANimation - { - word version; - dword flags; - if (section_size >= 8) - { - Read_word_be(IFF_file, &version); - Read_word_be(IFF_file, &frame_count); - Read_dword_be(IFF_file, &flags); - section_size -= 8; - } - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes - } - else if (memcmp(section, "DLTA", 4) == 0) // Animation DeLTA - { - int i, plane; - dword offsets[16]; - dword current_offset = 0; - byte * frame; - - if (Image_HAM > 1) - { - Warning("HAM animations are not supported"); - //Verbose_message("Notice", "HAM animations are not supported, loading only first frame"); // TODO: causes an issue with colors - break; - } - if (frame_count != 0 && current_frame >= (frame_count - 1)) - break; // some animations have delta from last to first frame - if (previous_frame == NULL) - { - real_line_size = (context->Width+15) & ~15; - plane_line_size = real_line_size >> 3; // 8bits per byte - line_size = plane_line_size * real_bit_planes; - - previous_frame = calloc(line_size * context->Height,1); - for (y_pos=0; y_posHeight; y_pos++) - { - const byte * pix_p = context->Target_address + y_pos * context->Pitch; - // Dispatch the pixel into planes - for (x_pos=0; x_posWidth; x_pos++) - { - Set_IFF_color(previous_frame+y_pos*line_size, x_pos, *pix_p++, real_line_size, real_bit_planes); - } - } - // many animations are designed for double buffering - // and delta is against frame n-2 - anteprevious_frame = malloc(line_size * context->Height); - memcpy(anteprevious_frame, previous_frame, line_size * context->Height); - } - - Set_loading_layer(context, ++current_frame); - Set_frame_duration(context, (aheader.reltime * 50) / 3 ); // convert 1/60th sec in msec - frame = previous_frame; - - if(aheader.operation == 5) // Byte Vertical Delta mode - { - if (aheader.interleave != 1) - frame = anteprevious_frame; - for (i = 0; i < 16; i++) - { - if (!Read_dword_be(IFF_file, offsets+i)) - { - File_error = 2; - break; - } - current_offset += 4; - } - for (plane = 0; plane < 16; plane++) - { - byte op_count = 0; - if (offsets[plane] == 0) - continue; - if (plane >= real_bit_planes) - { - GFX2_Log(GFX2_WARNING, "IFF : too much bitplanes in DLTA data %u >= %u (frame #%u/%u)\n", plane, real_bit_planes, current_frame + 1, frame_count); - break; - } - while (current_offset < offsets[plane]) - { - Read_byte(IFF_file, &op_count); - current_offset++; - } - if (current_offset > offsets[plane]) - { - Warning("Loading ERROR in DLTA data"); - File_error = 2; - break; - } - for (x_pos = 0; x_pos < (context->Width+7) >> 3; x_pos++) - { - byte * p = frame + x_pos + plane * plane_line_size; - y_pos = 0; - Read_byte(IFF_file, &op_count); - current_offset++; - for (i = 0; i < op_count; i++) - { - byte op; - - if (y_pos >= context->Height) - { - } - Read_byte(IFF_file, &op); - current_offset++; - if (op == 0) - { // Same ops - byte countb, datab; - Read_byte(IFF_file, &countb); - Read_byte(IFF_file, &datab); - current_offset += 2; - while(countb > 0 && y_pos < context->Height) - { - if(aheader.bits & 2) // XOR - *p ^= datab; - else // set - *p = datab; - p += line_size; - y_pos++; - countb--; - } - } - else if (op & 0x80) - { // Uniq Ops - op &= 0x7f; - while (op > 0) - { - byte datab; - Read_byte(IFF_file, &datab); - current_offset++; - if (y_pos < context->Height) - { - if(aheader.bits & 2) // XOR - *p ^= datab; - else // set - *p = datab; - p += line_size; - y_pos++; - } - op--; - } - } - else - { // skip ops - p += op * line_size; - y_pos += op; - } - } - if (y_pos > context->Height) - { - } - } - } - } - else if(aheader.operation==74) - { - // from sources found on aminet : - // http://aminet.net/package/gfx/conv/unmovie - while (current_offset < section_size) - { - word change_type; - word uni_flag; - word y_size; - word x_size; - word num_blocks; - word offset; - word x_start, y_start; - - Read_word_be(IFF_file, &change_type); - current_offset += 2; - if (change_type == 0) - break; - else if (change_type == 1) - { // "Wall" - Read_word_be(IFF_file, &uni_flag); - Read_word_be(IFF_file, &y_size); - Read_word_be(IFF_file, &num_blocks); - current_offset += 6; - while (num_blocks-- > 0) - { - Read_word_be(IFF_file, &offset); - current_offset += 2; - x_start = offset % plane_line_size; - y_start = offset / plane_line_size; - for (plane = 0; plane < real_bit_planes; plane++) - { - byte * p = frame + plane * plane_line_size; - p += x_start + y_start*line_size; - for (y_pos=0; y_pos < y_size; y_pos++) - { - byte value; - Read_byte(IFF_file, &value); - current_offset++; - if (uni_flag) - *p ^= value; - else - *p = value; - } - p += line_size; - } - } - } - else if (change_type == 2) - { // "Pile" - Read_word_be(IFF_file, &uni_flag); - Read_word_be(IFF_file, &y_size); - Read_word_be(IFF_file, &x_size); - Read_word_be(IFF_file, &num_blocks); - current_offset += 8; - while (num_blocks-- > 0) - { - Read_word_be(IFF_file, &offset); - current_offset += 2; - x_start = offset % plane_line_size; - y_start = offset / plane_line_size; - for (plane = 0; plane < real_bit_planes; plane++) - { - byte * p = frame + plane * plane_line_size; - p += x_start + y_start*line_size; - for (y_pos=0; y_pos < y_size; y_pos++) - { - for (x_pos=0; x_pos < x_size; x_pos++) - { - byte value; - Read_byte(IFF_file, &value); - current_offset++; - if (uni_flag) - p[x_pos] ^= value; - else - p[x_pos] = value; - } - p += line_size; - } - } - } - } - else - { - Warning("Unknown change type in type 74 DLTA"); - File_error = 2; - break; - } - if (current_offset & 1) // align to WORD boundary - { - byte junk; - Read_byte(IFF_file, &junk); - current_offset++; - } - } - } - else - { - GFX2_Log(GFX2_INFO, "IFF DLTA : Unsupported compression type %u\n", aheader.operation); - } - - if (File_error == 0) - { - for (y_pos=0; y_posHeight; y_pos++) - { - Draw_IFF_line(context, frame+line_size*y_pos,y_pos,real_line_size,real_bit_planes); - } - } - if (aheader.operation == 5 && aheader.interleave != 1) - { - anteprevious_frame = previous_frame; - previous_frame = frame; - } - if (current_offset&1) - { - byte dummy_byte; - Read_byte(IFF_file, &dummy_byte); - current_offset++; - } - section_size -= current_offset; - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it - } - else if (memcmp(section, "ADAT", 4) == 0) - { // Animation made with Deluxe Paint for Atari ST - buffer = previous_frame; - if (section_size > 0) - { - byte * p; - byte data[2]; - word cmd_count; - word cmd; - signed char * commands; - word count; - - y_pos = 0; x_pos = 0; - Read_word_be(IFF_file,&cmd_count); - cmd_count -= 2; - commands = (signed char *)malloc(cmd_count); - if (!Read_bytes(IFF_file,commands,cmd_count)) - { - File_error = 31; - break; - } - section_size -= (cmd_count + 2); - for (cmd = 0; cmd < cmd_count && section_size > 0; cmd++) - { - if (commands[cmd] == 0) - { // move pointer - word offset; - short x_ofs, y_ofs; - Read_word_be(IFF_file,&offset); - section_size -= 2; - if((short)offset < 0) { - y_ofs = ((short)offset - plane_line_size + 1) / plane_line_size; - x_ofs = (short)offset - y_ofs * plane_line_size; - } else { - x_ofs = offset % plane_line_size; - y_ofs = offset / plane_line_size; - } - y_pos += y_ofs; - x_pos += x_ofs; - } - else if(commands[cmd] < 0) - { // XOR with a string of Words - if (commands[cmd] == -1 && section_size >= 2) - { - Read_word_be(IFF_file,&count); - section_size -= 2; - } - else - count = -commands[cmd] - 1; - while (count-- > 0 && y_pos < context->Height && section_size > 0) - { - p = buffer+x_pos+y_pos*line_size+vdlt_plane*plane_line_size; - Read_bytes(IFF_file,data,2); - section_size -= 2; - p[0] ^= data[0]; - p[1] ^= data[1]; - y_pos++; - } - } - else // commands[cmd] > 0 - { // XOR a word several times - if (commands[cmd] == 1) - { - Read_word_be(IFF_file,&count); - section_size -= 2; - if (section_size < 2) - break; - } - else - count = commands[cmd] - 1; - Read_bytes(IFF_file,data,2); - section_size -= 2; - do - { - p = buffer+x_pos+y_pos*line_size+vdlt_plane*plane_line_size; - p[0] ^= data[0]; - p[1] ^= data[1]; - y_pos++; - } - while (count-- > 0 && y_pos < context->Height); - } - } - free(commands); - if(cmd < (cmd_count-1) || section_size > 0) - { - Warning("Early end in ADAT chunk"); - } - } - - vdlt_plane++; - if (vdlt_plane == real_bit_planes) - { - for (y_pos=0; y_posHeight; y_pos++) - { - Draw_IFF_line(context, buffer+line_size*y_pos,y_pos,real_line_size,real_bit_planes); - } - } - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes - } - else if (memcmp(section, "CMAP", 4) == 0) // Colour MAP - { - nb_colors = section_size/3; - - if (nb_colors > 256) - { - File_error = 1; - break; - } - GFX2_Log(GFX2_DEBUG, "IFF CMAP : %u colors (header.BitPlanes=%u)\n", nb_colors, header.BitPlanes); - - if (current_frame != 0) - GFX2_Log(GFX2_WARNING, "IFF : One CMAP per frame is not supported (frame #%u/%u)\n", current_frame + 1, frame_count); - if ((header.BitPlanes==6 && nb_colors==16) || (header.BitPlanes==8 && nb_colors==64)) - { - Image_HAM=header.BitPlanes; - bpp = 3 * (header.BitPlanes - 2); // HAM6 = 12bpp, HAM8 = 18bpp - } - - if (Read_bytes(IFF_file,context->Palette,3*nb_colors)) - { - section_size -= 3*nb_colors; - if (nb_colors <= 64) - { - unsigned int i; - int is_4bits_colors = 1; // recoil also checks that header.Pad1 is not 0x80 - for(i = 0; i < nb_colors && is_4bits_colors; i++) - { - is_4bits_colors = is_4bits_colors && ((context->Palette[i].R & 0x0f) == 0) - && ((context->Palette[i].G & 0x0f) == 0) - && ((context->Palette[i].B & 0x0f) == 0); - } - if (is_4bits_colors) // extend 4bits per component color palettes to 8bits per component - for(i = 0; i < nb_colors; i++) - { - context->Palette[i].R |= (context->Palette[i].R >> 4); - context->Palette[i].G |= (context->Palette[i].G >> 4); - context->Palette[i].B |= (context->Palette[i].B >> 4); - } - } - if (((nb_colors==32) || (AmigaViewModes & 0x80)) && (header.BitPlanes==6)) - { - IFF_Set_EHB_Palette(context->Palette); // This is a Extra Half-Brite (EHB) 64 color image. - nb_colors = 64; - } - - while(section_size > 0) // Read padding bytes - { - if (Read_byte(IFF_file,&temp_byte)) - File_error=20; - section_size--; - } - } - else - File_error=1; - if (context->Type == CONTEXT_PALETTE || context->Type == CONTEXT_PREVIEW_PALETTE) - break; // stop once the palette is loaded - } - else if (memcmp(section,"CRNG",4) == 0) - { - // The content of a CRNG is as follows: - word padding; - word rate; - word flags; - byte min_col; - byte max_col; - if ( (Read_word_be(IFF_file,&padding)) - && (Read_word_be(IFF_file,&rate)) - && (Read_word_be(IFF_file,&flags)) - && (Read_byte(IFF_file,&min_col)) - && (Read_byte(IFF_file,&max_col))) - { - GFX2_Log(GFX2_DEBUG, "IFF CRNG : [#%u #%u] rate=%u flags=%04x\n", min_col, max_col, rate, flags); - if (section_size == 8 && min_col != max_col) - { - // Valid cycling range - if (max_colColor_cycles >= 16) - { - GFX2_Log(GFX2_INFO, "IFF CRNG : Maximum CRNG number is 16\n"); - } - else - { - word speed; - - context->Cycle_range[context->Color_cycles].Start=min_col; - context->Cycle_range[context->Color_cycles].End=max_col; - context->Cycle_range[context->Color_cycles].Inverse=(flags&2)?1:0; - speed = (flags&1) ? rate/78 : 0; - context->Cycle_range[context->Color_cycles].Speed = (speed > COLOR_CYCLING_SPEED_MAX) ? COLOR_CYCLING_SPEED_MAX : speed; - context->Color_cycles++; - } - } - } - else - File_error=47; - } - else if (memcmp(section, "DRNG", 4) == 0) // DPaint IV enhanced color cycle chunk - { - /// @todo DRNG IFF chunk is read, but complex color cycling are not implemented. - // see http://amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node0282.html - byte min_col; - byte max_col; - word rate; - word flags; - byte ntruecolor; - byte nregs; - Read_byte(IFF_file, &min_col); - Read_byte(IFF_file, &max_col); - Read_word_be(IFF_file, &rate); - Read_word_be(IFF_file, &flags); - Read_byte(IFF_file, &ntruecolor); - Read_byte(IFF_file, &nregs); - section_size -= 8; - GFX2_Log(GFX2_DEBUG, "IFF DRNG : [#%u #%u] rate=%u flags=%04x, %u tc cells, %u reg cells\n", min_col, max_col, rate, flags, ntruecolor, nregs); - while (ntruecolor-- > 0 && section_size > 0) - { - byte cell, r, g, b; - Read_byte(IFF_file, &cell); - Read_byte(IFF_file, &r); - Read_byte(IFF_file, &g); - Read_byte(IFF_file, &b); - section_size -= 4; - GFX2_Log(GFX2_DEBUG, " #%u #%02X%02X%02X\n", cell, r, g, b); - } - while (nregs-- > 0 && section_size > 0) - { - byte cell, index; - Read_byte(IFF_file, &cell); - Read_byte(IFF_file, &index); - section_size -= 2; - GFX2_Log(GFX2_DEBUG, " #%u index %u\n", cell, index); - } - if (section_size > 0) - { - GFX2_Log(GFX2_DEBUG, "IFF DRNG : skipping %u bytes\n", section_size); - fseek(IFF_file, section_size, SEEK_CUR); - } - } - else if (memcmp(section, "CCRT", 4) == 0) // Color Cycling Range and Timing - { - // see http://amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node01BA.html - word direction; - byte start, end; - dword seconds, microseconds; - word pad; - - Read_word_be(IFF_file, &direction); - Read_byte(IFF_file, &start); - Read_byte(IFF_file, &end); - Read_dword_be(IFF_file, &seconds); - Read_dword_be(IFF_file, µseconds); - Read_word_be(IFF_file, &pad); - section_size -= 14; - GFX2_Log(GFX2_DEBUG, "IFF CCRT : dir %04x [#%u #%u] delay %u.%06u\n", - direction, start, end, seconds, microseconds); - if (context->Color_cycles >= 16) - { - GFX2_Log(GFX2_INFO, "IFF CCRT : Maximum CRNG number is 16\n"); - } - else if (start != end) - { - // Speed resolution is 0.2856Hz - // Speed = (1000000/delay) / 0.2856 = 3501400 / delay - - context->Cycle_range[context->Color_cycles].Start = start; - context->Cycle_range[context->Color_cycles].End = end; - if (direction != 0) - { - dword speed; - context->Cycle_range[context->Color_cycles].Inverse = (~direction >> 15) & 1; - speed = 3501400 / (seconds * 1000000 + microseconds); - context->Cycle_range[context->Color_cycles].Speed = (speed > COLOR_CYCLING_SPEED_MAX) ? COLOR_CYCLING_SPEED_MAX : speed; - } - else - { - context->Cycle_range[context->Color_cycles].Speed = 0; - } - - context->Color_cycles++; - } - } - else if (memcmp(section, "DPI ", 4) == 0) - { - word dpi_x, dpi_y; - Read_word_be(IFF_file, &dpi_x); - Read_word_be(IFF_file, &dpi_y); - section_size -= 4; - GFX2_Log(GFX2_DEBUG, "IFF DPI %hu x %hu\n", dpi_x, dpi_y); - } - else if (memcmp(section, "CAMG", 4) == 0) // Amiga Viewport Modes - { - Read_dword_be(IFF_file, &AmigaViewModes); // HIRES=0x8000 LACE=0x4 HAM=0x800 HALFBRITE=0x80 - section_size -= 4; - GFX2_Log(GFX2_DEBUG, "CAMG = $%08x\n", AmigaViewModes); - if (AmigaViewModes & 0x800 && (header.BitPlanes == 6 || header.BitPlanes == 8)) - { - Image_HAM = header.BitPlanes; - bpp = 3 * (header.BitPlanes - 2); - } - if ((AmigaViewModes & 0x80) && (header.BitPlanes == 6)) // This is a Extra Half-Brite (EHB) 64 color image. - { - IFF_Set_EHB_Palette(context->Palette); // Set the palette in case CAMG is after CMAP - nb_colors = 64; - } - } - else if (memcmp(section, "DPPV", 4) == 0) // DPaint II ILBM perspective chunk - { - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it - } - else if (memcmp(section, "CLUT", 4) == 0) // lookup table - { - dword lut_type; // 0 = A Monochrome, contrast or intensity LUT - byte lut[256]; // 1 = RED, 2 = GREEN, 3 = BLUE, 4 = HUE, 5 = SATURATION - - Read_dword_be(IFF_file, &lut_type); - Read_dword_be(IFF_file, &dummy); - Read_bytes(IFF_file, lut, 256); - section_size -= (4+4+256); - if (section_size > 0) - { - Warning("Extra bytes at the end of CLUT chunk"); - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); - } - } - else if (memcmp(section, "DYCP", 4) == 0) // DYnamic Color Palette - { - // All files I've seen have 4 words (8bytes) : - // { 0, 1, 16, 0} 16 is probably the number of colors in each palette - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it - } - else if (memcmp(section, "SHAM", 4) == 0) // Sliced HAM - { - int i; - word version; - dword SHAM_palette_count; - T_IFF_PCHG_Palette * prev_pal = NULL; - T_IFF_PCHG_Palette * new_pal = NULL; - - Image_HAM = header.BitPlanes; - bpp = 3 * (header.BitPlanes - 2); - Read_word_be(IFF_file, &version); // always 0 - section_size -= 2; - SHAM_palette_count = section_size >> 5; // 32 bytes per palette (16 colors * 2 bytes) - // SHAM_palette_count should be the image height, or height/2 for "interlaced" images - for (y_pos = 0; y_pos < header.Height && section_size >= 32; y_pos += (SHAM_palette_count < header.Height ? 2 : 1)) - { - new_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); - if (new_pal == NULL) - { - Warning("Memory allocation error"); - File_error = 1; - break; - } - new_pal->Next = NULL; - new_pal->StartLine = y_pos; - for (i = 0; i < 16; i++) - { - Read_byte(IFF_file, &temp_byte); // 0R - new_pal->Palette[i].R = (temp_byte & 0x0f) * 0x11; // 4 bits to 8 bits - Read_byte(IFF_file, &temp_byte); // GB - new_pal->Palette[i].G = (temp_byte & 0xf0) | (temp_byte >> 4); - new_pal->Palette[i].B = (temp_byte & 0x0f) * 0x11; // 4 bits to 8 bits - section_size -= 2; - } - if (prev_pal != NULL) - prev_pal->Next = new_pal; - else - PCHG_palettes = new_pal; - prev_pal = new_pal; - } - if (section_size > 0) - { - Warning("Extra bytes at the end of SHAM chunk"); - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); - } - } - else if (memcmp(section, "BEAM", 4) == 0 || memcmp(section, "CTBL", 4) == 0) - { - // One palette per line is stored - if (section_size >= header.Height * nb_colors * 2) - { - T_Palette palette; - T_IFF_PCHG_Palette * prev_pal = NULL; - T_IFF_PCHG_Palette * new_pal = NULL; - - for (y_pos = 0; y_pos < header.Height; y_pos++) - { - unsigned int i; - for (i = 0; i < nb_colors; i++) - { - word value; - Read_word_be(IFF_file, &value); - section_size -= 2; - palette[i].R = ((value & 0x0f00) >> 8) * 0x11; - palette[i].G = ((value & 0x00f0) >> 4) * 0x11; - palette[i].B = (value & 0x000f) * 0x11; - } - if (y_pos == 0) - { - prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); - if (prev_pal == NULL) - { - Warning("Memory allocation error"); - File_error = 1; - break; - } - prev_pal->Next = NULL; - prev_pal->StartLine = 0; - memcpy(prev_pal->Palette, palette, nb_colors*sizeof(T_Components)); - PCHG_palettes = prev_pal; - } - else if (memcmp(palette, prev_pal->Palette, nb_colors*sizeof(T_Components)) != 0) - { - new_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); - if (new_pal == NULL) - { - Warning("Memory allocation error"); - File_error = 1; - break; - } - new_pal->Next = NULL; - new_pal->StartLine = y_pos; - memcpy(new_pal->Palette, palette, nb_colors*sizeof(T_Components)); - prev_pal->Next = new_pal; - prev_pal = new_pal; - } - } - if (PCHG_palettes != NULL) - bpp = 12; - } - else - Warning("inconsistent size of BEAM/CTLB chunk, ignoring"); - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); - } - else if (memcmp(section, "PCHG", 4) == 0) // Palette CHanGes - { - dword * lineBitMask; - int i; - T_IFF_PCHG_Palette * prev_pal = NULL; - T_IFF_PCHG_Palette * curr_pal = NULL; - void * PCHGData = NULL; - - // http://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.PCHG - // http://vigna.di.unimi.it/software.php - // Header - word Compression; // 0 = None, 1 = Huffman - word Flags; // 0x1 = SmallLineChanges, 0x2 = BigLineChanges, 0x4 = use alpha - short StartLine; // possibly negative - word LineCount; - word ChangedLines; - word MinReg; - word MaxReg; - word MaxChanges; - dword TotalChanges; - - if (!(Read_word_be(IFF_file, &Compression) - && Read_word_be(IFF_file, &Flags) - && Read_word_be(IFF_file, (word *)&StartLine) - && Read_word_be(IFF_file, &LineCount) - && Read_word_be(IFF_file, &ChangedLines) - && Read_word_be(IFF_file, &MinReg) - && Read_word_be(IFF_file, &MaxReg) - && Read_word_be(IFF_file, &MaxChanges) - && Read_dword_be(IFF_file, &TotalChanges))) - File_error = 1; - section_size -= 20; - if (Compression) - { - short * TreeCode; - const short * TreeP; //a3 - int remaining_bits = 0; //d1 - dword src_dword = 0; //d2 - byte * dst_data; //a1 - - dword CompInfoSize; - dword OriginalDataSize; //d0 - - Read_dword_be(IFF_file, &CompInfoSize); - Read_dword_be(IFF_file, &OriginalDataSize); - section_size -= 8; - // read HuffMan tree - TreeCode = malloc(CompInfoSize); - for (i = 0; i < (int)(CompInfoSize / 2); i++) - Read_word_be(IFF_file, (word *)(TreeCode + i)); - section_size -= CompInfoSize; - - PCHGData = malloc(OriginalDataSize); - dst_data = PCHGData; - // HuffMan depacking - TreeP = TreeCode+(CompInfoSize/2-1); // pointer to the last element - while (OriginalDataSize > 0) - { - if (--remaining_bits < 0) - { - Read_dword_be(IFF_file, &src_dword); - section_size -= 4; - remaining_bits = 31; - } - if (src_dword & (1 << remaining_bits)) - { - if (*TreeP < 0) - { - // OffsetPointer - TreeP += (*TreeP / 2); - continue; // pick another bit - } - } - else - { - // Case0 - --TreeP; - if ((*TreeP < 0) || !(*TreeP & 0x100)) - continue; // pick another bit - } - // StoreValue - *dst_data = (byte)(*TreeP & 0xff); - dst_data++; - TreeP = TreeCode+(CompInfoSize/2-1); // pointer to the last element - OriginalDataSize--; - } - free(TreeCode); - } - else - { - PCHGData = malloc(section_size); - Read_bytes(IFF_file, PCHGData, section_size); - section_size = 0; - } - if (PCHGData != NULL) - { - const byte * data; - // initialize first palette from CMAP - prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); - prev_pal->Next = NULL; - prev_pal->StartLine = 0; - memcpy(prev_pal->Palette, context->Palette, nb_colors*sizeof(T_Components)); - PCHG_palettes = prev_pal; - - lineBitMask = (dword *)PCHGData; -#if defined(SDL_BYTEORDER) && (SDL_BYTEORDER != SDL_BIG_ENDIAN) - for (i = 0 ; i < ((LineCount + 31) >> 5); i++) - lineBitMask[i] = SDL_Swap32(lineBitMask[i]); -#elif defined(BYTE_ORDER) && (BYTE_ORDER != BIG_ENDIAN) - for (i = 0 ; i < ((LineCount + 31) >> 5); i++) - lineBitMask[i] = be32toh(lineBitMask[i]); -#elif defined(WIN32) - // assume WIN32 is little endian - for (i = 0 ; i < ((LineCount + 31) >> 5); i++) - lineBitMask[i] = WIN32_BSWAP32(lineBitMask[i]); -#endif - data = (const byte *)PCHGData + ((LineCount + 31) >> 5) * 4; - for (y_pos = 0 ; y_pos < LineCount; y_pos++) - { - if (lineBitMask[y_pos >> 5] & (1 << (31-(y_pos & 31)))) - { - byte ChangeCount16, ChangeCount32; - word PaletteChange; - - if ((y_pos + StartLine) < 0) - curr_pal = PCHG_palettes; - else - { - curr_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); - curr_pal->Next = NULL; - curr_pal->StartLine = StartLine + y_pos; - memcpy(curr_pal->Palette, prev_pal->Palette, nb_colors*sizeof(T_Components)); - prev_pal->Next = curr_pal; - } - - ChangeCount16 = *data++; - ChangeCount32 = *data++; - for (i = 0; i < ChangeCount16; i++) - { - PaletteChange = data[0] << 8 | data[1]; // Big endian - data += 2; - curr_pal->Palette[(PaletteChange >> 12)].R = ((PaletteChange & 0x0f00) >> 8) * 0x11; - curr_pal->Palette[(PaletteChange >> 12)].G = ((PaletteChange & 0x00f0) >> 4) * 0x11; - curr_pal->Palette[(PaletteChange >> 12)].B = ((PaletteChange & 0x000f) >> 0) * 0x11; - } - for (i = 0; i < ChangeCount32; i++) - { - PaletteChange = data[0] << 8 | data[1]; // Big endian - data += 2; - curr_pal->Palette[16+(PaletteChange >> 12)].R = ((PaletteChange & 0x0f00) >> 8) * 0x11; - curr_pal->Palette[16+(PaletteChange >> 12)].G = ((PaletteChange & 0x00f0) >> 4) * 0x11; - curr_pal->Palette[16+(PaletteChange >> 12)].B = ((PaletteChange & 0x000f) >> 0) * 0x11; - } - if (nb_colors == 64) // Extend the 32 colors decoded to 64 - IFF_Set_EHB_Palette(curr_pal->Palette); - prev_pal = curr_pal; - } - } - free(PCHGData); - } - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); - if (PCHG_palettes != NULL) - bpp = 12; - } - else if (memcmp(section, "RAST", 4) == 0) // Atari ST - { - if (PCHG_palettes == NULL) - { - RAST_chunk_decode(context, IFF_file, section_size, &PCHG_palettes); - if (PCHG_palettes != NULL) - bpp = 12; - } - else - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip - } - else if (memcmp(section, "TINY", 4) == 0) - { - word tiny_width, tiny_height; - Read_word_be(IFF_file,&tiny_width); - Read_word_be(IFF_file,&tiny_height); - section_size -= 4; - - // Load thumbnail if in preview mode - if ((context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE) - && tiny_width > 0 && tiny_height > 0) - { - context->Original_width = header.Width; - context->Original_height = header.Height; - Pre_load(context, tiny_width, tiny_height,file_size,iff_format,ratio,bpp); - context->Background_transparent = header.Mask == 2; - context->Transparent_color = context->Background_transparent ? header.Transp_col : 0; - if (iff_format == FORMAT_PBM) - PBM_Decode(context, IFF_file, header.Compression, tiny_width, tiny_height); - else - LBM_Decode(context, IFF_file, header.Compression, Image_HAM, stored_bit_planes, real_bit_planes, PCHG_palettes); - fclose(IFF_file); - IFF_file = NULL; - return; - } - else - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); - } - else if (memcmp(section, "ANNO", 4) == 0) - { - dword length; - section_size = (section_size + 1) & ~1; - length = section_size; - if (length > COMMENT_SIZE) - length = COMMENT_SIZE; - Read_bytes(IFF_file,context->Comment,length); - context->Comment[length]='\0'; - section_size -= length; - fseek(IFF_file, section_size, SEEK_CUR); - } - else if (memcmp(section, "ABIT", 4) == 0) - { - // ACBM format : ABIT = Amiga BITplanes - // The ABIT chunk contains contiguous bitplane data. - // The chunk contains sequential data for bitplane 0 through bitplane n. - if (header.Width == 0 || header.Height == 0) - break; - Pre_load(context, header.Width, header.Height, file_size, iff_format, ratio, bpp); - // compute row size - real_line_size = (context->Width+15) & ~15; - plane_line_size = real_line_size >> 3; // 8bits per byte - line_size = plane_line_size * stored_bit_planes; - buffer = malloc(line_size * context->Height); - if ((dword)(line_size * context->Height) == section_size) - header.Compression = 0; // size is of uncompressed data. Forcing. - for (plane = 0; plane < stored_bit_planes; plane++) - { - for (y_pos = 0; y_pos < context->Height; y_pos++) - { - if (header.Compression == 0) - { - if (!Read_bytes(IFF_file,buffer+line_size*y_pos+plane_line_size*plane,plane_line_size)) - { - File_error = 21; - break; - } - } - else - { - Warning("Unhandled compression for ACBM ABIT chunk"); - File_error = 32; - break; - } - } - } - if (File_error == 0) - { - for (y_pos = 0; y_pos < context->Height; y_pos++) - { - if (Image_HAM <= 1) - Draw_IFF_line(context, buffer+y_pos*line_size, y_pos,real_line_size, real_bit_planes); - else - Draw_IFF_line_HAM(context, buffer+y_pos*line_size, y_pos,real_line_size, real_bit_planes, PCHG_palettes); - } - } - free(buffer); - buffer = NULL; - } - else if (memcmp(section, "BODY", 4) == 0) - { - long offset = ftell(IFF_file); - if (file_size > (unsigned long)(offset + section_size + 8)) - { - // Chunk RAST is placed AFTER the BODY, but we need the palette now to decode the image - // In addition, some files break the IFF standard by not aligning - // the chunk on word boundaries. - fseek(IFF_file, section_size, SEEK_CUR); - Read_bytes(IFF_file, section, 1); - if (section[0] == 'R') // we are good - Read_bytes(IFF_file, section + 1, 3); // read the remaining 3 bytes - else // skip 1 byte - Read_bytes(IFF_file, section, 4); // read 4 bytes - if (memcmp(section, "RAST", 4) == 0) - { - dword rast_size; - Read_dword_be(IFF_file, &rast_size); - RAST_chunk_decode(context, IFF_file, rast_size, &PCHG_palettes); - if (PCHG_palettes != NULL) - bpp = 12; - } - fseek(IFF_file, offset, SEEK_SET); // rewind - } - if (header.Width == 0 || header.Height == 0) - break; - Original_screen_X = header.X_screen; - Original_screen_Y = header.Y_screen; - - Pre_load(context, header.Width, header.Height, file_size, iff_format, ratio, bpp); - context->Background_transparent = header.Mask == 2; - context->Transparent_color = context->Background_transparent ? header.Transp_col : 0; - - Set_image_mode(context, IMAGE_MODE_ANIMATION); - - if (iff_format == FORMAT_LBM) // "ILBM": InterLeaved BitMap - { - LBM_Decode(context, IFF_file, header.Compression, Image_HAM, stored_bit_planes, real_bit_planes, PCHG_palettes); - } - else // "PBM ": Packed BitMap - { - PBM_Decode(context, IFF_file, header.Compression, context->Width, context->Height); - } - if (ftell(IFF_file) & 1) - fseek(IFF_file, 1, SEEK_CUR); // SKIP one byte - if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE) - { - break; // dont load animations in Preview mode - } - } - else - { - // skip section - GFX2_Log(GFX2_INFO, "IFF : Skip unknown section '%.4s' of %u bytes at offset %06X\n", section, section_size, ftell(IFF_file)); - if (section_size < 256) - { - byte buffer[256]; - - Read_bytes(IFF_file, buffer, (section_size+1)&~1); - GFX2_LogHexDump(GFX2_DEBUG, "", buffer, 0, (section_size+1)&~1); - } - else - fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); - } - } - } - - fclose(IFF_file); - IFF_file = NULL; - } - else - File_error=1; - if (previous_frame) - free(previous_frame); - if (anteprevious_frame) - free(anteprevious_frame); - while (PCHG_palettes != NULL) - { - T_IFF_PCHG_Palette * next = PCHG_palettes->Next; - free(PCHG_palettes); - PCHG_palettes = next; - } -} - - -// -- Sauver un fichier au format IFF --------------------------------------- - - byte IFF_color_list[129]; - word IFF_list_size; - byte IFF_repetition_mode; - - // ------------- Ecrire les couleurs que l'on vient de traiter ------------ - void Transfer_colors(FILE * file) - { - byte index; - - if (IFF_list_size>0) - { - if (IFF_list_size > 128) - { - GFX2_Log(GFX2_ERROR, "Transfer_colors() IFF_list_size=%d !\n", IFF_list_size); - } - if (IFF_repetition_mode) - { - Write_one_byte(file,257-IFF_list_size); - Write_one_byte(file,IFF_color_list[0]); - } - else - { - Write_one_byte(file,IFF_list_size-1); - for (index=0; index= 127) - { - // diff mode with 126 bytes then 2 identical bytes - IFF_list_size--; - Transfer_colors(file); - IFF_color_list[0]=color; - IFF_color_list[1]=color; - IFF_list_size=2; - IFF_repetition_mode=1; - } - else if ( (IFF_repetition_mode) || (second_last_color!=color) ) - { - // same mode is kept - if (IFF_list_size==128) - Transfer_colors(file); - IFF_color_list[IFF_list_size++]=color; - } - else - { - // diff mode and 3 identical bytes - IFF_list_size-=2; - Transfer_colors(file); - IFF_color_list[0]=color; - IFF_color_list[1]=color; - IFF_color_list[2]=color; - IFF_list_size=3; - IFF_repetition_mode=1; - } - } - else // the color is different from the previous one - { - if (!IFF_repetition_mode) // keep mode - { - if (IFF_list_size == 128) - Transfer_colors(file); - IFF_color_list[IFF_list_size++]=color; - } - else // change mode - { - Transfer_colors(file); - IFF_color_list[IFF_list_size]=color; - IFF_list_size++; - } - } - } - } - - -/// Save IFF file (LBM or PBM) -void Save_IFF(T_IO_Context * context) -{ - FILE * IFF_file; - T_IFF_Header header; - word x_pos; - word y_pos; - byte temp_byte; - int i; - int palette_entries; - byte bit_depth; - long body_offset = -1; - int is_ehb = 0; // Extra half-bright - - if (context->Format == FORMAT_LBM) - { - // Check how many bits are used by pixel colors - temp_byte = 0; - for (y_pos=0; y_posHeight; y_pos++) - for (x_pos=0; x_posWidth; x_pos++) - temp_byte |= Get_pixel(context, x_pos,y_pos); - bit_depth=0; - do - { - bit_depth++; - temp_byte>>=1; - } while (temp_byte); - if (bit_depth == 6) - { - for (i = 0; i < 32; i++) - { - if ( (context->Palette[i].R >> 5) != (context->Palette[i+32].R >> 4) - || (context->Palette[i].G >> 5) != (context->Palette[i+32].G >> 4) - || (context->Palette[i].B >> 5) != (context->Palette[i+32].B >> 4)) - break; - } - if (i == 32) is_ehb = 1; - } - GFX2_Log(GFX2_DEBUG, "Saving ILBM with bit_depth = %d %s\n", bit_depth, - is_ehb ? "(Extra Half-Bright)" : ""); - palette_entries = 1 << (bit_depth - is_ehb); - } - else // FORMAT_PBM - { - bit_depth = 8; - palette_entries = 256; - } - - File_error=0; - - // Ouverture du fichier - if ((IFF_file=Open_file_write(context))) - { - setvbuf(IFF_file, NULL, _IOFBF, 64*1024); - - Write_bytes(IFF_file,"FORM",4); - Write_dword_be(IFF_file,0); // On mettra la taille à jour à la fin - - if (context->Format == FORMAT_LBM) - Write_bytes(IFF_file,"ILBM",4); - else - Write_bytes(IFF_file,"PBM ",4); - - Write_bytes(IFF_file,"BMHD",4); - Write_dword_be(IFF_file,20); - - header.Width=context->Width; - header.Height=context->Height; - header.X_org=0; - header.Y_org=0; - header.BitPlanes=bit_depth; - header.Mask=context->Background_transparent ? 2 : 0; - header.Compression=1; - header.Pad1=0; - header.Transp_col=context->Background_transparent ? context->Transparent_color : 0; - header.X_aspect=10; // Amiga files are usually 10:11 - header.Y_aspect=10; - switch (context->Ratio) - { - case PIXEL_SIMPLE: - case PIXEL_DOUBLE: - case PIXEL_TRIPLE: - case PIXEL_QUAD: - default: - break; - case PIXEL_WIDE: - case PIXEL_WIDE2: - header.X_aspect *= 2; // 2:1 - break; - case PIXEL_TALL3: // 3:4 - header.X_aspect = (header.X_aspect * 15) / 10; // *1.5 -#if defined(__GNUC__) && (__GNUC__ >= 7) - __attribute__ ((fallthrough)); -#endif - case PIXEL_TALL: - case PIXEL_TALL2: - header.Y_aspect *= 2; // 1:2 - break; - } - header.X_screen = context->Width;// Screen_width?; - header.Y_screen = context->Height;// Screen_height?; - - Write_word_be(IFF_file,header.Width); - Write_word_be(IFF_file,header.Height); - Write_word_be(IFF_file,header.X_org); - Write_word_be(IFF_file,header.Y_org); - Write_bytes(IFF_file,&header.BitPlanes,1); - Write_bytes(IFF_file,&header.Mask,1); - Write_bytes(IFF_file,&header.Compression,1); - Write_bytes(IFF_file,&header.Pad1,1); - Write_word_be(IFF_file,header.Transp_col); - Write_bytes(IFF_file,&header.X_aspect,1); - Write_bytes(IFF_file,&header.Y_aspect,1); - Write_word_be(IFF_file,header.X_screen); - Write_word_be(IFF_file,header.Y_screen); - - if (context->Format == FORMAT_LBM) - { - dword ViewMode = 0; // HIRES=0x8000 LACE=0x4 HAM=0x800 HALFBRITE=0x80 - if (is_ehb) ViewMode |= 0x80; - if (context->Width > 400) - { - ViewMode |= 0x8000; - if (context->Width > 800) - ViewMode |= 0x20; // Super High-Res - } - if (context->Height > 300) ViewMode |= 0x4; - Write_bytes(IFF_file, "CAMG", 4); - Write_dword_be(IFF_file, 4); // Section size - Write_dword_be(IFF_file, ViewMode); - } - - Write_bytes(IFF_file,"CMAP",4); - Write_dword_be(IFF_file,palette_entries*3); - - Write_bytes(IFF_file,context->Palette,palette_entries*3); - - if (context->Comment[0]) // write ANNO - { - dword comment_size; - - Write_bytes(IFF_file,"ANNO",4); // Chunk name - comment_size = strlen(context->Comment); // NULL termination is not needed - Write_dword_be(IFF_file, comment_size); // Section size - Write_bytes(IFF_file, context->Comment, comment_size); - if (comment_size&1) - Write_byte(IFF_file, 0); // align to WORD boundaries - } - - for (i=0; iColor_cycles; i++) - { - word flags=0; - flags|= context->Cycle_range[i].Speed?1:0; // Cycling or not - flags|= context->Cycle_range[i].Inverse?2:0; // Inverted - - Write_bytes(IFF_file,"CRNG",4); - Write_dword_be(IFF_file,8); // Section size - Write_word_be(IFF_file,0); // Padding - Write_word_be(IFF_file,context->Cycle_range[i].Speed*78); // Rate - Write_word_be(IFF_file,flags); // Flags - Write_byte(IFF_file,context->Cycle_range[i].Start); // Min color - Write_byte(IFF_file,context->Cycle_range[i].End); // Max color - // No padding, size is multiple of 2 - } - - body_offset = ftell(IFF_file); - Write_bytes(IFF_file,"BODY",4); - Write_dword_be(IFF_file,0); // On mettra la taille à jour à la fin - - if (context->Format == FORMAT_LBM) - { - byte * buffer; - short line_size; // Size of line in bytes - short plane_line_size; // Size of line in bytes for 1 plane - short real_line_size; // Size of line in pixels - - // Calcul de la taille d'une ligne ILBM (pour les images ayant des dimensions exotiques) - real_line_size = (context->Width+15) & ~15; - plane_line_size = real_line_size >> 3; // 8bits per byte - line_size = plane_line_size * header.BitPlanes; - buffer=(byte *)malloc(line_size); - - // Start encoding - IFF_list_size=0; - for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) - { - // Dispatch the pixel into planes - memset(buffer,0,line_size); - for (x_pos=0; x_posWidth; x_pos++) - Set_IFF_color(buffer, x_pos, Get_pixel(context, x_pos,y_pos), real_line_size, header.BitPlanes); - - if (context->Width&1) // odd width fix - Set_IFF_color(buffer, x_pos, 0, real_line_size, header.BitPlanes); - - // encode the resulting sequence of bytes - if (header.Compression) - { - int plane_width=line_size/header.BitPlanes; - int plane; - - for (plane=0; planeHeight) && (!File_error)); y_pos++) - { - for (x_pos=0; ((x_posWidth) && (!File_error)); x_pos++) - New_color(IFF_file, Get_pixel(context, x_pos,y_pos)); - - if (context->Width&1) // odd width fix - New_color(IFF_file, 0); - - if (!File_error) - Transfer_colors(IFF_file); - } - } - // Now update FORM and BODY size - if (!File_error) - { - long file_size = ftell(IFF_file); - if (file_size&1) - { - // PAD to even file size - if (! Write_byte(IFF_file,0)) - File_error=1; - } - // Write BODY size - fseek(IFF_file, body_offset + 4, SEEK_SET); - Write_dword_be(IFF_file, file_size-body_offset-8); - // Write FORM size - file_size = (file_size+1)&~1; - fseek(IFF_file,4,SEEK_SET); - Write_dword_be(IFF_file,file_size-8); - } - fclose(IFF_file); - if (File_error != 0) // remove the file if there have been an error - Remove_file(context); - } - else - File_error=1; -} - -/** @} */ - - /////////////////////////// .info (Amiga Icons) ///////////////////////////// typedef struct { // offset diff --git a/src/ifformat.c b/src/ifformat.c new file mode 100644 index 00000000..2d502071 --- /dev/null +++ b/src/ifformat.c @@ -0,0 +1,2355 @@ +/* vim:expandtab:ts=2 sw=2: +*/ +/* Grafx2 - The Ultimate 256-color bitmap paint program + + Copyright 2018 Thomas Bernard + Copyright 2011 Pawel Góralski + Copyright 2009 Petter Lindquist + Copyright 2008 Yves Rizoud + Copyright 2008 Franck Charlet + Copyright 2007 Adrien Destugues + Copyright 1996-2001 Sunset Design (Guillaume Dorme & Karl Maritaud) + + Grafx2 is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; version 2 + of the License. + + Grafx2 is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Grafx2; if not, see +*/ + +///@file ifformat.c +/// Saving and loading IFF picture formats. + +#include +#include +#include "fileformats.h" +#include "loadsavefuncs.h" +#include "io.h" +#include "misc.h" +#include "gfx2mem.h" +#include "gfx2log.h" + +//////////////////////////////////// IFF //////////////////////////////////// +/** @defgroup IFF Interchange File Format + * @ingroup loadsaveformats + * Interchange File Format (Electronic Arts) + * + * This is the "native" format of Amiga. + * We support ILBM/PBM/ACBM + * @{ + */ +typedef struct +{ + word Width; + word Height; + word X_org; // Inutile + word Y_org; // Inutile + byte BitPlanes; + byte Mask; // 0=none, 1=mask, 2=transp color, 3=Lasso + byte Compression; // 0=none, 1=packbits, 2=vertical RLE + byte Pad1; // Inutile + word Transp_col; // transparent color for masking mode 2 + byte X_aspect; // Inutile + byte Y_aspect; // Inutile + word X_screen; + word Y_screen; +} T_IFF_Header; + +typedef struct +{ + byte operation; // 0=normal body, 1=XOR, 2=Long Delta, 3=Short Delta, + // 4=Generalized Long/Short Delta, 5=Byte Vertical Delta, + // 7=short/long vertical delta, 74=Eric Graham's compression + byte mask; // for XOR mode only + word w,h; // for XOR mode only + word x,y; // for XOR mode only + dword abstime; + dword reltime; + byte interleave; + byte pad0; + dword bits; +} T_IFF_AnimHeader; + +/// Test if a file is in IFF format +void Test_IFF(FILE * IFF_file, const char *sub_type) +{ + char format[4]; + char section[4]; + dword dummy; + + File_error=1; + + if (! Read_bytes(IFF_file,section,4)) + return; + if (memcmp(section,"FORM",4)) + return; + + if (! Read_dword_be(IFF_file, &dummy)) + return; + // On aurait pu vérifier que ce long est égal à la taille + // du fichier - 8, mais ça aurait interdit de charger des + // fichiers tronqués (et déjà que c'est chiant de perdre + // une partie du fichier il faut quand même pouvoir en + // garder un peu... Sinon, moi je pleure :'( !!! ) + if (! Read_bytes(IFF_file,format,4)) + return; + + if (!memcmp(format,"ANIM",4)) + { + dword size; + + // An ANIM header: need to check that it encloses an image + do + { + if (! Read_bytes(IFF_file,section,4)) + return; + if (memcmp(section,"FORM",4)) + return; + if (! Read_dword_be(IFF_file, &size)) + return; + if (! Read_bytes(IFF_file,format,4)) + return; + if (fseek(IFF_file, size - 4, SEEK_CUR) < 0) + return; + } + while(0 == memcmp(format, "8SVX", 4)); // skip sound frames + } + else if(memcmp(format,"DPST",4) == 0) + { + if (! Read_bytes(IFF_file,section,4)) + return; + if (memcmp(section, "DPAH", 4) != 0) + return; + if (! Read_dword_be(IFF_file, &dummy)) + return; + fseek(IFF_file, dummy, SEEK_CUR); + if (! Read_bytes(IFF_file,section,4)) + return; + if (memcmp(section,"FORM",4)) + return; + if (! Read_dword_be(IFF_file, &dummy)) + return; + if (! Read_bytes(IFF_file,format,4)) + return; + } + if ( memcmp(format,sub_type,4)) + return; + + // If we reach this part, file is indeed ILBM/PBM or ANIM + File_error=0; +} + +void Test_PBM(T_IO_Context * context, FILE * f) +{ + (void)context; + Test_IFF(f, "PBM "); +} +void Test_LBM(T_IO_Context * context, FILE * f) +{ + (void)context; + Test_IFF(f, "ILBM"); +} +void Test_ACBM(T_IO_Context * context, FILE * f) +{ + (void)context; + Test_IFF(f, "ACBM"); +} + + +// -- Lire un fichier au format IFF ----------------------------------------- + +typedef struct T_IFF_PCHG_Palette { + struct T_IFF_PCHG_Palette * Next; + short StartLine; + T_Components Palette[1]; +} T_IFF_PCHG_Palette; + +/// Skips the current section in an IFF file. +/// This function should be called while the file pointer is right +/// after the 4-character code that identifies the section. +static int IFF_Skip_section(FILE * file) +{ + dword size; + + if (!Read_dword_be(file,&size)) + return 0; + if (size&1) + size++; + if (fseek(file,size,SEEK_CUR)) + return 0; + return 1; +} + +/// Wait for a specific IFF chunk +static byte IFF_Wait_for(FILE * file, const char * expected_section) +{ + // Valeur retournée: 1=Section trouvée, 0=Section non trouvée (erreur) + byte section_read[4]; + + if (! Read_bytes(file,section_read,4)) + return 0; + while (memcmp(section_read,expected_section,4)) // Sect. pas encore trouvée + { + if (!IFF_Skip_section(file)) + return 0; + if (! Read_bytes(file,section_read,4)) + return 0; + } + return 1; +} + +// Les images ILBM sont stockés en bitplanes donc on doit trifouiller les bits pour +// en faire du chunky + +/// +/// Decodes the color of one pixel from the ILBM line buffer. +/// Planar to chunky conversion +/// @param buffer Planar buffer +/// @param x_pos Position of the pixel in graphic line +/// @param real_line_size Width of one bitplane in memory, in bytes +/// @param bitplanes Number of bitplanes +static dword Get_IFF_color(const byte * buffer, word x_pos, word real_line_size, byte bitplanes) +{ + byte shift = 7 - (x_pos & 7); + int address,masked_bit,plane; + dword color=0; + + for(plane=bitplanes-1;plane>=0;plane--) + { + address = (real_line_size * plane + x_pos) >> 3; + masked_bit = (buffer[address] >> shift) & 1; + + color = (color << 1) + masked_bit; + } + + return color; +} + +/// chunky to planar +static void Set_IFF_color(byte * buffer, word x_pos, byte color, word real_line_size, byte bitplanes) +{ + byte shift = 7 - (x_pos & 7); + int address, plane; + + for(plane=0;plane> 3; + buffer[address] |= (color&1) << shift; + color = color >> 1; + } +} + +// ----------------------- Afficher une ligne ILBM ------------------------ +/// Planar to chunky conversion of a line +/// @param context the IO context +/// @param buffer Planar buffer +/// @param y_pos Current line +/// @param real_line_size Width of one bitplane in memory, in bytes +/// @param bitplanes Number of bitplanes +void Draw_IFF_line(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes) +{ + short x_pos; + + if (bitplanes > 8) + { + for (x_pos=0; x_posWidth; x_pos++) + { + // Default standard deep ILBM bit ordering: + // saved first -----------------------------------------------> saved last + // R0 R1 R2 R3 R4 R5 R6 R7 G0 G1 G2 G3 G4 G5 G6 G7 B0 B1 B2 B3 B4 B5 B6 B7 + dword rgb = Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); + Set_pixel_24b(context, x_pos,y_pos, rgb, rgb >> 8, rgb >> 16); // R is 8 LSB, etc. + } + } + else for (x_pos=0; x_posWidth; x_pos++) + { + Set_pixel(context, x_pos, y_pos, Get_IFF_color(buffer, x_pos,real_line_size, bitplanes)); + } +} + +/// decode pixels with palette changes per line (copper list:) +static void Draw_IFF_line_PCHG(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes, const T_IFF_PCHG_Palette * PCHG_palettes) +{ + const T_IFF_PCHG_Palette * palette; + short x_pos; + + palette = PCHG_palettes; // find the palette to use for the line + if (palette == NULL) + return; + while (palette->Next != NULL && palette->Next->StartLine <= y_pos) + palette = palette->Next; + + for (x_pos=0; x_posWidth; x_pos++) + { + dword c = Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); + Set_pixel_24b(context, x_pos,y_pos, palette->Palette[c].R, palette->Palette[c].G, palette->Palette[c].B); + } +} + +/// Decode a HAM line to 24bits pixels +static void Draw_IFF_line_HAM(T_IO_Context *context, const byte * buffer, short y_pos, short real_line_size, byte bitplanes, const T_IFF_PCHG_Palette * PCHG_palettes) +{ + short x_pos; + byte red, green, blue, temp; + const T_Components * palette; + + if (PCHG_palettes == NULL) + palette = context->Palette; + else + { + // find the palette to use for the line + while (PCHG_palettes->Next != NULL && PCHG_palettes->Next->StartLine <= y_pos) + PCHG_palettes = PCHG_palettes->Next; + palette = PCHG_palettes->Palette; + } + red = palette[0].R; + green = palette[0].G; + blue = palette[0].B; + if (bitplanes == 6) + { + for (x_pos=0; x_posWidth; x_pos++) // HAM6 + { + temp=Get_IFF_color(buffer, x_pos,real_line_size, bitplanes); + switch (temp & 0x30) + { + case 0x10: // blue + blue=(temp&0x0F)*0x11; + break; + case 0x20: // red + red=(temp&0x0F)*0x11; + break; + case 0x30: // green + green=(temp&0x0F)*0x11; + break; + default: // Nouvelle couleur + red=palette[temp].R; + green =palette[temp].G; + blue =palette[temp].B; + } + Set_pixel_24b(context, x_pos,y_pos,red,green,blue); + } + } + else + { + for (x_pos=0; x_posWidth; x_pos++) // HAM8 + { + temp=Get_IFF_color(buffer,x_pos,real_line_size, bitplanes); + switch (temp >> 6) + { + case 0x01: // blue + blue= (temp << 2) | ((temp & 0x30) >> 4); + break; + case 0x02: // red + red= (temp << 2) | ((temp & 0x30) >> 4); + break; + case 0x03: // green + green= (temp << 2) | ((temp & 0x30) >> 4); + break; + default: // Nouvelle couleur + red=palette[temp].R; + green =palette[temp].G; + blue =palette[temp].B; + } + Set_pixel_24b(context, x_pos,y_pos,red,green,blue); + } + } +} + +/// Decode PBM data +/// +/// Supports compressions : +/// - 0 uncompressed +/// - 1 compressed +static void PBM_Decode(T_IO_Context * context, FILE * file, byte compression, word width, word height) +{ + byte * line_buffer; + word x_pos, y_pos; + word real_line_size = (width+1)&~1; + + switch (compression) + { + case 0: // uncompressed + line_buffer=(byte *)malloc(real_line_size); + for (y_pos=0; ((y_pos127) + { + if(Read_byte(file, &color)!=1) + { + File_error=28; + break; + } + do { + Set_pixel(context, x_pos++,y_pos,color); + } + while(temp_byte++ != 0); + } + else + do + { + if(Read_byte(file, &color)!=1) + { + File_error=29; + break; + } + Set_pixel(context, x_pos++,y_pos,color); + } + while(temp_byte-- > 0); + } + } + break; + default: + GFX2_Log(GFX2_ERROR, "PBM only supports compression type 0 and 1 (not %d)\n", compression); + File_error = 50; + } +} + +/// Decode LBM data +/// +/// Supports packings : +/// - 0 uncompressed +/// - 1 packbits (Amiga) +/// - 2 Vertical RLE (Atari ST) +static void LBM_Decode(T_IO_Context * context, FILE * file, byte compression, byte Image_HAM, + byte stored_bit_planes, byte real_bit_planes, const T_IFF_PCHG_Palette * PCHG_palettes) +{ + int plane; + byte * buffer; + short x_pos, y_pos; + // compute row size + int real_line_size = (context->Width+15) & ~15; // size in bit for one bit plane + int plane_line_size = real_line_size >> 3; // size in byte for one bit plane + int line_size = plane_line_size * stored_bit_planes; // size in byte for all bitplanes + + switch(compression) + { + case 0: // uncompressed + buffer = (byte *)GFX2_malloc(line_size); + if (buffer == NULL) + { + File_error=1; + return; + } + for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) + { + if (Read_bytes(file,buffer,line_size)) + { + if (Image_HAM > 1) + Draw_IFF_line_HAM(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); + else if (PCHG_palettes) + Draw_IFF_line_PCHG(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); + else + Draw_IFF_line(context, buffer, y_pos,real_line_size, real_bit_planes); + } + else + File_error=21; + } + free(buffer); + break; + case 1: // packbits compression (Amiga) + buffer = (byte *)GFX2_malloc(line_size); + if (buffer == NULL) + { + File_error=1; + return; + } + for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) + { + for (x_pos=0; ((x_pos 127 => repeat (257-temp_byte) the next byte + // temp_byte <= 127 => copy (temp_byte + 1) bytes + if(temp_byte == 128) // 128 = NOP ! + { + GFX2_Log(GFX2_WARNING, "NOP in packbits stream\n"); + } + else if (temp_byte>127) + { + if(Read_byte(file, &color)!=1) + { + File_error=23; + break; + } + do + { + if (x_pos=line_size || !Read_byte(file, &(buffer[x_pos++]))) + { + File_error=25; + break; + } + } + while (temp_byte-- > 0); + } + if (!File_error) + { + if (Image_HAM > 1) + Draw_IFF_line_HAM(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); + else if (PCHG_palettes) + Draw_IFF_line_PCHG(context, buffer, y_pos,real_line_size, real_bit_planes, PCHG_palettes); + else + Draw_IFF_line(context, buffer, y_pos,real_line_size,real_bit_planes); + } + } + free(buffer); + break; + case 2: // vertical RLE compression (Atari ST) + buffer = (byte *)GFX2_malloc(line_size*context->Height); + if (buffer == NULL) + { + File_error=1; + return; + } + memset(buffer, 0, line_size*context->Height); + for (plane = 0; plane < stored_bit_planes && !File_error; plane++) + { + dword section_size; + word cmd_count; + word cmd; + signed char * commands; + word count; + + y_pos = 0; x_pos = 0; + if (!IFF_Wait_for(file, "VDAT")) + { + if (plane == 0) // don't cancel loading if at least 1 bitplane has been loaded + File_error = 30; + break; + } + Read_dword_be(file,§ion_size); + Read_word_be(file,&cmd_count); + cmd_count -= 2; + commands = (signed char *)malloc(cmd_count); + if (!Read_bytes(file,commands,cmd_count)) + { + File_error = 31; + break; + } + section_size -= (cmd_count + 2); + for (cmd = 0; cmd < cmd_count && x_pos < plane_line_size && section_size > 0; cmd++) + { + if (commands[cmd] <= 0) + { // cmd=0 : load count from data, COPY + // cmd < 0 : count = -cmd, COPY + if (commands[cmd] == 0) + { + Read_word_be(file,&count); + section_size -= 2; + } + else + count = -commands[cmd]; + while (count-- > 0 && x_pos < plane_line_size && section_size > 0) + { + Read_bytes(file,buffer+x_pos+y_pos*line_size+plane*plane_line_size,2); + section_size -= 2; + if(++y_pos >= context->Height) + { + y_pos = 0; + x_pos += 2; + } + } + } + else if (commands[cmd] >= 1) + { // cmd=1 : load count from data, RLE + // cmd >1 : count = cmd, RLE + byte data[2]; + if (commands[cmd] == 1) + { + Read_word_be(file,&count); + section_size -= 2; + } + else + count = (word)commands[cmd]; + if (section_size == 0) + break; + Read_bytes(file,data,2); + section_size -= 2; + while (count-- > 0 && x_pos < plane_line_size) + { + memcpy(buffer+x_pos+y_pos*line_size+plane*plane_line_size,data,2); + if(++y_pos >= context->Height) + { + y_pos = 0; + x_pos += 2; + } + } + } + } + if(cmd < (cmd_count-1) || section_size > 0) + GFX2_Log(GFX2_WARNING, "Early end in VDAT chunk\n"); + if (section_size > 0) + fseek(file, (section_size+1)&~1, SEEK_CUR); // skip bytes + free(commands); + } + if (!File_error) + { + for (y_pos = 0; y_pos < context->Height; y_pos++) + { + Draw_IFF_line(context,buffer+line_size*y_pos,y_pos,real_line_size,real_bit_planes); + } + } + free(buffer); + break; + default: + GFX2_Log(GFX2_WARNING, "Unknown IFF compression %d\n", compression); + File_error = 32; + } +} + +/// Decode RAST chunk (from Atari ST pictures) +static void RAST_chunk_decode(T_IO_Context * context, FILE * file, dword section_size, T_IFF_PCHG_Palette ** PCHG_palettes) +{ + int i; + T_Components palette[16]; + T_IFF_PCHG_Palette * prev_pal = NULL; + T_IFF_PCHG_Palette * new_pal = NULL; + + // 17 words per palette : 1 for line, 16 for the colors + while (section_size >= 34) + { + word line, value; + + Read_word_be(file, &line); + for (i = 0; i < 16; i++) + { + Read_word_be(file, &value); // Decode STE Palette + palette[i].R = ((value & 0x0700) >> 7 | (value & 0x0800) >> 11) * 0x11; + palette[i].G = ((value & 0x0070) >> 3 | (value & 0x0080) >> 7) * 0x11; + palette[i].B = ((value & 0x0007) << 1 | (value & 0x0008) >> 3) * 0x11; + } + section_size -= 34; + + if (prev_pal == NULL || (line > prev_pal->StartLine && (0 != memcmp(palette, prev_pal->Palette, sizeof(T_Components)*3)))) + { + new_pal = malloc(sizeof(T_IFF_PCHG_Palette) + sizeof(T_Components) * 16); + if (new_pal == NULL) + { + File_error = 2; + return; + } + memcpy(new_pal->Palette, palette, sizeof(T_Components) * 16); + new_pal->StartLine = line; + new_pal->Next = NULL; + if (prev_pal != NULL) + { + prev_pal->Next = new_pal; + prev_pal = new_pal; + } + else if (line == 0) + { + prev_pal = new_pal; + *PCHG_palettes = prev_pal; + } + else // line > 0 && prev_pal == NULL + { + // create a palette for line 0 + prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + sizeof(T_Components) * 16); + if (prev_pal == NULL) + { + File_error = 2; + return; + } + memcpy(prev_pal->Palette, context->Palette, sizeof(T_Components) * 16); + prev_pal->StartLine = 0; + prev_pal->Next = new_pal; + *PCHG_palettes = prev_pal; + prev_pal = new_pal; + } + } + } +} + +/// Sets 32 upper colors of EHB palette +static void IFF_Set_EHB_Palette(T_Components * palette) +{ + int i, j; /// 32 colors in the palette. + for (i=0; i<32; i++) /// The next 32 colors are the same with values divided by 2 + { + j=i+32; + palette[j].R = palette[i].R>>1; + palette[j].G = palette[i].G>>1; + palette[j].B = palette[i].B>>1; + } +} + +/// Load IFF picture (PBM/ILBM/ACBM) or animation +void Load_IFF(T_IO_Context * context) +{ + FILE * IFF_file; + T_IFF_Header header; + T_IFF_AnimHeader aheader; + char format[4]; + char section[4]; + byte temp_byte; + dword nb_colors = 0; // number of colors in the CMAP (color map) + dword section_size; + short x_pos; + short y_pos; + short line_size = 0; // Taille d'une ligne en octets + short plane_line_size = 0; // Size of line in bytes for 1 plane + short real_line_size = 0; // Taille d'une ligne en pixels + unsigned long file_size; + dword dummy; + int iff_format = 0; + int plane; + dword AmigaViewModes = 0; + enum PIXEL_RATIO ratio = PIXEL_SIMPLE; + byte * buffer; + byte bpp = 0; + byte Image_HAM = 0; + T_IFF_PCHG_Palette * PCHG_palettes = NULL; + int current_frame = 0; + byte * previous_frame = NULL; // For animations + byte * anteprevious_frame = NULL; + word frame_count = 0; + word frame_duration = 0; + word vdlt_plane = 0; // current plane during Atari ST animation decoding + + memset(&aheader, 0, sizeof(aheader)); + + File_error=0; + + if ((IFF_file=Open_file_read(context))) + { + file_size=File_length_file(IFF_file); + + // FORM + size(4) + Read_bytes(IFF_file,section,4); + Read_dword_be(IFF_file,&dummy); + + Read_bytes(IFF_file,format,4); + if (!memcmp(format,"ANIM",4)) + { + // Skip a bit, brother + Read_bytes(IFF_file,section,4); + Read_dword_be(IFF_file,§ion_size); + Read_bytes(IFF_file,format,4); + while (0 == memcmp(format, "8SVX", 4)) + { + GFX2_Log(GFX2_DEBUG, "IFF : skip sound %.4s %u bytes\n", format, section_size); + if (fseek(IFF_file, section_size - 4, SEEK_CUR) < 0) + break; + Read_bytes(IFF_file,section,4); + Read_dword_be(IFF_file,§ion_size); + Read_bytes(IFF_file,format,4); + } + } + else if(memcmp(format,"DPST",4)==0) + { + // read DPAH + while (File_error == 0) + { + if (!(Read_bytes(IFF_file,section,4) + && Read_dword_be(IFF_file,§ion_size))) + File_error = 1; + if (memcmp(section, "FORM", 4) == 0) + { + Read_bytes(IFF_file,format,4); + break; + } + else if (memcmp(section, "DPAH", 4) == 0) // Deluxe Paint Animation Header + { + word version; + Read_word_be(IFF_file, &frame_duration); + Read_word_be(IFF_file, &frame_count); + Read_word_be(IFF_file, &version); + section_size -= 6; + Set_frame_duration(context, (frame_duration * 50) / 3); // convert 1/60th sec to msec + } + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // skip unread bytes + } + } + if (memcmp(format,"ILBM",4) == 0) + iff_format = FORMAT_LBM; + else if(memcmp(format,"PBM ",4) == 0) + iff_format = FORMAT_PBM; + else if(memcmp(format,"ACBM",4) == 0) + iff_format = FORMAT_ACBM; + else + { + GFX2_Log(GFX2_WARNING, "Unknown IFF format '%.4s'\n", format); + File_error=1; + } + + + { + byte real_bit_planes = 0; + byte stored_bit_planes = 0; + while (File_error == 0 + && Read_bytes(IFF_file,section,4) + && Read_dword_be(IFF_file,§ion_size)) + { + if (memcmp(section, "FORM", 4) == 0) + { + // special + Read_bytes(IFF_file, format, 4); + if (memcmp(format, "VDLT", 4) == 0) // Vertical DeLTa + { // found in animation produced by DeluxePaint for Atari ST + if (frame_count != 0 && current_frame >= (frame_count - 1)) + break; // file contains the Delta from the last frame to the 1st one to allow looping, skip it + + real_line_size = (context->Width+15) & ~15; + plane_line_size = real_line_size >> 3; // 8bits per byte + line_size = plane_line_size * real_bit_planes; + + if (previous_frame == NULL) + { + previous_frame = calloc(line_size * context->Height,1); + for (y_pos=0; y_posHeight; y_pos++) + { + const byte * pix_p = context->Target_address + y_pos * context->Pitch; + // Dispatch the pixel into planes + for (x_pos=0; x_posWidth; x_pos++) + { + Set_IFF_color(previous_frame+y_pos*line_size, x_pos, *pix_p++, real_line_size, real_bit_planes); + } + } + } + + Set_loading_layer(context, ++current_frame); + Set_frame_duration(context, (frame_duration * 50) / 3); // convert 1/60th sec to msec + vdlt_plane = 0; + } + continue; + } + if (memcmp(section, "BMHD", 4) == 0) // BitMap HeaDer + { + if (!((Read_word_be(IFF_file,&header.Width)) + && (Read_word_be(IFF_file,&header.Height)) + && (Read_word_be(IFF_file,&header.X_org)) + && (Read_word_be(IFF_file,&header.Y_org)) + && (Read_byte(IFF_file,&header.BitPlanes)) + && (Read_byte(IFF_file,&header.Mask)) + && (Read_byte(IFF_file,&header.Compression)) + && (Read_byte(IFF_file,&header.Pad1)) + && (Read_word_be(IFF_file,&header.Transp_col)) + && (Read_byte(IFF_file,&header.X_aspect)) + && (Read_byte(IFF_file,&header.Y_aspect)) + && (Read_word_be(IFF_file,&header.X_screen)) + && (Read_word_be(IFF_file,&header.Y_screen))) ) + { + File_error = 1; + break; + } + GFX2_Log(GFX2_DEBUG, "IFF BMHD : origin (%u,%u) size %ux%u BitPlanes=%u Mask=%u Compression=%u\n", + header.X_org, header.Y_org, header.Width, header.Height, + header.BitPlanes, header.Mask, header.Compression); + GFX2_Log(GFX2_DEBUG, " Transp_col=#%u aspect ratio %u/%u screen size %ux%u\n", + header.Transp_col, header.X_aspect, header.Y_aspect, + header.X_screen, header.Y_screen); + real_bit_planes = header.BitPlanes; + stored_bit_planes = header.BitPlanes; + if (header.Mask==1) + stored_bit_planes++; + Image_HAM=0; + if (header.X_aspect != 0 && header.Y_aspect != 0) + { + if ((10 * header.X_aspect) <= (6 * header.Y_aspect)) + ratio = PIXEL_TALL; // ratio <= 0.6 + else if ((10 * header.X_aspect) <= (8 * header.Y_aspect)) + ratio = PIXEL_TALL3; // 0.6 < ratio <= 0.8 + else if ((10 * header.X_aspect) < (15 * header.Y_aspect)) + ratio = PIXEL_SIMPLE; // 0.8 < ratio < 1.5 + else + ratio = PIXEL_WIDE; // 1.5 <= ratio + } + bpp = header.BitPlanes; + if (bpp <= 8 && bpp > 0) + { + unsigned int i; + // Set a default grayscale palette : if the + // ILBM/PBM file has no palette, it should be assumed to be a + // Gray scale picture. + if (Config.Clear_palette) + memset(context->Palette,0,sizeof(T_Palette)); + nb_colors = 1 << bpp; + for (i = 0; i < nb_colors; i++) + context->Palette[i].R = + context->Palette[i].G = + context->Palette[i].B = (i * 255) / (nb_colors - 1); + } + } + else if (memcmp(section, "ANHD", 4) == 0) // ANimation HeaDer + { + // http://www.textfiles.com/programming/FORMATS/anim7.txt + Read_byte(IFF_file, &aheader.operation); + Read_byte(IFF_file, &aheader.mask); + Read_word_be(IFF_file, &aheader.w); + Read_word_be(IFF_file, &aheader.h); + Read_word_be(IFF_file, &aheader.x); + Read_word_be(IFF_file, &aheader.y); + Read_dword_be(IFF_file, &aheader.abstime); + Read_dword_be(IFF_file, &aheader.reltime); + Read_byte(IFF_file, &aheader.interleave); + Read_byte(IFF_file, &aheader.pad0); + Read_dword_be(IFF_file, &aheader.bits); + section_size -= 24; + + if ((aheader.bits & 0xffffffc0) != 0) // invalid ? => clearing + aheader.bits = 0; + if (aheader.operation == 0) // ANHD for 1st frame (BODY) + Set_frame_duration(context, (aheader.reltime * 50) / 3); // convert 1/60th sec to msec + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes + } + else if (memcmp(section, "DPAN", 4) == 0) // Deluxe Paint ANimation + { + word version; + dword flags; + if (section_size >= 8) + { + Read_word_be(IFF_file, &version); + Read_word_be(IFF_file, &frame_count); + Read_dword_be(IFF_file, &flags); + section_size -= 8; + } + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes + } + else if (memcmp(section, "DLTA", 4) == 0) // Animation DeLTA + { + int i, plane; + dword offsets[16]; + dword current_offset = 0; + byte * frame; + + if (Image_HAM > 1) + { + GFX2_Log(GFX2_WARNING, "HAM animations are not supported\n"); + //Verbose_message("Notice", "HAM animations are not supported, loading only first frame"); // TODO: causes an issue with colors + break; + } + if (frame_count != 0 && current_frame >= (frame_count - 1)) + break; // some animations have delta from last to first frame + if (previous_frame == NULL) + { + real_line_size = (context->Width+15) & ~15; + plane_line_size = real_line_size >> 3; // 8bits per byte + line_size = plane_line_size * real_bit_planes; + + previous_frame = calloc(line_size * context->Height,1); + for (y_pos=0; y_posHeight; y_pos++) + { + const byte * pix_p = context->Target_address + y_pos * context->Pitch; + // Dispatch the pixel into planes + for (x_pos=0; x_posWidth; x_pos++) + { + Set_IFF_color(previous_frame+y_pos*line_size, x_pos, *pix_p++, real_line_size, real_bit_planes); + } + } + // many animations are designed for double buffering + // and delta is against frame n-2 + anteprevious_frame = malloc(line_size * context->Height); + memcpy(anteprevious_frame, previous_frame, line_size * context->Height); + } + + Set_loading_layer(context, ++current_frame); + Set_frame_duration(context, (aheader.reltime * 50) / 3 ); // convert 1/60th sec in msec + frame = previous_frame; + + if(aheader.operation == 5) // Byte Vertical Delta mode + { + if (aheader.interleave != 1) + frame = anteprevious_frame; + for (i = 0; i < 16; i++) + { + if (!Read_dword_be(IFF_file, offsets+i)) + { + File_error = 2; + break; + } + current_offset += 4; + } + for (plane = 0; plane < 16; plane++) + { + byte op_count = 0; + if (offsets[plane] == 0) + continue; + if (plane >= real_bit_planes) + { + GFX2_Log(GFX2_WARNING, "IFF : too much bitplanes in DLTA data %u >= %u (frame #%u/%u)\n", plane, real_bit_planes, current_frame + 1, frame_count); + break; + } + while (current_offset < offsets[plane]) + { + Read_byte(IFF_file, &op_count); + current_offset++; + } + if (current_offset > offsets[plane]) + { + GFX2_Log(GFX2_ERROR, "Loading ERROR in DLTA data\n"); + File_error = 2; + break; + } + for (x_pos = 0; x_pos < (context->Width+7) >> 3; x_pos++) + { + byte * p = frame + x_pos + plane * plane_line_size; + y_pos = 0; + Read_byte(IFF_file, &op_count); + current_offset++; + for (i = 0; i < op_count; i++) + { + byte op; + + if (y_pos >= context->Height) + { + } + Read_byte(IFF_file, &op); + current_offset++; + if (op == 0) + { // Same ops + byte countb, datab; + Read_byte(IFF_file, &countb); + Read_byte(IFF_file, &datab); + current_offset += 2; + while(countb > 0 && y_pos < context->Height) + { + if(aheader.bits & 2) // XOR + *p ^= datab; + else // set + *p = datab; + p += line_size; + y_pos++; + countb--; + } + } + else if (op & 0x80) + { // Uniq Ops + op &= 0x7f; + while (op > 0) + { + byte datab; + Read_byte(IFF_file, &datab); + current_offset++; + if (y_pos < context->Height) + { + if(aheader.bits & 2) // XOR + *p ^= datab; + else // set + *p = datab; + p += line_size; + y_pos++; + } + op--; + } + } + else + { // skip ops + p += op * line_size; + y_pos += op; + } + } + if (y_pos > context->Height) + { + } + } + } + } + else if(aheader.operation==74) + { + // from sources found on aminet : + // http://aminet.net/package/gfx/conv/unmovie + while (current_offset < section_size) + { + word change_type; + word uni_flag; + word y_size; + word x_size; + word num_blocks; + word offset; + word x_start, y_start; + + Read_word_be(IFF_file, &change_type); + current_offset += 2; + if (change_type == 0) + break; + else if (change_type == 1) + { // "Wall" + Read_word_be(IFF_file, &uni_flag); + Read_word_be(IFF_file, &y_size); + Read_word_be(IFF_file, &num_blocks); + current_offset += 6; + while (num_blocks-- > 0) + { + Read_word_be(IFF_file, &offset); + current_offset += 2; + x_start = offset % plane_line_size; + y_start = offset / plane_line_size; + for (plane = 0; plane < real_bit_planes; plane++) + { + byte * p = frame + plane * plane_line_size; + p += x_start + y_start*line_size; + for (y_pos=0; y_pos < y_size; y_pos++) + { + byte value; + Read_byte(IFF_file, &value); + current_offset++; + if (uni_flag) + *p ^= value; + else + *p = value; + } + p += line_size; + } + } + } + else if (change_type == 2) + { // "Pile" + Read_word_be(IFF_file, &uni_flag); + Read_word_be(IFF_file, &y_size); + Read_word_be(IFF_file, &x_size); + Read_word_be(IFF_file, &num_blocks); + current_offset += 8; + while (num_blocks-- > 0) + { + Read_word_be(IFF_file, &offset); + current_offset += 2; + x_start = offset % plane_line_size; + y_start = offset / plane_line_size; + for (plane = 0; plane < real_bit_planes; plane++) + { + byte * p = frame + plane * plane_line_size; + p += x_start + y_start*line_size; + for (y_pos=0; y_pos < y_size; y_pos++) + { + for (x_pos=0; x_pos < x_size; x_pos++) + { + byte value; + Read_byte(IFF_file, &value); + current_offset++; + if (uni_flag) + p[x_pos] ^= value; + else + p[x_pos] = value; + } + p += line_size; + } + } + } + } + else + { + GFX2_Log(GFX2_ERROR, "Unknown change type in type 74 DLTA\n"); + File_error = 2; + break; + } + if (current_offset & 1) // align to WORD boundary + { + byte junk; + Read_byte(IFF_file, &junk); + current_offset++; + } + } + } + else + { + GFX2_Log(GFX2_INFO, "IFF DLTA : Unsupported compression type %u\n", aheader.operation); + } + + if (File_error == 0) + { + for (y_pos=0; y_posHeight; y_pos++) + { + Draw_IFF_line(context, frame+line_size*y_pos,y_pos,real_line_size,real_bit_planes); + } + } + if (aheader.operation == 5 && aheader.interleave != 1) + { + anteprevious_frame = previous_frame; + previous_frame = frame; + } + if (current_offset&1) + { + byte dummy_byte; + Read_byte(IFF_file, &dummy_byte); + current_offset++; + } + section_size -= current_offset; + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it + } + else if (memcmp(section, "ADAT", 4) == 0) + { // Animation made with Deluxe Paint for Atari ST + buffer = previous_frame; + if (section_size > 0) + { + byte * p; + byte data[2]; + word cmd_count; + word cmd; + signed char * commands; + word count; + + y_pos = 0; x_pos = 0; + Read_word_be(IFF_file,&cmd_count); + cmd_count -= 2; + commands = (signed char *)malloc(cmd_count); + if (!Read_bytes(IFF_file,commands,cmd_count)) + { + File_error = 31; + break; + } + section_size -= (cmd_count + 2); + for (cmd = 0; cmd < cmd_count && section_size > 0; cmd++) + { + if (commands[cmd] == 0) + { // move pointer + word offset; + short x_ofs, y_ofs; + Read_word_be(IFF_file,&offset); + section_size -= 2; + if((short)offset < 0) { + y_ofs = ((short)offset - plane_line_size + 1) / plane_line_size; + x_ofs = (short)offset - y_ofs * plane_line_size; + } else { + x_ofs = offset % plane_line_size; + y_ofs = offset / plane_line_size; + } + y_pos += y_ofs; + x_pos += x_ofs; + } + else if(commands[cmd] < 0) + { // XOR with a string of Words + if (commands[cmd] == -1 && section_size >= 2) + { + Read_word_be(IFF_file,&count); + section_size -= 2; + } + else + count = -commands[cmd] - 1; + while (count-- > 0 && y_pos < context->Height && section_size > 0) + { + p = buffer+x_pos+y_pos*line_size+vdlt_plane*plane_line_size; + Read_bytes(IFF_file,data,2); + section_size -= 2; + p[0] ^= data[0]; + p[1] ^= data[1]; + y_pos++; + } + } + else // commands[cmd] > 0 + { // XOR a word several times + if (commands[cmd] == 1) + { + Read_word_be(IFF_file,&count); + section_size -= 2; + if (section_size < 2) + break; + } + else + count = commands[cmd] - 1; + Read_bytes(IFF_file,data,2); + section_size -= 2; + do + { + p = buffer+x_pos+y_pos*line_size+vdlt_plane*plane_line_size; + p[0] ^= data[0]; + p[1] ^= data[1]; + y_pos++; + } + while (count-- > 0 && y_pos < context->Height); + } + } + free(commands); + if(cmd < (cmd_count-1) || section_size > 0) + { + GFX2_Log(GFX2_WARNING, "Early end in ADAT chunk\n"); + } + } + + vdlt_plane++; + if (vdlt_plane == real_bit_planes) + { + for (y_pos=0; y_posHeight; y_pos++) + { + Draw_IFF_line(context, buffer+line_size*y_pos,y_pos,real_line_size,real_bit_planes); + } + } + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip remaining bytes + } + else if (memcmp(section, "CMAP", 4) == 0) // Colour MAP + { + nb_colors = section_size/3; + + if (nb_colors > 256) + { + File_error = 1; + break; + } + GFX2_Log(GFX2_DEBUG, "IFF CMAP : %u colors (header.BitPlanes=%u)\n", nb_colors, header.BitPlanes); + + if (current_frame != 0) + GFX2_Log(GFX2_WARNING, "IFF : One CMAP per frame is not supported (frame #%u/%u)\n", current_frame + 1, frame_count); + if ((header.BitPlanes==6 && nb_colors==16) || (header.BitPlanes==8 && nb_colors==64)) + { + Image_HAM=header.BitPlanes; + bpp = 3 * (header.BitPlanes - 2); // HAM6 = 12bpp, HAM8 = 18bpp + } + + if (Read_bytes(IFF_file,context->Palette,3*nb_colors)) + { + section_size -= 3*nb_colors; + if (nb_colors <= 64) + { + unsigned int i; + int is_4bits_colors = 1; // recoil also checks that header.Pad1 is not 0x80 + for(i = 0; i < nb_colors && is_4bits_colors; i++) + { + is_4bits_colors = is_4bits_colors && ((context->Palette[i].R & 0x0f) == 0) + && ((context->Palette[i].G & 0x0f) == 0) + && ((context->Palette[i].B & 0x0f) == 0); + } + if (is_4bits_colors) // extend 4bits per component color palettes to 8bits per component + for(i = 0; i < nb_colors; i++) + { + context->Palette[i].R |= (context->Palette[i].R >> 4); + context->Palette[i].G |= (context->Palette[i].G >> 4); + context->Palette[i].B |= (context->Palette[i].B >> 4); + } + } + if (((nb_colors==32) || (AmigaViewModes & 0x80)) && (header.BitPlanes==6)) + { + IFF_Set_EHB_Palette(context->Palette); // This is a Extra Half-Brite (EHB) 64 color image. + nb_colors = 64; + } + + while(section_size > 0) // Read padding bytes + { + if (Read_byte(IFF_file,&temp_byte)) + File_error=20; + section_size--; + } + } + else + File_error=1; + if (context->Type == CONTEXT_PALETTE || context->Type == CONTEXT_PREVIEW_PALETTE) + break; // stop once the palette is loaded + } + else if (memcmp(section,"CRNG",4) == 0) + { + // The content of a CRNG is as follows: + word padding; + word rate; + word flags; + byte min_col; + byte max_col; + if ( (Read_word_be(IFF_file,&padding)) + && (Read_word_be(IFF_file,&rate)) + && (Read_word_be(IFF_file,&flags)) + && (Read_byte(IFF_file,&min_col)) + && (Read_byte(IFF_file,&max_col))) + { + GFX2_Log(GFX2_DEBUG, "IFF CRNG : [#%u #%u] rate=%u flags=%04x\n", min_col, max_col, rate, flags); + if (section_size == 8 && min_col != max_col) + { + // Valid cycling range + if (max_colColor_cycles >= 16) + { + GFX2_Log(GFX2_INFO, "IFF CRNG : Maximum CRNG number is 16\n"); + } + else + { + word speed; + + context->Cycle_range[context->Color_cycles].Start=min_col; + context->Cycle_range[context->Color_cycles].End=max_col; + context->Cycle_range[context->Color_cycles].Inverse=(flags&2)?1:0; + speed = (flags&1) ? rate/78 : 0; + context->Cycle_range[context->Color_cycles].Speed = (speed > COLOR_CYCLING_SPEED_MAX) ? COLOR_CYCLING_SPEED_MAX : speed; + context->Color_cycles++; + } + } + } + else + File_error=47; + } + else if (memcmp(section, "DRNG", 4) == 0) // DPaint IV enhanced color cycle chunk + { + /// @todo DRNG IFF chunk is read, but complex color cycling are not implemented. + // see http://amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node0282.html + byte min_col; + byte max_col; + word rate; + word flags; + byte ntruecolor; + byte nregs; + Read_byte(IFF_file, &min_col); + Read_byte(IFF_file, &max_col); + Read_word_be(IFF_file, &rate); + Read_word_be(IFF_file, &flags); + Read_byte(IFF_file, &ntruecolor); + Read_byte(IFF_file, &nregs); + section_size -= 8; + GFX2_Log(GFX2_DEBUG, "IFF DRNG : [#%u #%u] rate=%u flags=%04x, %u tc cells, %u reg cells\n", min_col, max_col, rate, flags, ntruecolor, nregs); + while (ntruecolor-- > 0 && section_size > 0) + { + byte cell, r, g, b; + Read_byte(IFF_file, &cell); + Read_byte(IFF_file, &r); + Read_byte(IFF_file, &g); + Read_byte(IFF_file, &b); + section_size -= 4; + GFX2_Log(GFX2_DEBUG, " #%u #%02X%02X%02X\n", cell, r, g, b); + } + while (nregs-- > 0 && section_size > 0) + { + byte cell, index; + Read_byte(IFF_file, &cell); + Read_byte(IFF_file, &index); + section_size -= 2; + GFX2_Log(GFX2_DEBUG, " #%u index %u\n", cell, index); + } + if (section_size > 0) + { + GFX2_Log(GFX2_DEBUG, "IFF DRNG : skipping %u bytes\n", section_size); + fseek(IFF_file, section_size, SEEK_CUR); + } + } + else if (memcmp(section, "CCRT", 4) == 0) // Color Cycling Range and Timing + { + // see http://amigadev.elowar.com/read/ADCD_2.1/Devices_Manual_guide/node01BA.html + word direction; + byte start, end; + dword seconds, microseconds; + word pad; + + Read_word_be(IFF_file, &direction); + Read_byte(IFF_file, &start); + Read_byte(IFF_file, &end); + Read_dword_be(IFF_file, &seconds); + Read_dword_be(IFF_file, µseconds); + Read_word_be(IFF_file, &pad); + section_size -= 14; + GFX2_Log(GFX2_DEBUG, "IFF CCRT : dir %04x [#%u #%u] delay %u.%06u\n", + direction, start, end, seconds, microseconds); + if (context->Color_cycles >= 16) + { + GFX2_Log(GFX2_INFO, "IFF CCRT : Maximum CRNG number is 16\n"); + } + else if (start != end) + { + // Speed resolution is 0.2856Hz + // Speed = (1000000/delay) / 0.2856 = 3501400 / delay + + context->Cycle_range[context->Color_cycles].Start = start; + context->Cycle_range[context->Color_cycles].End = end; + if (direction != 0) + { + dword speed; + context->Cycle_range[context->Color_cycles].Inverse = (~direction >> 15) & 1; + speed = 3501400 / (seconds * 1000000 + microseconds); + context->Cycle_range[context->Color_cycles].Speed = (speed > COLOR_CYCLING_SPEED_MAX) ? COLOR_CYCLING_SPEED_MAX : speed; + } + else + { + context->Cycle_range[context->Color_cycles].Speed = 0; + } + + context->Color_cycles++; + } + } + else if (memcmp(section, "DPI ", 4) == 0) + { + word dpi_x, dpi_y; + Read_word_be(IFF_file, &dpi_x); + Read_word_be(IFF_file, &dpi_y); + section_size -= 4; + GFX2_Log(GFX2_DEBUG, "IFF DPI %hu x %hu\n", dpi_x, dpi_y); + } + else if (memcmp(section, "CAMG", 4) == 0) // Amiga Viewport Modes + { + Read_dword_be(IFF_file, &AmigaViewModes); // HIRES=0x8000 LACE=0x4 HAM=0x800 HALFBRITE=0x80 + section_size -= 4; + GFX2_Log(GFX2_DEBUG, "CAMG = $%08x\n", AmigaViewModes); + if (AmigaViewModes & 0x800 && (header.BitPlanes == 6 || header.BitPlanes == 8)) + { + Image_HAM = header.BitPlanes; + bpp = 3 * (header.BitPlanes - 2); + } + if ((AmigaViewModes & 0x80) && (header.BitPlanes == 6)) // This is a Extra Half-Brite (EHB) 64 color image. + { + IFF_Set_EHB_Palette(context->Palette); // Set the palette in case CAMG is after CMAP + nb_colors = 64; + } + } + else if (memcmp(section, "DPPV", 4) == 0) // DPaint II ILBM perspective chunk + { + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it + } + else if (memcmp(section, "CLUT", 4) == 0) // lookup table + { + dword lut_type; // 0 = A Monochrome, contrast or intensity LUT + byte lut[256]; // 1 = RED, 2 = GREEN, 3 = BLUE, 4 = HUE, 5 = SATURATION + + Read_dword_be(IFF_file, &lut_type); + Read_dword_be(IFF_file, &dummy); + Read_bytes(IFF_file, lut, 256); + section_size -= (4+4+256); + if (section_size > 0) + { + GFX2_Log(GFX2_WARNING, "Extra bytes at the end of CLUT chunk\n"); + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); + } + } + else if (memcmp(section, "DYCP", 4) == 0) // DYnamic Color Palette + { + // All files I've seen have 4 words (8bytes) : + // { 0, 1, 16, 0} 16 is probably the number of colors in each palette + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip it + } + else if (memcmp(section, "SHAM", 4) == 0) // Sliced HAM + { + int i; + word version; + dword SHAM_palette_count; + T_IFF_PCHG_Palette * prev_pal = NULL; + T_IFF_PCHG_Palette * new_pal = NULL; + + Image_HAM = header.BitPlanes; + bpp = 3 * (header.BitPlanes - 2); + Read_word_be(IFF_file, &version); // always 0 + section_size -= 2; + SHAM_palette_count = section_size >> 5; // 32 bytes per palette (16 colors * 2 bytes) + // SHAM_palette_count should be the image height, or height/2 for "interlaced" images + for (y_pos = 0; y_pos < header.Height && section_size >= 32; y_pos += (SHAM_palette_count < header.Height ? 2 : 1)) + { + new_pal = GFX2_malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); + if (new_pal == NULL) + { + File_error = 1; + break; + } + new_pal->Next = NULL; + new_pal->StartLine = y_pos; + for (i = 0; i < 16; i++) + { + Read_byte(IFF_file, &temp_byte); // 0R + new_pal->Palette[i].R = (temp_byte & 0x0f) * 0x11; // 4 bits to 8 bits + Read_byte(IFF_file, &temp_byte); // GB + new_pal->Palette[i].G = (temp_byte & 0xf0) | (temp_byte >> 4); + new_pal->Palette[i].B = (temp_byte & 0x0f) * 0x11; // 4 bits to 8 bits + section_size -= 2; + } + if (prev_pal != NULL) + prev_pal->Next = new_pal; + else + PCHG_palettes = new_pal; + prev_pal = new_pal; + } + if (section_size > 0) + { + GFX2_Log(GFX2_WARNING, "Extra bytes at the end of SHAM chunk\n"); + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); + } + } + else if (memcmp(section, "BEAM", 4) == 0 || memcmp(section, "CTBL", 4) == 0) + { + // One palette per line is stored + if (section_size >= header.Height * nb_colors * 2) + { + T_Palette palette; + T_IFF_PCHG_Palette * prev_pal = NULL; + T_IFF_PCHG_Palette * new_pal = NULL; + + for (y_pos = 0; y_pos < header.Height; y_pos++) + { + unsigned int i; + for (i = 0; i < nb_colors; i++) + { + word value; + Read_word_be(IFF_file, &value); + section_size -= 2; + palette[i].R = ((value & 0x0f00) >> 8) * 0x11; + palette[i].G = ((value & 0x00f0) >> 4) * 0x11; + palette[i].B = (value & 0x000f) * 0x11; + } + if (y_pos == 0) + { + prev_pal = GFX2_malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); + if (prev_pal == NULL) + { + File_error = 1; + break; + } + prev_pal->Next = NULL; + prev_pal->StartLine = 0; + memcpy(prev_pal->Palette, palette, nb_colors*sizeof(T_Components)); + PCHG_palettes = prev_pal; + } + else if (memcmp(palette, prev_pal->Palette, nb_colors*sizeof(T_Components)) != 0) + { + new_pal = GFX2_malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); + if (new_pal == NULL) + { + File_error = 1; + break; + } + new_pal->Next = NULL; + new_pal->StartLine = y_pos; + memcpy(new_pal->Palette, palette, nb_colors*sizeof(T_Components)); + prev_pal->Next = new_pal; + prev_pal = new_pal; + } + } + if (PCHG_palettes != NULL) + bpp = 12; + } + else + GFX2_Log(GFX2_WARNING, "inconsistent size of BEAM/CTLB chunk, ignoring\n"); + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); + } + else if (memcmp(section, "PCHG", 4) == 0) // Palette CHanGes + { + dword * lineBitMask; + int i; + T_IFF_PCHG_Palette * prev_pal = NULL; + T_IFF_PCHG_Palette * curr_pal = NULL; + void * PCHGData = NULL; + + // http://wiki.amigaos.net/wiki/ILBM_IFF_Interleaved_Bitmap#ILBM.PCHG + // http://vigna.di.unimi.it/software.php + // Header + word Compression; // 0 = None, 1 = Huffman + word Flags; // 0x1 = SmallLineChanges, 0x2 = BigLineChanges, 0x4 = use alpha + short StartLine; // possibly negative + word LineCount; + word ChangedLines; + word MinReg; + word MaxReg; + word MaxChanges; + dword TotalChanges; + + if (!(Read_word_be(IFF_file, &Compression) + && Read_word_be(IFF_file, &Flags) + && Read_word_be(IFF_file, (word *)&StartLine) + && Read_word_be(IFF_file, &LineCount) + && Read_word_be(IFF_file, &ChangedLines) + && Read_word_be(IFF_file, &MinReg) + && Read_word_be(IFF_file, &MaxReg) + && Read_word_be(IFF_file, &MaxChanges) + && Read_dword_be(IFF_file, &TotalChanges))) + File_error = 1; + section_size -= 20; + if (Compression) + { + short * TreeCode; + const short * TreeP; //a3 + int remaining_bits = 0; //d1 + dword src_dword = 0; //d2 + byte * dst_data; //a1 + + dword CompInfoSize; + dword OriginalDataSize; //d0 + + Read_dword_be(IFF_file, &CompInfoSize); + Read_dword_be(IFF_file, &OriginalDataSize); + section_size -= 8; + // read HuffMan tree + TreeCode = malloc(CompInfoSize); + for (i = 0; i < (int)(CompInfoSize / 2); i++) + Read_word_be(IFF_file, (word *)(TreeCode + i)); + section_size -= CompInfoSize; + + PCHGData = malloc(OriginalDataSize); + dst_data = PCHGData; + // HuffMan depacking + TreeP = TreeCode+(CompInfoSize/2-1); // pointer to the last element + while (OriginalDataSize > 0) + { + if (--remaining_bits < 0) + { + Read_dword_be(IFF_file, &src_dword); + section_size -= 4; + remaining_bits = 31; + } + if (src_dword & (1 << remaining_bits)) + { + if (*TreeP < 0) + { + // OffsetPointer + TreeP += (*TreeP / 2); + continue; // pick another bit + } + } + else + { + // Case0 + --TreeP; + if ((*TreeP < 0) || !(*TreeP & 0x100)) + continue; // pick another bit + } + // StoreValue + *dst_data = (byte)(*TreeP & 0xff); + dst_data++; + TreeP = TreeCode+(CompInfoSize/2-1); // pointer to the last element + OriginalDataSize--; + } + free(TreeCode); + } + else + { + PCHGData = malloc(section_size); + Read_bytes(IFF_file, PCHGData, section_size); + section_size = 0; + } + if (PCHGData != NULL) + { + const byte * data; + // initialize first palette from CMAP + prev_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); + prev_pal->Next = NULL; + prev_pal->StartLine = 0; + memcpy(prev_pal->Palette, context->Palette, nb_colors*sizeof(T_Components)); + PCHG_palettes = prev_pal; + + lineBitMask = (dword *)PCHGData; +#if defined(SDL_BYTEORDER) && (SDL_BYTEORDER != SDL_BIG_ENDIAN) + for (i = 0 ; i < ((LineCount + 31) >> 5); i++) + lineBitMask[i] = SDL_Swap32(lineBitMask[i]); +#elif defined(BYTE_ORDER) && (BYTE_ORDER != BIG_ENDIAN) + for (i = 0 ; i < ((LineCount + 31) >> 5); i++) + lineBitMask[i] = be32toh(lineBitMask[i]); +#elif defined(WIN32) + // assume WIN32 is little endian + for (i = 0 ; i < ((LineCount + 31) >> 5); i++) + lineBitMask[i] = WIN32_BSWAP32(lineBitMask[i]); +#endif + data = (const byte *)PCHGData + ((LineCount + 31) >> 5) * 4; + for (y_pos = 0 ; y_pos < LineCount; y_pos++) + { + if (lineBitMask[y_pos >> 5] & (1 << (31-(y_pos & 31)))) + { + byte ChangeCount16, ChangeCount32; + word PaletteChange; + + if ((y_pos + StartLine) < 0) + curr_pal = PCHG_palettes; + else + { + curr_pal = malloc(sizeof(T_IFF_PCHG_Palette) + nb_colors*sizeof(T_Components)); + curr_pal->Next = NULL; + curr_pal->StartLine = StartLine + y_pos; + memcpy(curr_pal->Palette, prev_pal->Palette, nb_colors*sizeof(T_Components)); + prev_pal->Next = curr_pal; + } + + ChangeCount16 = *data++; + ChangeCount32 = *data++; + for (i = 0; i < ChangeCount16; i++) + { + PaletteChange = data[0] << 8 | data[1]; // Big endian + data += 2; + curr_pal->Palette[(PaletteChange >> 12)].R = ((PaletteChange & 0x0f00) >> 8) * 0x11; + curr_pal->Palette[(PaletteChange >> 12)].G = ((PaletteChange & 0x00f0) >> 4) * 0x11; + curr_pal->Palette[(PaletteChange >> 12)].B = ((PaletteChange & 0x000f) >> 0) * 0x11; + } + for (i = 0; i < ChangeCount32; i++) + { + PaletteChange = data[0] << 8 | data[1]; // Big endian + data += 2; + curr_pal->Palette[16+(PaletteChange >> 12)].R = ((PaletteChange & 0x0f00) >> 8) * 0x11; + curr_pal->Palette[16+(PaletteChange >> 12)].G = ((PaletteChange & 0x00f0) >> 4) * 0x11; + curr_pal->Palette[16+(PaletteChange >> 12)].B = ((PaletteChange & 0x000f) >> 0) * 0x11; + } + if (nb_colors == 64) // Extend the 32 colors decoded to 64 + IFF_Set_EHB_Palette(curr_pal->Palette); + prev_pal = curr_pal; + } + } + free(PCHGData); + } + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); + if (PCHG_palettes != NULL) + bpp = 12; + } + else if (memcmp(section, "RAST", 4) == 0) // Atari ST + { + if (PCHG_palettes == NULL) + { + RAST_chunk_decode(context, IFF_file, section_size, &PCHG_palettes); + if (PCHG_palettes != NULL) + bpp = 12; + } + else + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); // Skip + } + else if (memcmp(section, "TINY", 4) == 0) + { + word tiny_width, tiny_height; + Read_word_be(IFF_file,&tiny_width); + Read_word_be(IFF_file,&tiny_height); + section_size -= 4; + + // Load thumbnail if in preview mode + if ((context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE) + && tiny_width > 0 && tiny_height > 0) + { + context->Original_width = header.Width; + context->Original_height = header.Height; + Pre_load(context, tiny_width, tiny_height,file_size,iff_format,ratio,bpp); + context->Background_transparent = header.Mask == 2; + context->Transparent_color = context->Background_transparent ? header.Transp_col : 0; + if (iff_format == FORMAT_PBM) + PBM_Decode(context, IFF_file, header.Compression, tiny_width, tiny_height); + else + LBM_Decode(context, IFF_file, header.Compression, Image_HAM, stored_bit_planes, real_bit_planes, PCHG_palettes); + fclose(IFF_file); + IFF_file = NULL; + return; + } + else + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); + } + else if (memcmp(section, "ANNO", 4) == 0) + { + dword length; + section_size = (section_size + 1) & ~1; + length = section_size; + if (length > COMMENT_SIZE) + length = COMMENT_SIZE; + Read_bytes(IFF_file,context->Comment,length); + context->Comment[length]='\0'; + section_size -= length; + fseek(IFF_file, section_size, SEEK_CUR); + } + else if (memcmp(section, "ABIT", 4) == 0) + { + // ACBM format : ABIT = Amiga BITplanes + // The ABIT chunk contains contiguous bitplane data. + // The chunk contains sequential data for bitplane 0 through bitplane n. + if (header.Width == 0 || header.Height == 0) + break; + Pre_load(context, header.Width, header.Height, file_size, iff_format, ratio, bpp); + // compute row size + real_line_size = (context->Width+15) & ~15; + plane_line_size = real_line_size >> 3; // 8bits per byte + line_size = plane_line_size * stored_bit_planes; + buffer = malloc(line_size * context->Height); + if ((dword)(line_size * context->Height) == section_size) + header.Compression = 0; // size is of uncompressed data. Forcing. + for (plane = 0; plane < stored_bit_planes; plane++) + { + for (y_pos = 0; y_pos < context->Height; y_pos++) + { + if (header.Compression == 0) + { + if (!Read_bytes(IFF_file,buffer+line_size*y_pos+plane_line_size*plane,plane_line_size)) + { + File_error = 21; + break; + } + } + else + { + GFX2_Log(GFX2_WARNING, "Unhandled compression %d for ACBM ABIT chunk\n", header.Compression); + File_error = 32; + break; + } + } + } + if (File_error == 0) + { + for (y_pos = 0; y_pos < context->Height; y_pos++) + { + if (Image_HAM <= 1) + Draw_IFF_line(context, buffer+y_pos*line_size, y_pos,real_line_size, real_bit_planes); + else + Draw_IFF_line_HAM(context, buffer+y_pos*line_size, y_pos,real_line_size, real_bit_planes, PCHG_palettes); + } + } + free(buffer); + buffer = NULL; + } + else if (memcmp(section, "BODY", 4) == 0) + { + long offset = ftell(IFF_file); + if (file_size > (unsigned long)(offset + section_size + 8)) + { + // Chunk RAST is placed AFTER the BODY, but we need the palette now to decode the image + // In addition, some files break the IFF standard by not aligning + // the chunk on word boundaries. + fseek(IFF_file, section_size, SEEK_CUR); + Read_bytes(IFF_file, section, 1); + if (section[0] == 'R') // we are good + Read_bytes(IFF_file, section + 1, 3); // read the remaining 3 bytes + else // skip 1 byte + Read_bytes(IFF_file, section, 4); // read 4 bytes + if (memcmp(section, "RAST", 4) == 0) + { + dword rast_size; + Read_dword_be(IFF_file, &rast_size); + RAST_chunk_decode(context, IFF_file, rast_size, &PCHG_palettes); + if (PCHG_palettes != NULL) + bpp = 12; + } + fseek(IFF_file, offset, SEEK_SET); // rewind + } + if (header.Width == 0 || header.Height == 0) + break; + Original_screen_X = header.X_screen; + Original_screen_Y = header.Y_screen; + + Pre_load(context, header.Width, header.Height, file_size, iff_format, ratio, bpp); + context->Background_transparent = header.Mask == 2; + context->Transparent_color = context->Background_transparent ? header.Transp_col : 0; + + Set_image_mode(context, IMAGE_MODE_ANIMATION); + + if (iff_format == FORMAT_LBM) // "ILBM": InterLeaved BitMap + { + LBM_Decode(context, IFF_file, header.Compression, Image_HAM, stored_bit_planes, real_bit_planes, PCHG_palettes); + } + else // "PBM ": Packed BitMap + { + PBM_Decode(context, IFF_file, header.Compression, context->Width, context->Height); + } + if (ftell(IFF_file) & 1) + fseek(IFF_file, 1, SEEK_CUR); // SKIP one byte + if (context->Type == CONTEXT_PREVIEW || context->Type == CONTEXT_PREVIEW_PALETTE) + { + break; // dont load animations in Preview mode + } + } + else + { + // skip section + GFX2_Log(GFX2_INFO, "IFF : Skip unknown section '%.4s' of %u bytes at offset %06X\n", section, section_size, ftell(IFF_file)); + if (section_size < 256) + { + byte buffer[256]; + + Read_bytes(IFF_file, buffer, (section_size+1)&~1); + GFX2_LogHexDump(GFX2_DEBUG, "", buffer, 0, (section_size+1)&~1); + } + else + fseek(IFF_file, (section_size+1)&~1, SEEK_CUR); + } + } + } + + fclose(IFF_file); + IFF_file = NULL; + } + else + File_error=1; + if (previous_frame) + free(previous_frame); + if (anteprevious_frame) + free(anteprevious_frame); + while (PCHG_palettes != NULL) + { + T_IFF_PCHG_Palette * next = PCHG_palettes->Next; + free(PCHG_palettes); + PCHG_palettes = next; + } +} + + +// -- Sauver un fichier au format IFF --------------------------------------- + + byte IFF_color_list[129]; + word IFF_list_size; + byte IFF_repetition_mode; + + // ------------- Ecrire les couleurs que l'on vient de traiter ------------ + void Transfer_colors(FILE * file) + { + byte index; + + if (IFF_list_size>0) + { + if (IFF_list_size > 128) + { + GFX2_Log(GFX2_ERROR, "Transfer_colors() IFF_list_size=%d !\n", IFF_list_size); + } + if (IFF_repetition_mode) + { + Write_one_byte(file,257-IFF_list_size); + Write_one_byte(file,IFF_color_list[0]); + } + else + { + Write_one_byte(file,IFF_list_size-1); + for (index=0; index= 127) + { + // diff mode with 126 bytes then 2 identical bytes + IFF_list_size--; + Transfer_colors(file); + IFF_color_list[0]=color; + IFF_color_list[1]=color; + IFF_list_size=2; + IFF_repetition_mode=1; + } + else if ( (IFF_repetition_mode) || (second_last_color!=color) ) + { + // same mode is kept + if (IFF_list_size==128) + Transfer_colors(file); + IFF_color_list[IFF_list_size++]=color; + } + else + { + // diff mode and 3 identical bytes + IFF_list_size-=2; + Transfer_colors(file); + IFF_color_list[0]=color; + IFF_color_list[1]=color; + IFF_color_list[2]=color; + IFF_list_size=3; + IFF_repetition_mode=1; + } + } + else // the color is different from the previous one + { + if (!IFF_repetition_mode) // keep mode + { + if (IFF_list_size == 128) + Transfer_colors(file); + IFF_color_list[IFF_list_size++]=color; + } + else // change mode + { + Transfer_colors(file); + IFF_color_list[IFF_list_size]=color; + IFF_list_size++; + } + } + } + } + + +/// Save IFF file (LBM or PBM) +void Save_IFF(T_IO_Context * context) +{ + FILE * IFF_file; + T_IFF_Header header; + word x_pos; + word y_pos; + byte temp_byte; + int i; + int palette_entries; + byte bit_depth; + long body_offset = -1; + int is_ehb = 0; // Extra half-bright + + if (context->Format == FORMAT_LBM) + { + // Check how many bits are used by pixel colors + temp_byte = 0; + for (y_pos=0; y_posHeight; y_pos++) + for (x_pos=0; x_posWidth; x_pos++) + temp_byte |= Get_pixel(context, x_pos,y_pos); + bit_depth=0; + do + { + bit_depth++; + temp_byte>>=1; + } while (temp_byte); + if (bit_depth == 6) + { + for (i = 0; i < 32; i++) + { + if ( (context->Palette[i].R >> 5) != (context->Palette[i+32].R >> 4) + || (context->Palette[i].G >> 5) != (context->Palette[i+32].G >> 4) + || (context->Palette[i].B >> 5) != (context->Palette[i+32].B >> 4)) + break; + } + if (i == 32) is_ehb = 1; + } + GFX2_Log(GFX2_DEBUG, "Saving ILBM with bit_depth = %d %s\n", bit_depth, + is_ehb ? "(Extra Half-Bright)" : ""); + palette_entries = 1 << (bit_depth - is_ehb); + } + else // FORMAT_PBM + { + bit_depth = 8; + palette_entries = 256; + } + + File_error=0; + + // Ouverture du fichier + if ((IFF_file=Open_file_write(context))) + { + setvbuf(IFF_file, NULL, _IOFBF, 64*1024); + + Write_bytes(IFF_file,"FORM",4); + Write_dword_be(IFF_file,0); // On mettra la taille à jour à la fin + + if (context->Format == FORMAT_LBM) + Write_bytes(IFF_file,"ILBM",4); + else + Write_bytes(IFF_file,"PBM ",4); + + Write_bytes(IFF_file,"BMHD",4); + Write_dword_be(IFF_file,20); + + header.Width=context->Width; + header.Height=context->Height; + header.X_org=0; + header.Y_org=0; + header.BitPlanes=bit_depth; + header.Mask=context->Background_transparent ? 2 : 0; + header.Compression=1; + header.Pad1=0; + header.Transp_col=context->Background_transparent ? context->Transparent_color : 0; + header.X_aspect=10; // Amiga files are usually 10:11 + header.Y_aspect=10; + switch (context->Ratio) + { + case PIXEL_SIMPLE: + case PIXEL_DOUBLE: + case PIXEL_TRIPLE: + case PIXEL_QUAD: + default: + break; + case PIXEL_WIDE: + case PIXEL_WIDE2: + header.X_aspect *= 2; // 2:1 + break; + case PIXEL_TALL3: // 3:4 + header.X_aspect = (header.X_aspect * 15) / 10; // *1.5 +#if defined(__GNUC__) && (__GNUC__ >= 7) + __attribute__ ((fallthrough)); +#endif + case PIXEL_TALL: + case PIXEL_TALL2: + header.Y_aspect *= 2; // 1:2 + break; + } + header.X_screen = context->Width;// Screen_width?; + header.Y_screen = context->Height;// Screen_height?; + + Write_word_be(IFF_file,header.Width); + Write_word_be(IFF_file,header.Height); + Write_word_be(IFF_file,header.X_org); + Write_word_be(IFF_file,header.Y_org); + Write_bytes(IFF_file,&header.BitPlanes,1); + Write_bytes(IFF_file,&header.Mask,1); + Write_bytes(IFF_file,&header.Compression,1); + Write_bytes(IFF_file,&header.Pad1,1); + Write_word_be(IFF_file,header.Transp_col); + Write_bytes(IFF_file,&header.X_aspect,1); + Write_bytes(IFF_file,&header.Y_aspect,1); + Write_word_be(IFF_file,header.X_screen); + Write_word_be(IFF_file,header.Y_screen); + + if (context->Format == FORMAT_LBM) + { + dword ViewMode = 0; // HIRES=0x8000 LACE=0x4 HAM=0x800 HALFBRITE=0x80 + if (is_ehb) ViewMode |= 0x80; + if (context->Width > 400) + { + ViewMode |= 0x8000; + if (context->Width > 800) + ViewMode |= 0x20; // Super High-Res + } + if (context->Height > 300) ViewMode |= 0x4; + Write_bytes(IFF_file, "CAMG", 4); + Write_dword_be(IFF_file, 4); // Section size + Write_dword_be(IFF_file, ViewMode); + } + + Write_bytes(IFF_file,"CMAP",4); + Write_dword_be(IFF_file,palette_entries*3); + + Write_bytes(IFF_file,context->Palette,palette_entries*3); + + if (context->Comment[0]) // write ANNO + { + dword comment_size; + + Write_bytes(IFF_file,"ANNO",4); // Chunk name + comment_size = strlen(context->Comment); // NULL termination is not needed + Write_dword_be(IFF_file, comment_size); // Section size + Write_bytes(IFF_file, context->Comment, comment_size); + if (comment_size&1) + Write_byte(IFF_file, 0); // align to WORD boundaries + } + + for (i=0; iColor_cycles; i++) + { + word flags=0; + flags|= context->Cycle_range[i].Speed?1:0; // Cycling or not + flags|= context->Cycle_range[i].Inverse?2:0; // Inverted + + Write_bytes(IFF_file,"CRNG",4); + Write_dword_be(IFF_file,8); // Section size + Write_word_be(IFF_file,0); // Padding + Write_word_be(IFF_file,context->Cycle_range[i].Speed*78); // Rate + Write_word_be(IFF_file,flags); // Flags + Write_byte(IFF_file,context->Cycle_range[i].Start); // Min color + Write_byte(IFF_file,context->Cycle_range[i].End); // Max color + // No padding, size is multiple of 2 + } + + body_offset = ftell(IFF_file); + Write_bytes(IFF_file,"BODY",4); + Write_dword_be(IFF_file,0); // On mettra la taille à jour à la fin + + if (context->Format == FORMAT_LBM) + { + byte * buffer; + short line_size; // Size of line in bytes + short plane_line_size; // Size of line in bytes for 1 plane + short real_line_size; // Size of line in pixels + + // Calcul de la taille d'une ligne ILBM (pour les images ayant des dimensions exotiques) + real_line_size = (context->Width+15) & ~15; + plane_line_size = real_line_size >> 3; // 8bits per byte + line_size = plane_line_size * header.BitPlanes; + buffer=(byte *)malloc(line_size); + + // Start encoding + IFF_list_size=0; + for (y_pos=0; ((y_posHeight) && (!File_error)); y_pos++) + { + // Dispatch the pixel into planes + memset(buffer,0,line_size); + for (x_pos=0; x_posWidth; x_pos++) + Set_IFF_color(buffer, x_pos, Get_pixel(context, x_pos,y_pos), real_line_size, header.BitPlanes); + + if (context->Width&1) // odd width fix + Set_IFF_color(buffer, x_pos, 0, real_line_size, header.BitPlanes); + + // encode the resulting sequence of bytes + if (header.Compression) + { + int plane_width=line_size/header.BitPlanes; + int plane; + + for (plane=0; planeHeight) && (!File_error)); y_pos++) + { + for (x_pos=0; ((x_posWidth) && (!File_error)); x_pos++) + New_color(IFF_file, Get_pixel(context, x_pos,y_pos)); + + if (context->Width&1) // odd width fix + New_color(IFF_file, 0); + + if (!File_error) + Transfer_colors(IFF_file); + } + } + // Now update FORM and BODY size + if (!File_error) + { + long file_size = ftell(IFF_file); + if (file_size&1) + { + // PAD to even file size + if (! Write_byte(IFF_file,0)) + File_error=1; + } + // Write BODY size + fseek(IFF_file, body_offset + 4, SEEK_SET); + Write_dword_be(IFF_file, file_size-body_offset-8); + // Write FORM size + file_size = (file_size+1)&~1; + fseek(IFF_file,4,SEEK_SET); + Write_dword_be(IFF_file,file_size-8); + } + fclose(IFF_file); + if (File_error != 0) // remove the file if there have been an error + Remove_file(context); + } + else + File_error=1; +} + +/** @} */