/* Grafx2 - The Ultimate 256-color bitmap paint program 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 miscfileformats.c /// Formats that aren't fully saving, either because of palette restrictions or other things #include "engine.h" #include "errors.h" #include "global.h" #include "io.h" #include "libraw2crtc.h" #include "limits.h" #include "loadsave.h" #include "misc.h" #include "sdlscreen.h" #include "struct.h" #include "windows.h" //////////////////////////////////// PAL //////////////////////////////////// // void Draw_palette_preview(void) { short index; if (Pixel_load_function==Pixel_load_in_preview) for (index=0; index<256; index++) Window_rectangle(183+(index/16)*7,95+(index&15)*5,5,5,index); Update_window_area(183,95,120,80); } // -- Tester si un fichier est au format PAL -------------------------------- void Test_PAL(void) { FILE *file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier long file_size; // Taille du fichier Get_full_filename(filename, 0); File_error = 1; // Ouverture du fichier if ((file = fopen(filename, "rb"))) { // Lecture de la taille du fichier file_size = File_length_file(file); // Le fichier ne peut être au format PAL que si sa taille vaut 768 octets if (file_size == sizeof(T_Palette)) File_error = 0; else { // Sinon c'est peut être un fichier palette ASCII "Jasc" fread(filename, 1, 8, file); if (strncmp(filename,"JASC-PAL",8) == 0) { File_error = 0; } } fclose(file); } } // -- Lire un fichier au format PAL ----------------------------------------- void Load_PAL(void) { FILE *file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier //long file_size; // Taille du fichier Get_full_filename(filename,0); File_error=0; // Ouverture du fichier if ((file=fopen(filename, "rb"))) { long file_size = File_length_file(file); // Le fichier ne peut être au format PAL que si sa taille vaut 768 octets if (file_size == sizeof(T_Palette)) { T_Palette palette_64; // Init_preview(?); // Pas possible... pas d'image... // Lecture du fichier dans Main_palette if (Read_bytes(file, palette_64, sizeof(T_Palette))) { Palette_64_to_256(palette_64); memcpy(Main_palette, palette_64, sizeof(T_Palette)); Set_palette(Main_palette); Remap_fileselector(); // On dessine une preview de la palette (si chargement = preview) Draw_palette_preview(); } else File_error = 2; } else { fread(filename, 1, 8, file); if (strncmp(filename,"JASC-PAL",8) == 0) { int i, n, r, g, b; fscanf(file, "%d",&n); if(n != 100) { File_error = 2; fclose(file); return; } // Read color count fscanf(file, "%d",&n); for (i = 0; i < n; i++) { fscanf(file, "%d %d %d",&r, &g, &b); Main_palette[i].R = r; Main_palette[i].G = g; Main_palette[i].B = b; Set_palette(Main_palette); Remap_fileselector(); // On dessine une preview de la palette (si chargement = preview) Draw_palette_preview(); } } else File_error = 2; } // Fermeture du fichier fclose(file); } else // Si on n'a pas réussi à ouvrir le fichier, alors il y a eu une erreur File_error=1; } // -- Sauver un fichier au format PAL --------------------------------------- void Save_PAL(void) { FILE *file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier //long file_size; // Taille du fichier Get_full_filename(filename,0); File_error=0; // Ouverture du fichier if ((file=fopen(filename,"w"))) { int i; fputs("JASC-PAL\n0100\n256\n", file); for (i = 0; i < 256; i++) fprintf(file,"%d %d %d\n",Main_palette[i].R, Main_palette[i].G, Main_palette[i].B); } else // Si on n'a pas réussi à ouvrir le fichier, alors il y a eu une erreur { File_error=1; fclose(file); remove(filename); // On se fout du résultat de l'opération car si ça // renvoie 0 c'est que le fichier avait été partiel- // -lement écrit, sinon pas du tout. Or dans tous les // cas ça revient au même pour nous: Sauvegarde ratée! } } //////////////////////////////////// PKM //////////////////////////////////// #pragma pack(1) typedef struct { char Ident[3]; // Chaîne "PKM" } byte Method; // Compression method // 0 = compression en ligne (c)KM // autres = inconnues pour le moment byte recog1; // Octet de reconnaissance sur 1 octet } byte recog2; // Octet de reconnaissance sur 2 octets } word Width; // width de l'image word Height; // height de l'image T_Palette Palette; // Palette RVB 256*3 word Jump; // Taille du saut entre le header et l'image: // On va s'en servir pour rajouter un commentaire } T_PKM_Header; #pragma pack() // -- Tester si un fichier est au format PKM -------------------------------- void Test_PKM(void) { FILE *file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier T_PKM_Header header; Get_full_filename(filename,0); File_error=1; // Ouverture du fichier if ((file=fopen(filename, "rb"))) { // Lecture du header du fichier if (Read_bytes(file,&header,sizeof(T_PKM_Header))) { // On regarde s'il y a la signature PKM suivie de la méthode 0. // La constante "PKM" étant un chaîne, elle se termine toujours par 0. // Donc pas la peine de s'emm...er à regarder si la méthode est à 0. if ( (!memcmp(&header,"PKM",4)) && header.Width && header.Height) File_error=0; } fclose(file); } } // -- Lire un fichier au format PKM ----------------------------------------- void Load_PKM(void) { FILE *file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier T_PKM_Header header; byte color; byte temp_byte; word len; word index; dword Compteur_de_pixels; dword Compteur_de_donnees_packees; dword image_size; dword Taille_pack; long file_size; Get_full_filename(filename,0); File_error=0; if ((file=fopen(filename, "rb"))) { file_size=File_length_file(file); if (Read_bytes(file,&header,sizeof(T_PKM_Header))) { #if SDL_BYTEORDER == SDL_BIG_ENDIAN header.Width = SDL_Swap16(header.Width); header.Height = SDL_Swap16(header.Height); header.Jump = SDL_Swap16(header.Jump); #endif Main_comment[0]='\0'; // On efface le commentaire if (header.Jump) { index=0; while ( (indexCOMMENT_SIZE) { color=temp_byte; // On se sert de color comme temp_byte=COMMENT_SIZE; // variable temporaire color-=COMMENT_SIZE; } else color=0; if (Read_bytes(file,Main_comment,temp_byte)) { index+=temp_byte; Main_comment[temp_byte]='\0'; if (color) if (fseek(file,color,SEEK_CUR)) File_error=2; } else File_error=2; } else File_error=2; break; case 1 : // Dimensions de l'écran d'origine if (Read_byte(file,&temp_byte)) { if (temp_byte==4) { index+=4; if ( ! Read_word_le(file,(word *) &Original_screen_X) || !Read_word_le(file,(word *) &Original_screen_Y) ) File_error=2; } else File_error=2; } else File_error=2; break; case 2 : // color de transparence if (Read_byte(file,&temp_byte)) { if (temp_byte==1) { index++; if (! Read_byte(file,&Back_color)) File_error=2; } else File_error=2; } else File_error=2; break; default: if (Read_byte(file,&temp_byte)) { index+=temp_byte; if (fseek(file,temp_byte,SEEK_CUR)) File_error=2; } else File_error=2; } } else File_error=2; } if ( (!File_error) && (index!=header.Jump) ) File_error=2; } /*Init_lecture();*/ if (!File_error) { Init_preview(header.Width,header.Height,file_size,FORMAT_PKM,PIXEL_SIMPLE); if (File_error==0) { Main_image_width=header.Width; Main_image_height=header.Height; image_size=(dword)(Main_image_width*Main_image_height); // Palette lue en 64 memcpy(Main_palette,header.Palette,sizeof(T_Palette)); Palette_64_to_256(Main_palette); Set_palette(Main_palette); Remap_fileselector(); Compteur_de_donnees_packees=0; Compteur_de_pixels=0; Taille_pack=(file_size)-sizeof(T_PKM_Header)-header.Jump; // Boucle de décompression: while ( (Compteur_de_pixels>8); Write_one_byte(file,Screen_height&0xFF); Write_one_byte(file,Screen_height>>8); // Ecriture de la back-color Write_one_byte(file,2); Write_one_byte(file,1); Write_one_byte(file,Back_color); // Routine de compression PKM de l'image image_size=(dword)(Main_image_width*Main_image_height); Compteur_de_pixels=0; pixel_value=Read_pixel_function(0,0); while ( (Compteur_de_pixels=image_size) break; pixel_value=Read_pixel_function(Compteur_de_pixels % Main_image_width,Compteur_de_pixels / Main_image_width); } if ( (last_color!=header.recog1) && (last_color!=header.recog2) ) { if (repetitions==1) Write_one_byte(file,last_color); else if (repetitions==2) { Write_one_byte(file,last_color); Write_one_byte(file,last_color); } else if ( (repetitions>2) && (repetitions<256) ) { // RECON1/couleur/nombre Write_one_byte(file,header.recog1); Write_one_byte(file,last_color); Write_one_byte(file,repetitions&0xFF); } else if (repetitions>=256) { // RECON2/couleur/hi(nombre)/lo(nombre) Write_one_byte(file,header.recog2); Write_one_byte(file,last_color); Write_one_byte(file,repetitions>>8); Write_one_byte(file,repetitions&0xFF); } } else { if (repetitions<256) { Write_one_byte(file,header.recog1); Write_one_byte(file,last_color); Write_one_byte(file,repetitions&0xFF); } else { Write_one_byte(file,header.recog2); Write_one_byte(file,last_color); Write_one_byte(file,repetitions>>8); Write_one_byte(file,repetitions&0xFF); } } } End_write(file); } else File_error=1; fclose(file); } else { File_error=1; fclose(file); } // S'il y a eu une erreur de sauvegarde, on ne va tout de même pas laisser // ce fichier pourri traîner... Ca fait pas propre. if (File_error) remove(filename); } //////////////////////////////////// CEL //////////////////////////////////// #pragma pack(1) typedef struct { word Width; // width de l'image word Height; // height de l'image } T_CEL_Header1; typedef struct { byte Signature[4]; // Signature du format byte Kind; // Type de fichier ($10=PALette $20=BitMaP) byte Nb_bits; // Nombre de bits word Filler1; // ??? word Width; // width de l'image word Height; // height de l'image word X_offset; // Offset en X de l'image word Y_offset; // Offset en Y de l'image byte Filler2[16]; // ??? } T_CEL_Header2; #pragma pack() // -- Tester si un fichier est au format CEL -------------------------------- void Test_CEL(void) { char filename[MAX_PATH_CHARACTERS]; int size; FILE *file; T_CEL_Header1 header1; T_CEL_Header2 header2; int file_size; File_error=0; Get_full_filename(filename,0); file_size=File_length(filename); if (file_size==0) { File_error = 1; // Si on ne peut pas faire de stat il vaut mieux laisser tomber return; } if (! (file=fopen(filename, "rb"))) { File_error = 1; return; } if (Read_word_le(file,&header1.Width) && Read_word_le(file,&header1.Height) ) { // Vu que ce header n'a pas de signature, il va falloir tester la // cohérence de la dimension de l'image avec celle du fichier. size=file_size-sizeof(T_CEL_Header1); if ( (!size) || ( (((header1.Width+1)>>1)*header1.Height)!=size ) ) { // Tentative de reconnaissance de la signature des nouveaux fichiers fseek(file,0,SEEK_SET); if (Read_bytes(file,&header2.Signature,4) && !memcmp(header2.Signature,"KiSS",4) && Read_byte(file,&header2.Kind) && (header2.Kind==0x20) && Read_byte(file,&header2.Nb_bits) && Read_word_le(file,&header2.Filler1) && Read_word_le(file,&header2.Width) && Read_word_le(file,&header2.Height) && Read_word_le(file,&header2.X_offset) && Read_word_le(file,&header2.Y_offset) && Read_bytes(file,&header2.Filler2,16)) { // ok } else File_error=1; } else File_error=1; } else { File_error=1; } fclose(file); } // -- Lire un fichier au format CEL ----------------------------------------- void Load_CEL(void) { char filename[MAX_PATH_CHARACTERS]; FILE *file; T_CEL_Header1 header1; T_CEL_Header2 header2; short x_pos; short y_pos; byte last_byte=0; long file_size; File_error=0; Get_full_filename(filename,0); if ((file=fopen(filename, "rb"))) { if (Read_bytes(file,&header1,sizeof(T_CEL_Header1))) { file_size=File_length_file(file); if ( (file_size>(long int)sizeof(T_CEL_Header1)) && ( (((header1.Width+1)>>1)*header1.Height)==(file_size-(long int)sizeof(T_CEL_Header1)) ) ) { // Chargement d'un fichier CEL sans signature (vieux fichiers) Main_image_width=header1.Width; Main_image_height=header1.Height; Original_screen_X=Main_image_width; Original_screen_Y=Main_image_height; Init_preview(Main_image_width,Main_image_height,file_size,FORMAT_CEL,PIXEL_SIMPLE); if (File_error==0) { // Chargement de l'image /*Init_lecture();*/ for (y_pos=0;((y_pos> 4)); } else Pixel_load_function(x_pos,y_pos,(last_byte & 15)); /*Close_lecture();*/ } } else { // On réessaye avec le nouveau format fseek(file,0,SEEK_SET); if (Read_bytes(file,&header2,sizeof(T_CEL_Header2))) { // Chargement d'un fichier CEL avec signature (nouveaux fichiers) Main_image_width=header2.Width+header2.X_offset; Main_image_height=header2.Height+header2.Y_offset; Original_screen_X=Main_image_width; Original_screen_Y=Main_image_height; Init_preview(Main_image_width,Main_image_height,file_size,FORMAT_CEL,PIXEL_SIMPLE); if (File_error==0) { // Chargement de l'image /*Init_lecture();*/ if (!File_error) { // Effacement du décalage for (y_pos=0;y_pos> 4)); } else Pixel_load_function(x_pos+header2.X_offset,y_pos+header2.Y_offset,(last_byte & 15)); break; case 8: for (y_pos=0;((y_pos16 sont utilisées dans l'image for (x_pos=16;((x_pos<256) && (!Utilisation[x_pos]));x_pos++); if (x_pos==256) { // Cas d'une image 16 couleurs (écriture à l'ancien format) header1.Width =Main_image_width; header1.Height=Main_image_height; if (Write_bytes(file,&header1,sizeof(T_CEL_Header1))) { // Sauvegarde de l'image Init_write_buffer(); for (y_pos=0;((y_pos>4)!=0) File_error=1; } else { if (Read_bytes(file,&header2,sizeof(T_CEL_Header2))) { if (memcmp(header2.Signature,"KiSS",4)==0) { if (header2.Kind!=0x10) File_error=1; } else File_error=1; } else File_error=1; } fclose(file); } else File_error=1; } // -- Lire un fichier au format KCF ----------------------------------------- void Load_KCF(void) { char filename[MAX_PATH_CHARACTERS]; FILE *file; T_KCF_Header buffer; T_CEL_Header2 header2; byte bytes[3]; int pal_index; int color_index; int index; long file_size; File_error=0; Get_full_filename(filename,0); if ((file=fopen(filename, "rb"))) { file_size=File_length_file(file); if (file_size==sizeof(T_KCF_Header)) { // Fichier KCF à l'ancien format if (Read_bytes(file,&buffer,sizeof(T_KCF_Header))) { // Init_preview(?); // Pas possible... pas d'image... if (Config.Clear_palette) memset(Main_palette,0,sizeof(T_Palette)); // Chargement de la palette for (pal_index=0;pal_index<10;pal_index++) for (color_index=0;color_index<16;color_index++) { index=16+(pal_index*16)+color_index; Main_palette[index].R=((buffer.Palette[pal_index].color[color_index].Byte1 >> 4) << 4); Main_palette[index].B=((buffer.Palette[pal_index].color[color_index].Byte1 & 15) << 4); Main_palette[index].G=((buffer.Palette[pal_index].color[color_index].Byte2 & 15) << 4); } for (index=0;index<16;index++) { Main_palette[index].R=Main_palette[index+16].R; Main_palette[index].G=Main_palette[index+16].G; Main_palette[index].B=Main_palette[index+16].B; } Set_palette(Main_palette); Remap_fileselector(); } else File_error=1; } else { // Fichier KCF au nouveau format if (Read_bytes(file,&header2,sizeof(T_CEL_Header2))) { // Init_preview(?); // Pas possible... pas d'image... index=(header2.Nb_bits==12)?16:0; for (pal_index=0;pal_index> 4) << 4; Main_palette[index].B=(bytes[0] & 15) << 4; Main_palette[index].G=(bytes[1] & 15) << 4; break; case 24: // RRRR RRRR | VVVV VVVV | BBBB BBBB Read_bytes(file,bytes,3); Main_palette[index].R=bytes[0]; Main_palette[index].G=bytes[1]; Main_palette[index].B=bytes[2]; } index++; } } if (header2.Nb_bits==12) for (index=0;index<16;index++) { Main_palette[index].R=Main_palette[index+16].R; Main_palette[index].G=Main_palette[index+16].G; Main_palette[index].B=Main_palette[index+16].B; } Set_palette(Main_palette); Remap_fileselector(); } else File_error=1; } fclose(file); } else File_error=1; if (!File_error) Draw_palette_preview(); } // -- Ecrire un fichier au format KCF --------------------------------------- void Save_KCF(void) { char filename[MAX_PATH_CHARACTERS]; FILE *file; T_KCF_Header buffer; T_CEL_Header2 header2; byte bytes[3]; int pal_index; int color_index; int index; dword Utilisation[256]; // Table d'utilisation de couleurs // On commence par compter l'utilisation de chaque couleurs Count_used_colors(Utilisation); File_error=0; Get_full_filename(filename,0); if ((file=fopen(filename,"wb"))) { // Sauvegarde de la palette // On regarde si des couleurs >16 sont utilisées dans l'image for (index=16;((index<256) && (!Utilisation[index]));index++); if (index==256) { // Cas d'une image 16 couleurs (écriture à l'ancien format) for (pal_index=0;pal_index<10;pal_index++) for (color_index=0;color_index<16;color_index++) { index=16+(pal_index*16)+color_index; buffer.Palette[pal_index].color[color_index].Byte1=((Main_palette[index].R>>4)<<4) | (Main_palette[index].B>>4); buffer.Palette[pal_index].color[color_index].Byte2=Main_palette[index].G>>4; } if (! Write_bytes(file,&buffer,sizeof(T_KCF_Header))) File_error=1; } else { // Cas d'une image 256 couleurs (écriture au nouveau format) memcpy(header2.Signature,"KiSS",4); // Initialisation de la signature header2.Kind=0x10; // Initialisation du type (PALette) header2.Nb_bits=24; // Initialisation du nombre de bits header2.Filler1=0; // Initialisation du filler 1 (?) header2.Width=256; // Initialisation du nombre de couleurs header2.Height=1; // Initialisation du nombre de palettes header2.X_offset=0; // Initialisation du décalage X header2.Y_offset=0; // Initialisation du décalage Y for (index=0;index<16;index++) // Initialisation du filler 2 (?) header2.Filler2[index]=0; if (! Write_bytes(file,&header2,sizeof(T_CEL_Header2))) File_error=1; for (index=0;(index<256) && (!File_error);index++) { bytes[0]=Main_palette[index].R; bytes[1]=Main_palette[index].G; bytes[2]=Main_palette[index].B; if (! Write_bytes(file,bytes,3)) File_error=1; } } fclose(file); if (File_error) remove(filename); } else File_error=1; } //////////////////////////////////// PI1 //////////////////////////////////// //// DECODAGE d'une partie d'IMAGE //// void PI1_8b_to_16p(byte * src,byte * dest) { int i; // index du pixel à calculer word byte_mask; // Masque de decodage word w0,w1,w2,w3; // Les 4 words bien ordonnés de la source byte_mask=0x8000; w0=(((word)src[0])<<8) | src[1]; w1=(((word)src[2])<<8) | src[3]; w2=(((word)src[4])<<8) | src[5]; w3=(((word)src[6])<<8) | src[7]; for (i=0;i<16;i++) { // Pour décoder le pixel n°i, il faut traiter les 4 words sur leur bit // correspondant à celui du masque dest[i]=((w0 & byte_mask)?0x01:0x00) | ((w1 & byte_mask)?0x02:0x00) | ((w2 & byte_mask)?0x04:0x00) | ((w3 & byte_mask)?0x08:0x00); byte_mask>>=1; } } //// CODAGE d'une partie d'IMAGE //// void PI1_16p_to_8b(byte * src,byte * dest) { int i; // index du pixel à calculer word byte_mask; // Masque de codage word w0,w1,w2,w3; // Les 4 words bien ordonnés de la destination byte_mask=0x8000; w0=w1=w2=w3=0; for (i=0;i<16;i++) { // Pour coder le pixel n°i, il faut modifier les 4 words sur leur bit // correspondant à celui du masque w0|=(src[i] & 0x01)?byte_mask:0x00; w1|=(src[i] & 0x02)?byte_mask:0x00; w2|=(src[i] & 0x04)?byte_mask:0x00; w3|=(src[i] & 0x08)?byte_mask:0x00; byte_mask>>=1; } dest[0]=w0 >> 8; dest[1]=w0 & 0x00FF; dest[2]=w1 >> 8; dest[3]=w1 & 0x00FF; dest[4]=w2 >> 8; dest[5]=w2 & 0x00FF; dest[6]=w3 >> 8; dest[7]=w3 & 0x00FF; } //// DECODAGE de la PALETTE //// void PI1_decode_palette(byte * src,byte * palette) { int i; // Numéro de la couleur traitée int ip; // index dans la palette word w; // Word contenant le code // Schéma d'un word = // // Low High // VVVV RRRR | 0000 BBBB // 0321 0321 | 0321 ip=0; for (i=0;i<16;i++) { w=((word)src[(i*2)+1]<<8) | src[(i*2)+0]; // Traitement des couleurs rouge, verte et bleue: palette[ip++]=(((w & 0x0007) << 1) | ((w & 0x0008) >> 3)) << 4; palette[ip++]=(((w & 0x7000) >> 11) | ((w & 0x8000) >> 15)) << 4; palette[ip++]=(((w & 0x0700) >> 7) | ((w & 0x0800) >> 11)) << 4; } } //// CODAGE de la PALETTE //// void PI1_code_palette(byte * palette,byte * dest) { int i; // Numéro de la couleur traitée int ip; // index dans la palette word w; // Word contenant le code // Schéma d'un word = // // Low High // VVVV RRRR | 0000 BBBB // 0321 0321 | 0321 ip=0; for (i=0;i<16;i++) { // Traitement des couleurs rouge, verte et bleue: w =(((word)(palette[ip]>>2) & 0x38) >> 3) | (((word)(palette[ip]>>2) & 0x04) << 1); ip++; w|=(((word)(palette[ip]>>2) & 0x38) << 9) | (((word)(palette[ip]>>2) & 0x04) << 13); ip++; w|=(((word)(palette[ip]>>2) & 0x38) << 5) | (((word)(palette[ip]>>2) & 0x04) << 9); ip++; dest[(i*2)+0]=w & 0x00FF; dest[(i*2)+1]=(w>>8); } } // -- Tester si un fichier est au format PI1 -------------------------------- void Test_PI1(void) { FILE * file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier int size; // Taille du fichier word resolution; // Résolution de l'image Get_full_filename(filename,0); File_error=1; // Ouverture du fichier if ((file=fopen(filename, "rb"))) { // Vérification de la taille size=File_length_file(file); if ((size==32034) || (size==32066)) { // Lecture et vérification de la résolution if (Read_word_le(file,&resolution)) { if (resolution==0x0000) File_error=0; } } // Fermeture du fichier fclose(file); } } // -- Lire un fichier au format PI1 ----------------------------------------- void Load_PI1(void) { char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier FILE *file; word x_pos,y_pos; byte * buffer; byte * ptr; byte pixels[320]; Get_full_filename(filename,0); File_error=0; if ((file=fopen(filename, "rb"))) { // allocation d'un buffer mémoire buffer=(byte *)malloc(32034); if (buffer!=NULL) { // Lecture du fichier dans le buffer if (Read_bytes(file,buffer,32034)) { // Initialisation de la preview Init_preview(320,200,File_length_file(file),FORMAT_PI1,PIXEL_SIMPLE); if (File_error==0) { // Initialisation de la palette if (Config.Clear_palette) memset(Main_palette,0,sizeof(T_Palette)); PI1_decode_palette(buffer+2,(byte *)Main_palette); Set_palette(Main_palette); Remap_fileselector(); Main_image_width=320; Main_image_height=200; // Chargement/décompression de l'image ptr=buffer+34; for (y_pos=0;y_pos<200;y_pos++) { for (x_pos=0;x_pos<(320>>4);x_pos++) { PI1_8b_to_16p(ptr,pixels+(x_pos<<4)); ptr+=8; } for (x_pos=0;x_pos<320;x_pos++) Pixel_load_function(x_pos,y_pos,pixels[x_pos]); } } } else File_error=1; free(buffer); } else File_error=1; fclose(file); } else File_error=1; } // -- Sauver un fichier au format PI1 --------------------------------------- void Save_PI1(void) { char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier FILE *file; short x_pos,y_pos; byte * buffer; byte * ptr; byte pixels[320]; Get_full_filename(filename,0); File_error=0; // Ouverture du fichier if ((file=fopen(filename,"wb"))) { // allocation d'un buffer mémoire buffer=(byte *)malloc(32066); // Codage de la résolution buffer[0]=0x00; buffer[1]=0x00; // Codage de la palette PI1_code_palette((byte *)Main_palette,buffer+2); // Codage de l'image ptr=buffer+34; for (y_pos=0;y_pos<200;y_pos++) { // Codage de la ligne memset(pixels,0,320); if (y_pos>4);x_pos++) { PI1_16p_to_8b(pixels+(x_pos<<4),ptr); ptr+=8; } } memset(buffer+32034,0,32); // 32 extra NULL bytes at the end of the file to make ST Deluxe Paint happy if (Write_bytes(file,buffer,32066)) { fclose(file); } else // Error d'écriture (disque plein ou protégé) { fclose(file); remove(filename); File_error=1; } // Libération du buffer mémoire free(buffer); } else { fclose(file); remove(filename); File_error=1; } } //////////////////////////////////// PC1 //////////////////////////////////// //// DECOMPRESSION d'un buffer selon la méthode PACKBITS //// void PC1_uncompress_packbits(byte * src,byte * dest) { int is,id; // Les indices de parcour des buffers int n; // Octet de contrôle for (is=id=0;id<32000;) { n=src[is++]; if (n & 0x80) { // Recopier src[is] -n+1 fois n=257-n; for (;(n>0) && (id<32000);n--) dest[id++]=src[is]; is++; } else { // Recopier n+1 octets littéralement n=n+1; for (;(n>0) && (id<32000);n--) dest[id++]=src[is++]; } // Contrôle des erreurs if (n>0) File_error=1; } } //// COMPRESSION d'un buffer selon la méthode PACKBITS //// void PC1_compress_packbits(byte * src,byte * dest,int source_size,int * dest_size) { int is; // index dans la source int id; // index dans la destination int ir; // index de la répétition int n; // Taille des séquences int repet; // "Il y a répétition" for (is=id=0;is0;n--) dest[id++]=src[is++]; } // On code la partie sans répétitions if (repet) { // On compte la quantité de fois qu'il faut répéter la valeur for (ir+=3;ir>=1; } } } //// CODAGE d'une partie d'IMAGE //// // Transformation d'1 ligne de pixels en 4 plans de bits void PC1_1line_to_4bp(byte * src,byte * dst0,byte * dst1,byte * dst2,byte * dst3) { int i,j; // Compteurs int ip; // index du pixel à calculer byte byte_mask; // Masque de decodage byte b0,b1,b2,b3; // Les 4 octets des plans bits sources ip=0; // Pour chacun des 40 octets des plans de bits for (i=0;i<40;i++) { // Pour chacun des 8 bits des octets byte_mask=0x80; b0=b1=b2=b3=0; for (j=0;j<8;j++) { b0|=(src[ip] & 0x01)?byte_mask:0x00; b1|=(src[ip] & 0x02)?byte_mask:0x00; b2|=(src[ip] & 0x04)?byte_mask:0x00; b3|=(src[ip] & 0x08)?byte_mask:0x00; ip++; byte_mask>>=1; } dst0[i]=b0; dst1[i]=b1; dst2[i]=b2; dst3[i]=b3; } } // -- Tester si un fichier est au format PC1 -------------------------------- void Test_PC1(void) { FILE *file; // Fichier du fichier char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier int size; // Taille du fichier word resolution; // Résolution de l'image Get_full_filename(filename,0); File_error=1; // Ouverture du fichier if ((file=fopen(filename, "rb"))) { // Vérification de la taille size=File_length_file(file); if ((size<=32066)) { // Lecture et vérification de la résolution if (Read_word_le(file,&resolution)) { if (resolution==0x0080) File_error=0; } } // Fermeture du fichier fclose(file); } } // -- Lire un fichier au format PC1 ----------------------------------------- void Load_PC1(void) { char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier FILE *file; int size; word x_pos,y_pos; byte * buffercomp; byte * bufferdecomp; byte * ptr; byte pixels[320]; Get_full_filename(filename,0); File_error=0; if ((file=fopen(filename, "rb"))) { size=File_length_file(file); // allocation des buffers mémoire buffercomp=(byte *)malloc(size); bufferdecomp=(byte *)malloc(32000); if ( (buffercomp!=NULL) && (bufferdecomp!=NULL) ) { // Lecture du fichier dans le buffer if (Read_bytes(file,buffercomp,size)) { // Initialisation de la preview Init_preview(320,200,File_length_file(file),FORMAT_PC1,PIXEL_SIMPLE); if (File_error==0) { // Initialisation de la palette if (Config.Clear_palette) memset(Main_palette,0,sizeof(T_Palette)); PI1_decode_palette(buffercomp+2,(byte *)Main_palette); Set_palette(Main_palette); Remap_fileselector(); Main_image_width=320; Main_image_height=200; // Décompression du buffer PC1_uncompress_packbits(buffercomp+34,bufferdecomp); // Décodage de l'image ptr=bufferdecomp; for (y_pos=0;y_pos<200;y_pos++) { // Décodage de la scanline PC1_4bp_to_1line(ptr,ptr+40,ptr+80,ptr+120,pixels); ptr+=160; // Chargement de la ligne for (x_pos=0;x_pos<320;x_pos++) Pixel_load_function(x_pos,y_pos,pixels[x_pos]); } } } else File_error=1; free(bufferdecomp); free(buffercomp); } else { File_error=1; free(bufferdecomp); free(buffercomp); } fclose(file); } else File_error=1; } // -- Sauver un fichier au format PC1 --------------------------------------- void Save_PC1(void) { char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier FILE *file; int size; short x_pos,y_pos; byte * buffercomp; byte * bufferdecomp; byte * ptr; byte pixels[320]; Get_full_filename(filename,0); File_error=0; // Ouverture du fichier if ((file=fopen(filename,"wb"))) { // Allocation des buffers mémoire bufferdecomp=(byte *)malloc(32000); buffercomp =(byte *)malloc(64066); // Codage de la résolution buffercomp[0]=0x80; buffercomp[1]=0x00; // Codage de la palette PI1_code_palette((byte *)Main_palette,buffercomp+2); // Codage de l'image ptr=bufferdecomp; for (y_pos=0;y_pos<200;y_pos++) { // Codage de la ligne memset(pixels,0,320); if (y_pos>4);x_pos++) { PI1_8b_to_16p(ptr,pixels+(x_pos<<4)); ptr+=8; } for (x_pos=0;x_pos<320;x_pos++) Pixel_load_function(x_pos,y_pos,pixels[x_pos]); } } } else File_error=1; free(buffer); } else File_error=1; fclose(file); } else File_error=1; } void Save_NEO(void) { char filename[MAX_PATH_CHARACTERS]; // Nom complet du fichier FILE *file; short x_pos,y_pos; byte * buffer; byte * ptr; byte pixels[320]; Get_full_filename(filename,0); File_error=0; // Ouverture du fichier if ((file=fopen(filename,"wb"))) { // allocation d'un buffer mémoire buffer=(byte *)malloc(32128); // Codage de la résolution buffer[0]=0x00; buffer[1]=0x00; buffer[2]=0x00; buffer[3]=0x00; // Codage de la palette PI1_code_palette((byte *)Main_palette,buffer+4); // Codage de l'image ptr=buffer+128; for (y_pos=0;y_pos<200;y_pos++) { // Codage de la ligne memset(pixels,0,320); if (y_pos>4);x_pos++) { PI1_16p_to_8b(pixels+(x_pos<<4),ptr); ptr+=8; } } if (Write_bytes(file,buffer,32128)) { fclose(file); } else // Error d'écriture (disque plein ou protégé) { fclose(file); remove(filename); File_error=1; } // Libération du buffer mémoire free(buffer); } else { fclose(file); remove(filename); File_error=1; } } //////////////////////////////////// C64 //////////////////////////////////// void Test_C64(void) { FILE* file; char filename[MAX_PATH_CHARACTERS]; long file_size; Get_full_filename(filename,0); file = fopen(filename,"rb"); if (file) { file_size = File_length_file(file); switch (file_size) { case 1000: // screen or color case 1002: // (screen or color) + loadaddr case 8000: // raw bitmap case 8002: // raw bitmap with loadaddr case 9000: // bitmap + screen case 9002: // bitmap + screen + loadaddr case 10001: // multicolor case 10003: // multicolor + loadaddr File_error = 0; break; default: // then we don't know for now. File_error = 1; } fclose (file); } else { File_error = 1; } } void Load_C64_hires(byte *bitmap, byte *colors) { int cx,cy,x,y,c[4],pixel,color; for(cy=0; cy<25; cy++) { for(cx=0; cx<40; cx++) { c[1]=colors[cy*40+cx]&15; c[0]=colors[cy*40+cx]>>4; for(y=0; y<8; y++) { pixel=bitmap[cy*320+cx*8+y]; for(x=0; x<8; x++) { color=c[pixel&(1<<(7-x))?1:0]; Pixel_load_function(cx*8+x,cy*8+y,color); } } } } } void Load_C64_multi(byte *bitmap, byte *colors, byte *nybble, byte background) { int cx,cy,x,y,c[4],pixel,color; c[0]=background; for(cy=0; cy<25; cy++) { for(cx=0; cx<40; cx++) { c[1]=colors[cy*40+cx]>>4; c[2]=colors[cy*40+cx]&15; c[3]=nybble[cy*40+cx]; for(y=0; y<8; y++) { pixel=bitmap[cy*320+cx*8+y]; for(x=0; x<4; x++) { color=c[(pixel&3)]; pixel>>=2; Pixel_load_function(cx*4+(3-x),cy*8+y,color); } } } } } void Load_C64(void) { FILE* file; char filename[MAX_PATH_CHARACTERS]; long file_size; int i; byte background,hasLoadAddr=0; int loadFormat=0; enum c64_format {F_hires,F_multi,F_bitmap,F_screen,F_color}; const char *c64_format_names[]={"hires","multicolor","bitmap","screen","color"}; // Palette from http://www.pepto.de/projects/colorvic/ byte pal[48]={ 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x68, 0x37, 0x2B, 0x70, 0xA4, 0xB2, 0x6F, 0x3D, 0x86, 0x58, 0x8D, 0x43, 0x35, 0x28, 0x79, 0xB8, 0xC7, 0x6F, 0x6F, 0x4F, 0x25, 0x43, 0x39, 0x00, 0x9A, 0x67, 0x59, 0x44, 0x44, 0x44, 0x6C, 0x6C, 0x6C, 0x9A, 0xD2, 0x84, 0x6C, 0x5E, 0xB5, 0x95, 0x95, 0x95}; byte bitmap[8000],colors[1000],nybble[1000]; word width=320, height=200; Get_full_filename(filename,0); file = fopen(filename,"rb"); if (file) { File_error=0; file_size = File_length_file(file); switch (file_size) { case 1000: // screen or color hasLoadAddr=0; loadFormat=F_screen; break; case 1002: // (screen or color) + loadaddr hasLoadAddr=1; loadFormat=F_screen; break; case 8000: // raw bitmap hasLoadAddr=0; loadFormat=F_bitmap; case 8002: // raw bitmap with loadaddr hasLoadAddr=1; loadFormat=F_bitmap; break; case 9000: // bitmap + screen hasLoadAddr=0; loadFormat=F_hires; break; case 9002: // bitmap + screen + loadaddr hasLoadAddr=1; loadFormat=F_hires; break; case 10001: // multicolor hasLoadAddr=0; loadFormat=F_multi; break; case 10003: // multicolor + loadaddr hasLoadAddr=1; loadFormat=F_multi; break; default: // then we don't know what it is. File_error = 1; } memcpy(Main_palette,pal,48); // this set the software palette for grafx2 Set_palette(Main_palette); // this set the hardware palette for SDL Remap_fileselector(); // Always call it if you change the palette if (file_size>9002) width=160; if (hasLoadAddr) { // get load address Read_byte(file,&background); Read_byte(file,&background); sprintf(filename,"load at $%02x00",background); } else { sprintf(filename,"no addr"); } if(file_size>9002) { Ratio_of_loaded_image = PIXEL_WIDE; } sprintf(Main_comment,"C64 %s, %s", c64_format_names[loadFormat],filename); Init_preview(width, height, file_size, FORMAT_C64, Ratio_of_loaded_image); // Do this as soon as you can Main_image_width = width ; Main_image_height = height; Read_bytes(file,bitmap,8000); if (file_size>8002) Read_bytes(file,colors,1000); else { for(i=0;i<1000;i++) { colors[i]=1; } } if(width==160) { Read_bytes(file,nybble,1000); Read_byte(file,&background); Load_C64_multi(bitmap,colors,nybble,background); } else { Load_C64_hires(bitmap,colors); } File_error = 0; fclose(file); } else File_error = 1; } int Save_C64_window(byte *saveWhat, byte *loadAddr) { int button; unsigned int i; T_Dropdown_button *what, *addr; char * what_label[] = { "All", "Bitmap", "Screen", "Color" }; char * address_label[] = { "None", "$2000", "$4000", "$6000", "$8000", "$A000", "$C000", "$E000" }; Open_window(200,120,"c64 settings"); Window_set_normal_button(110,100,80,15,"Save",1,1,SDLK_RETURN); Window_set_normal_button(10,100,80,15,"Cancel",1,1,SDLK_ESCAPE); Print_in_window(13,18,"Data:",MC_Dark,MC_Light); what=Window_set_dropdown_button(10,28,90,15,70,what_label[*saveWhat],1, 0, 1, LEFT_SIDE); Window_dropdown_clear_items(what); for (i=0; i2) { Warning_message("More than 2 colors in 8x8 pixels"); // TODO here we should hilite the offending block printf("\nerror at %dx%d (%d colors)\n",cx*8,cy*8,numcolors); return 1; } for(i=0;i<16;i++) { if(cusage[i]) { c2=i; break; } } c1=c2; for(i=c2+1;i<16;i++) { if(cusage[i]) { c1=i; } } colors[cx+cy*40]=(c2<<4)|c1; for(y=0; y<8; y++) { bits=0; for(x=0; x<8; x++) { pixel=Read_pixel_function(x+cx*8,y+cy*8); if(pixel>15) { Warning_message("Color above 15 used"); // TODO hilite offending block here too? // or make it smarter with color allocation? // However, the palette is fixed to the 16 first colors return 1; } bits=bits<<1; if (pixel==c1) bits|=1; } bitmap[pos++]=bits; //Write_byte(file,bits&255); } } } file = fopen(filename,"wb"); if(!file) { Warning_message("File open failed"); File_error = 1; return 1; } if (loadAddr) { Write_byte(file,0); Write_byte(file,loadAddr); } if (saveWhat==0 || saveWhat==1) Write_bytes(file,bitmap,8000); if (saveWhat==0 || saveWhat==2) Write_bytes(file,colors,1000); fclose(file); return 0; } int Save_C64_multi(char *filename, byte saveWhat, byte loadAddr) { /* BITS COLOR INFORMATION COMES FROM 00 Background color #0 (screen color) 01 Upper 4 bits of screen memory 10 Lower 4 bits of screen memory 11 Color nybble (nybble = 1/2 byte = 4 bits) */ int cx,cy,x,y,c[4]={0,0,0,0},color,lut[16],bits,pixel,pos=0; byte bitmap[8000],screen[1000],nybble[1000]; word numcolors,count; dword cusage[256]; byte i,background=0; FILE *file; numcolors=Count_used_colors(cusage); count=0; for(x=0;x<16;x++) { //printf("color %d, pixels %d\n",x,cusage[x]); if(cusage[x]>count) { count=cusage[x]; background=x; } } for(cy=0; cy<25; cy++) { //printf("\ny:%2d ",cy); for(cx=0; cx<40; cx++) { numcolors=Count_used_colors_area(cusage,cx*4,cy*8,4,8); if(numcolors>4) { Warning_message("More than 4 colors in 4x8"); // TODO hilite offending block return 1; } color=1; c[0]=background; for(i=0; i<16; i++) { lut[i]=0; if(cusage[i]) { if(i!=background) { lut[i]=color; c[color]=i; color++; } else { lut[i]=0; } } } // add to screen and nybble screen[cx+cy*40]=c[1]<<4|c[2]; nybble[cx+cy*40]=c[3]; //printf("%x%x%x ",c[1],c[2],c[3]); for(y=0;y<8;y++) { bits=0; for(x=0;x<4;x++) { pixel=Read_pixel_function(cx*4+x,cy*8+y); if(pixel>15) { Warning_message("Color above 15 used"); // TODO hilite as in hires, you should stay to // the fixed 16 color palette return 1; } bits=bits<<2; bits|=lut[pixel]; } //Write_byte(file,bits&255); bitmap[pos++]=bits; } } } file = fopen(filename,"wb"); if(!file) { Warning_message("File open failed"); File_error = 1; return 1; } if (loadAddr) { Write_byte(file,0); Write_byte(file,loadAddr); } if (saveWhat==0 || saveWhat==1) Write_bytes(file,bitmap,8000); if (saveWhat==0 || saveWhat==2) Write_bytes(file,screen,1000); if (saveWhat==0 || saveWhat==3) Write_bytes(file,nybble,1000); if (saveWhat==0) Write_byte(file,background); fclose(file); //printf("\nbg:%d\n",background); return 0; } void Save_C64(void) { char filename[MAX_PATH_CHARACTERS]; static byte saveWhat=0, loadAddr=0; dword numcolors,cusage[256]; numcolors=Count_used_colors(cusage); Get_full_filename(filename,0); if (numcolors>16) { Warning_message("Error: Max 16 colors"); File_error = 1; return; } if (((Main_image_width!=320) && (Main_image_width!=160)) || Main_image_height!=200) { Warning_message("must be 320x200 or 160x200"); File_error = 1; return; } if(!Save_C64_window(&saveWhat,&loadAddr)) { File_error = 1; return; } //printf("saveWhat=%d, loadAddr=%d\n",saveWhat,loadAddr); if (Main_image_width==320) File_error = Save_C64_hires(filename,saveWhat,loadAddr); else File_error = Save_C64_multi(filename,saveWhat,loadAddr); } // SCR (Amstrad CPC) void Save_SCR(void) { unsigned char* output; unsigned long outsize; unsigned char r1; int cpc_mode; FILE* file; char filename[MAX_PATH_CHARACTERS]; long file_size; Get_full_filename(filename,0); switch(Pixel_ratio) { case PIXEL_WIDE: case PIXEL_WIDE2: cpc_mode = 0; break; case PIXEL_TALL: case PIXEL_TALL2: cpc_mode = 2; break; default: cpc_mode = 1; break; } output = raw2crtc(Main_image_width,Main_image_height,cpc_mode,7,&outsize,&r1,0,0); file = fopen(filename,"wb"); Write_bytes(file, output, outsize); fclose(file); File_error = 0; }