\r\n -> \n
[gnokii.git] / common / files / gsm-filetypes.c
1 /*
2
3   G N O K I I
4
5   A Linux/Unix toolset and driver for Nokia mobile phones.
6
7   Copyright (C) 1999, 2000 Hugh Blemings & Pavel Janík ml. 
8
9   Released under the terms of the GNU GPL, see file COPYING for more details.
10         
11   Functions to read and write common file types.
12  
13   Last modified: Mon Mar 20 22:02:15 CET 2000
14   Modified by Chris Kemp
15
16 */
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21 #include <ctype.h>
22 #include <sys/stat.h>
23 #ifndef VC6
24 #include <unistd.h>
25 #endif
26
27 #include "gsm-common.h"
28 #include "gsm-ringtones.h"
29 #include "gsm-bitmaps.h"
30 #include "files/gsm-filetypes.h"
31 #include "files/midifile.h"
32 #include "gsm-coding.h"
33 #include "misc.h"
34 #include "newmodules/n7110.h"
35 #include "newmodules/n6110.h"
36
37 #ifdef XPM
38   #include <X11/xpm.h>
39 #endif
40
41 /**
42 * GetvCalTime
43 *
44 * Fills vCalendar time string into GSM_DateTime structure
45 *
46 * in:
47 *   dt:  datetime structure
48 *   time:  string in format yyyymmddThhmmss
49 * out:
50 *   <>0 if error
51 */
52 int GetvCalTime(GSM_DateTime *dt, char *time)
53 {
54   char year[5]="", month[3]="", day[3]="", hour[3]="", minute[3]="", second[3]="";
55   dt->Year=dt->Month=dt->Day=dt->Hour=dt->Minute=dt->Second=dt->Timezone=0;
56
57   strncpy(year, time, 4);
58   strncpy(month, time+4, 2);
59   strncpy(day, time+6, 2);
60   strncpy(hour, time+9, 2);
61   strncpy(minute, time+11, 2);
62   strncpy(second, time+13, 2);
63
64 /* FIXME: Should check ranges... */
65   dt->Year=atoi(year);
66   dt->Month=atoi(month);
67   dt->Day=atoi(day);
68   dt->Hour=atoi(hour);
69   dt->Minute=atoi(minute);
70   dt->Second=atoi(second);
71 /* FIXME */
72   dt->Timezone=0;
73
74   return 0;
75 }
76
77 /**
78 * FillCalendarNote
79 *
80 * Fills calendar data from strings into calendar note
81 *
82 * in:
83 *   note:  calendar note structure
84 *   type:  type of calendar note
85 *   text:  text or phonenumber
86 *   time:  string in format yyyymmddThhmmss
87 *   alarm: dito
88 * out:
89 *   <>0 if error
90 */
91 int FillCalendarNote(GSM_CalendarNote *note, char *type,
92                        char *text, char *time, char *alarm)
93 {
94   GetvCalTime(&note->Time, time);
95   GetvCalTime(&note->Alarm, alarm);
96
97   note->Location=0; 
98
99   strncpy(note->Text, text, MAX_CALENDAR_TEXT_LENGTH);
100   strcpy(note->Phone, ""); /* correct in most cases */
101
102   /* FIXME: Handle additional strings, maybe from configuration file */
103
104   if(!strcmp(type, "PHONE CALL"))
105   {
106     strncpy(note->Phone, text, MAX_CALENDAR_PHONE_LENGTH);
107     note->Type=GCN_CALL;
108   }
109   else if(!strcmp(type, "MEETING"))
110       note->Type=GCN_MEETING;
111   else if(!strcmp(type, "SPECIAL OCCASION"))
112       note->Type=GCN_BIRTHDAY;
113   else
114       note->Type=GCN_REMINDER;
115
116   return 0;
117 }
118
119 /**
120 * GSM_ReadVCalendarFile
121 *
122 * Reads vCalendar file
123 *
124 * in:
125 *   FileName: name of vCalendar file 
126 *   cnote:  pointer to calendar note
127 *   number:  number in file of calendar note to read
128 * out:
129 *   <>0 if error
130 */
131 GSM_Error GSM_ReadVCalendarFile(char *FileName, GSM_CalendarNote *cnote, int *number)
132 {
133   FILE *file;
134   char type[21]="", text[40]="", time[16]="", alarm[16]="";
135         char phone[40]="";
136         long recurr=0L;
137         char altype=0x00;
138
139   int veventcounter=0;
140   int isOK=0;
141   
142   bool NoteOK=false;
143
144   char *Line, OLine[1024], BackLine[1024];
145
146   Line = OLine;
147
148   file=fopen(FileName, "r");    
149   if (!file) {
150 #ifdef DEBUG
151     fprintf(stderr, _("File cannot be opened!\n"));
152 #endif
153     return GE_CANTOPENFILE;
154   }
155
156   /* Go through data from file. */
157   while (GetLine(file, Line, sizeof(OLine))!=-1) {
158
159     strcpy(BackLine, Line);
160
161     switch (isOK) {
162       case 0:
163         if (!strcmp(Line,"BEGIN:VCALENDAR"))
164           isOK++;
165         break;
166       case 1:
167         if (!strcmp(Line,"BEGIN:VEVENT")) {
168           isOK++;
169           veventcounter++;
170         }
171         if (!strcmp(Line,"END:VCALENDAR"))
172           isOK--;       
173         break;
174       case 2:
175         if (veventcounter==*number) {
176           if (!strncmp(Line,"CATEGORIES:",11)) {
177             strncpy(type,Line+11,strlen(Line)-11);
178             type[strlen(Line)-11]=0;
179           }
180           if (!strncmp(Line,"DESCRIPTION:",12)) {
181             strncpy(phone,Line+12,strlen(Line)-12);
182             phone[strlen(Line)-12]=0;
183           }
184           if (!strncmp(Line,"SUMMARY:",8)) {
185             strncpy(text,Line+8,strlen(Line)-8);
186             text[strlen(Line)-8]=0;
187           }
188           if (!strncmp(Line,"SUMMARY;CHARSET=UTF-8;ENCODING=QUOTED-PRINTABLE:",48)) {
189             DecodeUTF8(text,Line+48,strlen(Line)-48);
190             text[strlen(Line)-48]=0;
191           }
192           if (!strncmp(Line,"DTSTART:",8)) {
193             strncpy(time,Line+8,strlen(Line)-8);
194             time[strlen(Line)-8]=0;
195           }
196           if (!strncmp(Line,"DALARM:",7)) {
197             strncpy(alarm,Line+7,strlen(Line)-7);
198             alarm[strlen(Line)-7]=0;
199           }
200
201           /* Obsolete */
202           if (!strncmp(Line,"RECURR:",7)) {
203             recurr=mem_to_int(Line+7,strlen(Line)-7);
204           }
205
206           if (!strncmp(Line,"RRULE:D1 :",9)) {
207             recurr=1;
208           }
209           if (!strncmp(Line,"RRULE:W1 :",9)) {
210             recurr=7;
211           }
212           if (!strncmp(Line,"RRULE:W2 :",9)) {
213             recurr=14;
214           }
215           if (!strncmp(Line,"RRULE:YD1 :",10)) {
216             recurr=365;
217           }
218
219           if (!strncmp(Line,"ALTYPE:",7)) {
220             altype=(!strncmp("TONE",Line+7,4)) ? 0x00 : 0x01;
221           }
222         }
223         if (!strcmp(Line,"END:VEVENT")) {
224           if (veventcounter==*number) NoteOK=true;
225           isOK--;
226         }
227         break;
228     }
229     
230     if (NoteOK) break;
231   }
232     
233   if (!NoteOK) {
234     *number=veventcounter;
235 #ifdef DEBUG
236     fprintf(stdout,_("Note not found in VCalendarfile\n"));
237 #endif
238     return GE_TOOSHORT;
239   }
240   
241   FillCalendarNote(cnote, type, text, time, alarm);
242
243   cnote->Recurrance = recurr*24; /* it was in days. I convert in hours */
244   cnote->AlarmType = altype;
245
246   if( strcmp( phone, "" ) ) { /* Invert data if CALL ... */
247     strcpy( cnote->Text, phone );
248     strcpy( cnote->Phone, text ); // alread FillCalendar does it ..
249   }
250
251   fclose(file);
252   
253   return 0;
254 }
255
256 GSM_Error GSM_ReadBinRingtoneFile(char *FileName, GSM_BinRingtone *ringtone)
257 {
258   FILE *file;
259
260   file = fopen(FileName, "rb");
261
262   if (!file)
263     return(GE_CANTOPENFILE);
264
265 //  fseek(file,3474485,0);
266   
267   ringtone->length=fread(ringtone->frame, 1, 500, file);
268
269   fclose(file);
270   
271   if (ringtone->frame[0]!=0x00 || ringtone->frame[1]!=0x00 ||
272       ringtone->frame[2]!=0x0C || ringtone->frame[3]!=0x01)
273     return GE_NOTSUPPORTED;
274
275   return GE_NONE;
276 }
277
278 /* Function to convert scale field in to correct number. */
279 int GetRTTLDuration (char *num)
280 {
281
282 int duration=0;
283
284  switch (atoi(num)) {
285  
286  case  1: duration=128; break;
287  case  2: duration= 64; break;
288  case  4: duration= 32; break;
289  case  8: duration= 16; break;
290  case 16: duration=  8; break;
291  case 32: duration=  4; break;
292  }
293    
294  return (duration);
295
296 }
297
298 int GetRTTLScale (char *num)
299 {
300
301   /* This may well need improving. */
302
303   int scale=0;
304
305   if ((atoi(num))<4) scale=(atoi(num));
306   if ((atoi(num))>4) scale=(atoi(num))-4;
307
308   return (scale);
309 }
310
311 GSM_Error GSM_ReadRingtoneFile(char *FileName, GSM_Ringtone *ringtone)
312 {
313   FILE *file;
314   unsigned char buffer[300];
315   GSM_Error error;
316   GSM_Filetypes filetype=RTTL;
317
318   file = fopen(FileName, "rb");
319
320   if (!file)
321     return(GE_CANTOPENFILE);
322
323   fread(buffer, 1, 4, file); /* Read the header of the file. */
324
325   /* Attempt to identify filetype */
326
327   if (memcmp(buffer, "MThd",3)==0)  /* MIDI files have 'MThd' at the start */
328     filetype=MIDI;
329
330   if (buffer[0]==0xc7 && buffer[1]==0x45 && buffer[2]==0xc1 && buffer[3]==0x53)
331     filetype=COMMUNICATOR;
332
333   if (strstr(FileName,".ott")) filetype=OTT; /* OTT files saved by NCDS3 */
334   
335   error=GE_NONE;
336   
337   rewind(file);
338
339   switch (filetype) {
340   case RTTL:
341     error=loadrttl(file,ringtone);
342     fclose(file);
343     break;
344   case OTT:
345     error=loadott(file,ringtone);
346     fclose(file);
347     break;
348   case COMMUNICATOR:
349     error=loadcommunicator(file,ringtone);
350     fclose(file);
351     break;
352   case MIDI:
353     fclose(file);
354     error=loadmid(FileName,ringtone);
355     break;
356   default:
357     error=GE_INVALIDFILEFORMAT;
358   }
359
360   return(error);
361
362 }
363
364 GSM_Error loadott(FILE *file, GSM_Ringtone *ringtone)
365 {
366   char Buffer[2000];
367   int i;
368   
369   i=fread(Buffer, 1, 2000, file);
370
371   if (!feof(file)) return GE_TOOLONG;
372   
373   return GSM_UnPackRingtone(ringtone, Buffer, i);
374 }
375
376 GSM_Error loadcommunicator(FILE *file, GSM_Ringtone *ringtone)
377 {
378   char Buffer[4000];
379   int i,j;
380   
381   i=fread(Buffer, 1, 4000, file);
382
383   if (!feof(file)) return GE_TOOLONG;
384   
385   i=0;j=0;
386   while (true) {
387     if (Buffer[j]==0x00 && Buffer[j+1]==0x02 &&
388         Buffer[j+2]==0x4a && Buffer[j+3]==0x3a) break;
389     if (j==i-4) return GE_INTERNALERROR;
390     j++;
391   }
392   j++;
393   
394   return GSM_UnPackRingtone(ringtone, Buffer+j, i-j);
395 }
396
397 /* TODO: spaces should not be interpreted */
398 /* Note: ringtone have to be in one line (without 0x13 and 0x10 chars) */
399 GSM_Error loadrttl(FILE *file, GSM_Ringtone *ringtone)
400 {
401   int NrNote=0;
402   
403   u8 DefNoteScale=2, DefNoteDuration=4;
404   int DefNoteTempo=63;
405   u8 DefNoteStyle=ContinuousStyle;
406
407   unsigned char buffer[2000];
408   unsigned char *def, *notes, *ptr;
409
410   ringtone->Loop=15; //default value
411
412   fread(buffer, 2000, 1, file);
413
414   /* This is for buggy RTTTL ringtones without name. */
415   if (buffer[0] != RTTTL_SEP[0]) {
416     strtok(buffer, RTTTL_SEP);
417     sprintf(ringtone->name, "%s", buffer);
418     def=strtok(NULL, RTTTL_SEP);
419     notes=strtok(NULL, RTTTL_SEP);
420   }
421   else {
422     sprintf(ringtone->name, "GNOKII");
423     def=strtok(buffer, RTTTL_SEP);
424     notes=strtok(NULL, RTTTL_SEP);
425   }
426
427   ptr=strtok(def, ", ");
428
429   /* Parsing the <defaults> section. */
430   while (ptr) {
431
432     switch(*ptr) {
433     case 'd':
434     case 'D':
435       DefNoteDuration=GetRTTLDuration(ptr+2);
436       break;
437     case 'o':
438     case 'O':
439       DefNoteScale=GetRTTLScale(ptr+2);
440       break;
441     case 'b':
442     case 'B':
443       DefNoteTempo=atoi(ptr+2);
444       break;
445     case 'l':
446     case 'L':
447       ringtone->Loop=atoi(ptr+2);
448       break;
449     case 's':
450     case 'S':
451       switch (*(ptr+1)) {
452         case 'C':
453         case 'c':
454           DefNoteStyle=ContinuousStyle;
455           break;
456         case 'N':
457         case 'n':
458           DefNoteStyle=NaturalStyle;
459           break;
460         case 'S':
461         case 's':
462           DefNoteStyle=StaccatoStyle;
463           break;        
464       }
465       switch (*(ptr+2)) {
466         case 'c':
467         case 'C':
468           DefNoteStyle=ContinuousStyle;
469           break;
470         case 'n':
471         case 'N':
472           DefNoteStyle=NaturalStyle;
473           break;
474         case 's':
475         case 'S':
476           DefNoteStyle=StaccatoStyle;
477           break;        
478       }
479       break;
480     }
481
482     ptr=strtok(NULL,", ");
483   }
484
485 #ifdef DEBUG
486   printf("DefNoteDuration=%d\n", DefNoteDuration);
487   printf("DefNoteScale=%d\n", DefNoteScale);
488 #endif
489
490   ptr=strtok(notes, ", ");
491
492   /* Parsing the <note-command>+ section. */
493   while (ptr && NrNote<MAX_RINGTONE_NOTES) {
494
495     switch(*ptr) {
496       case 'o':
497       case 'O':
498         DefNoteScale=GetRTTLScale(ptr+2);
499         break;
500       case 's':
501       case 'S':
502         switch (*(ptr+1)) {
503           case 'C':
504           case 'c':
505             DefNoteStyle=ContinuousStyle;
506             break;
507           case 'N':
508           case 'n':
509             DefNoteStyle=NaturalStyle;
510             break;
511           case 'S':
512           case 's':
513             DefNoteStyle=StaccatoStyle;
514             break;      
515         }
516         switch (*(ptr+2)) {
517           case 'C':
518           case 'c':
519             DefNoteStyle=ContinuousStyle;
520             break;
521           case 'N':
522           case 'n':
523             DefNoteStyle=NaturalStyle;
524             break;
525           case 'S':
526           case 's':
527             DefNoteStyle=StaccatoStyle;
528             break;      
529         }
530         break;
531       default:
532         /* [<duration>] */
533         ringtone->notes[NrNote].duration=GetRTTLDuration(ptr);
534         if (ringtone->notes[NrNote].duration==0)
535            ringtone->notes[NrNote].duration=DefNoteDuration;
536         
537         /* Skip all numbers in duration specification. */
538         while(isdigit(*ptr))
539           ptr++;
540
541         /* <note> */
542         /* B or b is not in specs, but I decided to put it, because
543            it's in some RTTL files. It's the same to H note */
544              if ((*ptr=='B') || (*ptr=='b')) ringtone->notes[NrNote].note=12;
545         else if ((*ptr=='H') || (*ptr=='h')) ringtone->notes[NrNote].note=12;        
546         else if ((*ptr>='a') && (*ptr<='g')) ringtone->notes[NrNote].note=((*ptr-'a')*2)+10;
547         else if ((*ptr>='A') && (*ptr<='G')) ringtone->notes[NrNote].note=((*ptr-'A')*2)+10;
548         else ringtone->notes[NrNote].note=255;
549
550         if ((ringtone->notes[NrNote].note>13)&&(ringtone->notes[NrNote].note!=255))
551           ringtone->notes[NrNote].note-=14;
552
553         ptr++;
554       
555         if ((*ptr)=='#') {
556           ringtone->notes[NrNote].note++;
557           if ((ringtone->notes[NrNote].note==5) || (ringtone->notes[NrNote].note==13))
558             ringtone->notes[NrNote].note++;
559           ptr++;
560         }
561
562         /* Check for dodgy rttl */
563         /* [<special-duration>] */
564         if (*ptr=='.') {
565           ringtone->notes[NrNote].duration*=1.5;
566           ptr++;
567         }
568
569         /* [<scale>] */
570         if (ringtone->notes[NrNote].note!=255) {
571           if (isdigit(*ptr)) {
572             ringtone->notes[NrNote].note+=GetRTTLScale(ptr)*14;
573             ptr++;
574           } else
575             ringtone->notes[NrNote].note+=DefNoteScale*14;
576         }
577
578         /* [<special-duration>] */
579         if (*ptr=='.') {
580           ringtone->notes[NrNote].duration*=1.5;
581           ptr++;
582         }
583
584         /* Style */
585         ringtone->notes[NrNote].style=DefNoteStyle;
586         
587         /* Tempo */
588         ringtone->notes[NrNote].tempo=DefNoteTempo;
589
590         NrNote++;            
591         
592         break;
593     }
594     ptr=strtok(NULL, ", ");
595   }
596
597   ringtone->NrNotes=NrNote;
598
599   return GE_NONE;
600 }
601
602 GSM_Error GSM_SaveRingtoneFile(char *FileName, GSM_Ringtone *ringtone)
603 {
604
605   FILE *file;
606   bool done=false;
607   
608    file = fopen(FileName, "wb");
609       
610    if (!file)
611      return(GE_CANTOPENFILE);
612         
613    if (strstr(FileName,".ott"))
614    {
615      saveott(file, ringtone);
616      done=true;
617    }
618    if (strstr(FileName,".mid"))
619    {
620      savemid(file, ringtone);
621      done=true;
622    }   
623
624    if (!done) saverttl(file, ringtone);
625
626    fclose(file);
627    
628    return GE_NONE;
629 }
630
631 void saveott(FILE *file, GSM_Ringtone *ringtone)
632 {
633   char Buffer[2000];
634   
635   int i=2000;
636     
637   GSM_PackRingtone(ringtone, Buffer, &i);
638   
639   fwrite(Buffer, 1, i, file);
640 }
641
642 void saverttl(FILE *file, GSM_Ringtone *ringtone)
643 {
644   u8 DefNoteScale=2, DefNoteDuration=4;
645   int DefNoteTempo=63;
646   u8 DefNoteStyle=ContinuousStyle;
647
648   int CurrentNote;
649   int buffer[6];
650   int i,j,k=0;
651   
652   /* Saves ringtone name */
653   fprintf(file,_("%s:"),ringtone->name);
654
655   /* Find the most frequently used duration and use this for the default */
656  
657   for (i=0;i<6;i++) buffer[i]=0;
658   for (i=0;i<ringtone->NrNotes;i++) {
659     switch (ringtone->notes[i].duration) {
660       case 192:buffer[0]++; break;
661       case 128:buffer[0]++; break;
662       case  96:buffer[1]++; break;
663       case  64:buffer[1]++; break;
664       case  48:buffer[2]++; break;
665       case  32:buffer[2]++; break;
666       case  24:buffer[3]++; break;
667       case  16:buffer[3]++; break;
668       case  12:buffer[4]++; break;
669       case   8:buffer[4]++; break;
670       case   6:buffer[5]++; break;
671       case   4:buffer[5]++; break;
672     }
673   }
674
675   /* Now find the most frequently used */
676   j=0;
677   for (i=0;i<6;i++) {
678     if (buffer[i]>j) {
679       k=i; 
680       j=buffer[i];
681     }
682   }
683
684   /* Finally convert and save the default duration */
685
686   switch (k) {
687       case 0: DefNoteDuration=128; fprintf(file, _("d=1,")); break;     
688       case 1: DefNoteDuration= 64; fprintf(file, _("d=2,")); break;     
689       case 2: DefNoteDuration= 32; fprintf(file, _("d=4,")); break;     
690       case 3: DefNoteDuration= 16; fprintf(file, _("d=8,")); break;     
691       case 4: DefNoteDuration=  8; fprintf(file,_("d=16,")); break;     
692       case 5: DefNoteDuration=  4; fprintf(file,_("d=32,")); break;     
693      default: DefNoteDuration= 16; fprintf(file, _("d=8,")); break;     
694   }  
695
696
697   /* Find the most frequently used scale and use this for the default */
698
699   for (i=0;i<6;i++) buffer[i]=0;
700   for (i=0;i<ringtone->NrNotes;i++) {
701     if (ringtone->notes[i].note!=255) {
702       buffer[ringtone->notes[i].note/14]++;
703     }
704   }
705   j=0;
706   for (i=0;i<6;i++) {
707     if (buffer[i]>j) {
708       DefNoteScale=i;
709       j=buffer[i];
710     }
711   }
712
713   if (ringtone->NrNotes!=0) {
714     DefNoteTempo=ringtone->notes[0].tempo;
715     DefNoteStyle=ringtone->notes[0].style;
716   }
717
718   /* Save the default scale */
719   fprintf(file,_("o=%i,"),DefNoteScale+4);  
720
721   switch (DefNoteStyle) {
722     case StaccatoStyle: fprintf(file,_("s=S,")); break;
723     case NaturalStyle : fprintf(file,_("s=N,")); break;
724   }
725
726   /* Save the default tempo */
727   fprintf(file,_("b=%i,"),DefNoteTempo);
728
729   /* Save the default loop */
730   fprintf(file,_("l=%i:"),ringtone->Loop);
731
732 #ifdef DEBUG
733   printf("DefNoteDuration=%d\n", DefNoteDuration);
734   printf("DefNoteScale=%d\n", DefNoteScale);
735   printf("Number of notes=%d\n",ringtone->NrNotes);
736 #endif
737   
738   /* Now loop round for each note */
739
740   for (i=0;i<ringtone->NrNotes;i++) {
741     CurrentNote=ringtone->notes[i].note;
742
743     if (ringtone->notes[i].style!=DefNoteStyle) {
744       DefNoteStyle=ringtone->notes[i].style;
745       switch (DefNoteStyle) {
746         case StaccatoStyle  : fprintf(file,_("s=S")); break;
747         case NaturalStyle   : fprintf(file,_("s=N")); break;
748         case ContinuousStyle: fprintf(file,_("s=C")); break;
749       }
750       /* And a separator before next note */
751       if (i!=ringtone->NrNotes-1)
752         fprintf(file,_(","));
753     }
754     
755     if (ringtone->notes[i].tempo!=DefNoteTempo) {
756       DefNoteTempo=ringtone->notes[i].tempo;
757       fprintf(file,_("b=%i"),DefNoteTempo);
758       if (i!=ringtone->NrNotes-1)
759         fprintf(file,_(","));
760     }    
761     
762     /* This note has a duration different than the default. We must save it */
763     if (ringtone->notes[i].duration!=DefNoteDuration) {
764       switch (ringtone->notes[i].duration) {
765         case 192: fprintf(file, _("1")); break; //192=128*1.5
766         case 128: fprintf(file, _("1")); break;
767         case  96: fprintf(file, _("2")); break; //96=64*1.5
768         case  64: fprintf(file, _("2")); break;
769         case  48: fprintf(file, _("4")); break; //48=32*1.5
770         case  32: fprintf(file, _("4")); break;
771         case  24: fprintf(file, _("8")); break; //24=16*1.5
772         case  16: fprintf(file, _("8")); break;
773         case  12: fprintf(file,_("16")); break; //12=8*1.5
774         case   8: fprintf(file,_("16")); break;
775         case   6: fprintf(file,_("32")); break; //6=4*1.5
776         case   4: fprintf(file,_("32")); break;
777         default: 
778           break;
779       }
780     }
781     
782     /* Now save the actual note */
783     switch (GSM_GetNote(CurrentNote)) {
784       case Note_C  :fprintf(file,_("c"));break;
785       case Note_Cis:fprintf(file,_("c#"));break;
786       case Note_D  :fprintf(file,_("d"));break;
787       case Note_Dis:fprintf(file,_("d#"));break;
788       case Note_E  :fprintf(file,_("e"));break;
789       case Note_F  :fprintf(file,_("f"));break;
790       case Note_Fis:fprintf(file,_("f#"));break;
791       case Note_G  :fprintf(file,_("g"));break;
792       case Note_Gis:fprintf(file,_("g#"));break;
793       case Note_A  :fprintf(file,_("a"));break;
794       case Note_Ais:fprintf(file,_("a#"));break;
795       case Note_H  :fprintf(file,_("h"));break;
796       default      :fprintf(file,_("p"));break; //Pause ?
797     }
798
799     /* Saving info about special duration */
800     if (ringtone->notes[i].duration==128*1.5 ||
801         ringtone->notes[i].duration==64*1.5 ||
802         ringtone->notes[i].duration==32*1.5 ||
803         ringtone->notes[i].duration==16*1.5 ||
804         ringtone->notes[i].duration==8*1.5 ||
805         ringtone->notes[i].duration==4*1.5)
806       fprintf(file,_("."));
807     
808     /* This note has a scale different than the default, so save it */
809     if ( (CurrentNote!=255) && (CurrentNote/14!=DefNoteScale))
810         fprintf(file,_("%i"),(CurrentNote/14)+4);
811     
812     /* And a separator before next note */
813     if (i!=ringtone->NrNotes-1)
814       fprintf(file,_(","));
815
816   }
817 }
818
819 void WriteVarLen(char* midifile, int* current, long value)
820 {
821    long buffer;
822
823    buffer = value & 0x7f;
824
825    while (value >>= 7) {
826       buffer <<= 8;
827       buffer |= 0x80;
828       buffer += (value & 0x7f);
829    }
830
831    while (1) {
832      midifile[(*current)++] = buffer;
833      if (buffer & 0x80)
834        buffer >>= 8;
835      else
836        break;
837    }
838 }
839
840 #define singlepauses
841
842 /* FIXME: need adding tempo before each note and scale too ? */
843 void savemid(FILE* file, GSM_Ringtone *ringtone)
844 {
845   char midifile[3000] = { 0x4D, 0x54, 0x68, 0x64, // MThd
846                           0x00, 0x00, 0x00, 0x06, // chunk length
847                           0x00, 0x00,             // format 0
848                           0x00, 0x01,             // one track
849                           0x00, 0x20,             // 32 per quarter note
850                           0x4D, 0x54, 0x72, 0x6B, // MTrk
851                           0x00, 0x00, 0x00, 0x00, // chunk length
852                           0x00, 0xFF, 0x51, 0x03, // tempo meta event
853                           0x00, 0x00, 0x00        // 3 bytes for us for a quarter note
854                         };
855
856 //{ "c", "c#", "d", "d#", "e",      "f", "f#", "g", "g#", "a", "a#", "h" };
857 char midinotes[14] =
858   { 0,    1,    2,   3,    4,   4,   5,   6,    7,   8,    9,  10 ,   11,   11 };
859
860   int length = 20;
861   int start = 22;
862   int current = 26, i, note, pause = 0;
863   bool notesexisting = false;
864
865   /* FIXME: we need add tempo before each note or so... */
866   long duration=60000000/63;  // us for a quarter note
867   if (ringtone->NrNotes!=0)
868     duration=60000000/ringtone->notes[0].tempo;
869   midifile[current++] = duration >> 16;
870   midifile[current++] = duration >> 8;
871   midifile[current++] = duration;
872
873   for (i = 0; i < ringtone->NrNotes; i++) {
874
875     note = ringtone->notes[i].note;
876     if (note == 255) {   // readmid does not read pauses at the beginning
877
878       if (notesexisting) {
879         pause += ringtone->notes[i].duration;
880 #ifdef singlepauses
881         WriteVarLen(midifile,&current,pause);
882         pause=0;
883         midifile[current++]=0x00;   // pause
884         midifile[current++]=0x00;
885 #endif
886       }
887       
888     } else {
889
890       notesexisting = true;
891       note = 48+12*((note/14)%4) + midinotes[note%14];
892
893       WriteVarLen(midifile,&current,pause);
894       pause=0;
895       midifile[current++]=0x90;   // note on
896       midifile[current++]=note;
897       midifile[current++]=0x64;   // forte
898
899       WriteVarLen(midifile,&current,ringtone->notes[i].duration);
900       midifile[current++]=0x80;   // note off
901       midifile[current++]=note;
902       midifile[current++]=0x64; 
903
904     }
905   }
906
907   if (pause) {
908     WriteVarLen(midifile,&current,pause);
909     midifile[current++]=0x00;   // pause
910     midifile[current++]=0x00;   //
911   }
912   midifile[current++] = 0x00;
913   midifile[current++] = 0xFF;   // track end
914   midifile[current++] = 0x2F;
915   midifile[current++] = 0x00;
916   midifile[length++] = (current-start) >> 8;
917   midifile[length++] = current-start;
918
919   fwrite(midifile,1,current,file);
920 }
921
922 GSM_Error GSM_ReadBitmapFile(char *FileName, GSM_Bitmap *bitmap)
923 {
924
925   FILE *file;
926   unsigned char buffer[300];
927   GSM_Error error;
928   GSM_Filetypes filetype=None;
929
930   file = fopen(FileName, "rb");
931
932   if (!file)
933     return(GE_CANTOPENFILE);
934
935   fread(buffer, 1, 9, file); /* Read the header of the file. */
936
937   /* Attempt to identify filetype */
938
939   if (memcmp(buffer, "NOL",3)==0) {  /* NOL files have 'NOL' at the start */
940     filetype=NOL;
941   } else if (memcmp(buffer, "NGG",3)==0) {  /* NGG files have 'NGG' at the start */
942     filetype=NGG;
943   } else if (memcmp(buffer, "FORM",4)==0) {  /* NSL files have 'FORM' at the start */
944     filetype=NSL;
945   } else if (memcmp(buffer, "NLM",3)==0) {  /* NLM files have 'NLM' at the start */
946     filetype=NLM;
947   } else if (memcmp(buffer, "BM",2)==0) {  /* BMP, I61 and GGP files have 'BM' at the start */
948     filetype=BMP;    
949   } else if (memcmp(buffer, "/* XPM */",9)==0) {  /* XPM files have 'XPM' at the start */  
950     filetype=XPMF;
951   } else filetype=None;
952
953   if (strstr(FileName,".otb")) filetype=OTA; /* OTA files saved by NCDS3 */
954   
955   error=GE_NONE;
956   
957   rewind(file);
958
959   switch (filetype) {
960     case NOL: error=loadnol(file,bitmap); fclose(file); break;
961     case NGG: error=loadngg(file,bitmap); fclose(file); break;
962     case NSL: error=loadnsl(file,bitmap); fclose(file); break;
963     case NLM: error=loadnlm(file,bitmap); fclose(file); break;
964     case OTA: error=loadota(file,bitmap); fclose(file); break;
965     case BMP: error=loadbmp(file,bitmap); fclose(file); break;
966 #ifdef XPM
967     case XPMF:fclose(file);error=loadxpm(FileName,bitmap);break;
968 #endif
969     default : error=GE_INVALIDFILEFORMAT;
970   }
971
972   return(error);
973 }
974
975 #ifdef XPM
976
977 GSM_Error loadxpm(char *filename, GSM_Bitmap *bitmap)
978 {
979   int y,x,error;
980   XpmImage image;
981   XpmInfo info;
982
983   error=XpmReadFileToXpmImage(filename,&image,&info);
984
985   switch (error) {
986     case XpmColorError:  return GE_WRONGCOLORS;break;
987     case XpmColorFailed: return GE_WRONGCOLORS;break;
988     case XpmOpenFailed:  return GE_CANTOPENFILE;break;
989     case XpmFileInvalid: return GE_INVALIDFILEFORMAT;break;
990     case XpmSuccess: break;
991   }
992
993   if (image.ncolors!=2) {
994     printf("Wrong number of colors\n");
995     return GE_WRONGNUMBEROFCOLORS;
996   }
997
998   if ((image.height==48) && (image.width==84)) {
999     bitmap->type=GSM_StartupLogo;
1000   }
1001   else if ((image.height==65) && (image.width==96)) {
1002     bitmap->type=GSM_7110StartupLogo;
1003   }
1004   else if ((image.height==60) && (image.width==96)) {
1005     bitmap->type=GSM_6210StartupLogo;
1006   }
1007   else if ((image.height==28) && (image.width==72)) {
1008     bitmap->type=GSM_PictureImage;
1009   }
1010   else if ((image.height==14) && (image.width==72)) {
1011     bitmap->type=GSM_CallerLogo;
1012   }
1013   else {
1014 #ifdef DEBUG
1015     printf("Invalid Image Size (%dx%d).\n",image.width,image.height);
1016 #endif
1017     return GE_INVALIDIMAGESIZE;
1018   }
1019
1020   bitmap->height=image.height;
1021   bitmap->width=image.width;
1022   bitmap->size=bitmap->height*bitmap->width/8;
1023
1024   GSM_ClearBitmap(bitmap);
1025   
1026   for(y=0;y<image.height;y++) {
1027     for(x=0;x<image.width;x++) {
1028       if (image.data[y*image.width+x]==0) GSM_SetPointBitmap(bitmap,x,y);        
1029     }
1030   }
1031
1032   return GE_NONE;
1033 }
1034
1035 #endif
1036
1037 /* Based on the article from the Polish Magazine "Bajtek" 11/92 */
1038                                      /* Marcin-Wiacek@Topnet.PL */
1039 GSM_Error loadbmp(FILE *file, GSM_Bitmap *bitmap)
1040 {
1041   unsigned char buffer[34];
1042   bool first_white;
1043   int w,h,pos,y,x,i,sizeimage;
1044
1045   fread(buffer, 1, 34, file); //required part of header
1046
1047   h=buffer[22]+256*buffer[21]; //height of image in the file
1048   w=buffer[18]+256*buffer[17]; //width of image in the file
1049 #ifdef DEBUG
1050   printf("Image Size in BMP file: %dx%d\n",w,h);
1051 #endif
1052
1053   bitmap->type=GSM_7110StartupLogo;
1054   bitmap->width=96;
1055   bitmap->height=65;
1056   
1057   if (h==48 && w==84) {
1058     bitmap->width=84;
1059     bitmap->height=48;
1060     bitmap->type=GSM_StartupLogo;    
1061   }
1062   if (h==60 && w==96) {
1063     bitmap->width=96;
1064     bitmap->height=60;
1065     bitmap->type=GSM_6210StartupLogo;    
1066   }  
1067   if (h==14 && w==72) {
1068     bitmap->width=72;
1069     bitmap->height=14;
1070     bitmap->type=GSM_CallerLogo;    
1071   }    
1072   if (h==28 && w==72) {
1073     bitmap->width=72;
1074     bitmap->height=28;
1075     bitmap->type=GSM_PictureImage;    
1076   }    
1077   if (h==21 && w==78) {
1078     bitmap->width=78;
1079     bitmap->height=21;
1080     bitmap->type=GSM_7110OperatorLogo;    
1081   }    
1082
1083         /* Gabo !!! */
1084   bitmap->size=(bitmap->width*bitmap->height + 7)/8;
1085
1086   GSM_ClearBitmap(bitmap);  
1087
1088 #ifdef DEBUG
1089   printf("Number of colors in BMP file: ");
1090   switch (buffer[28]) {
1091     case 1:printf("2 (supported by gnokii)\n");break;
1092     case 4:printf("16 (not supported by gnokii)\n");break;
1093     case 8:printf("256 (not supported by gnokii)\n");break;
1094     case 24:printf("True Color (not supported by gnokii)\n");break;
1095     default:printf("unknown\n");break;
1096   }
1097 #endif
1098   if (buffer[28]!=1) {
1099     printf("Wrong number of colors\n"); //we support only 2 colors images !
1100     return GE_WRONGNUMBEROFCOLORS;
1101   }
1102
1103 #ifdef DEBUG
1104   printf("Compression in BMP file: ");
1105   switch (buffer[30]) {
1106     case 0:printf("no compression (supported by gnokii)\n");break;
1107     case 1:printf("RLE8 (not supported by gnokii)\n");break;
1108     case 2:printf("RLE4 (not supported by gnokii)\n");break;
1109     default:printf("unknown\n");break;
1110   }
1111 #endif  
1112   if (buffer[30]!=0) {
1113 #ifdef DEBUG
1114     printf("Subformat not supported\n"); //we don't support RLE compression
1115 #endif
1116     return GE_SUBFORMATNOTSUPPORTED;
1117   }  
1118   
1119   pos=buffer[10]-34;
1120   fread(buffer, 1, pos, file); //rest of header (if exists) and color palette
1121   
1122 #ifdef DEBUG
1123   printf("First color in BMP file: %i %i %i ",buffer[pos-8], buffer[pos-7], buffer[pos-6]);
1124   if (buffer[pos-8]==0 && buffer[pos-7]==0 && buffer[pos-6]==0) printf("(white)");
1125   if (buffer[pos-8]==0xFF && buffer[pos-7]==0xFF && buffer[pos-6]==0xFF) printf("(black)");
1126   if (buffer[pos-8]==102 && buffer[pos-7]==204 && buffer[pos-6]==102) printf("(green)");
1127   printf("\n");
1128
1129   printf("Second color in BMP file: %i %i %i ",buffer[pos-4], buffer[pos-3], buffer[pos-2]);
1130   if (buffer[pos-4]==0 && buffer[pos-3]==0 && buffer[pos-2]==0) printf("(white)");
1131   if (buffer[pos-4]==0xFF && buffer[pos-3]==0xFF && buffer[pos-2]==0xFF) printf("(black)");
1132   printf("\n");  
1133 #endif
1134   first_white=true;
1135   if (buffer[pos-8]!=0 || buffer[pos-7]!=0 || buffer[pos-6]!=0) first_white=false;
1136  
1137   sizeimage=0;
1138   pos=7;
1139   for (y=h-1;y>=0;y--) { //lines are written from the last to the first
1140     i=1;
1141     for (x=0;x<w;x++) {
1142       if (pos==7) { //new byte !
1143         fread(buffer, 1, 1, file);
1144         sizeimage++;
1145         i++;
1146         if(i==5) i=1; //each line is written in multiply of 4 bytes
1147       }
1148       if (x<=bitmap->width && y<=bitmap->height) { //we have top left corner !
1149         if (first_white) {
1150           if ((buffer[0]&(1<<pos))<=0) GSM_SetPointBitmap(bitmap,x,y);
1151         } else {
1152           if ((buffer[0]&(1<<pos))>0) GSM_SetPointBitmap(bitmap,x,y);
1153         }
1154       }
1155       pos--;
1156       if (pos<0) pos=7; //going to new byte
1157     }
1158     pos=7; //going to new byte
1159     if (i!=1) {
1160       while (i!=5) //each line is written in multiply of 4 bytes
1161       {
1162         fread(buffer, 1, 1, file);
1163         sizeimage++;
1164         i++;
1165       }
1166     }
1167   }
1168
1169 #ifdef DEBUG
1170   printf("Data size in BMP file: %i\n",sizeimage);
1171 #endif
1172     
1173   return(GE_NONE);
1174 }
1175
1176 GSM_Error loadnol(FILE *file, GSM_Bitmap *bitmap)
1177 {
1178
1179   unsigned char buffer[2000];
1180   int i,j;
1181   
1182   bitmap->type=GSM_OperatorLogo;
1183
1184   fread(buffer, 1, 6, file);
1185   fread(buffer, 1, 4, file);
1186   sprintf(bitmap->netcode, "%d %02d", buffer[0]+256*buffer[1], buffer[2]);
1187
1188   fread(buffer, 1, 4, file); /* Width and height of the icon. */
1189   bitmap->width=buffer[0];
1190   bitmap->height=buffer[2];
1191   bitmap->size=bitmap->height*bitmap->width/8;
1192
1193   if ((bitmap->height!=14) || (bitmap->width!=72)) {
1194 #ifdef DEBUG
1195     printf("Invalid Image Size (%dx%d).\n",bitmap->width,bitmap->height);
1196 #endif
1197     return GE_INVALIDIMAGESIZE;
1198   }
1199
1200   fread(buffer, 1, 6, file); /* Unknown bytes. */
1201   
1202   for (i=0; i<bitmap->size; i++) {
1203     if (fread(buffer, 1, 8, file)==8) {
1204       bitmap->bitmap[i]=0;
1205       for (j=7; j>=0;j--)
1206         if (buffer[7-j] == '1')
1207           bitmap->bitmap[i]|=(1<<j);
1208     }
1209     else
1210       return (GE_TOOSHORT);
1211   }
1212
1213 #ifdef DEBUG
1214   /* Some programs writes here fileinfo */
1215   if (fread(buffer, 1, 1, file)==1) {
1216     fprintf(stdout, _("Fileinfo: %c"),buffer[0]);
1217     while (fread(buffer, 1, 1, file)==1) {
1218       if (buffer[0]!=0x0A) fprintf(stdout,_("%c"),buffer[0]);
1219     }  
1220     fprintf(stdout, _("\n"));
1221   }
1222 #endif
1223
1224   return(GE_NONE);
1225 }
1226
1227 GSM_Error loadngg(FILE *file, GSM_Bitmap *bitmap)
1228 {
1229
1230   unsigned char buffer[2000];
1231   int i,j;
1232
1233   bitmap->type=GSM_CallerLogo;
1234
1235   fread(buffer, 1, 6, file);
1236   fread(buffer, 1, 4, file); /* Width and height of the icon. */
1237   bitmap->width=buffer[0];
1238   bitmap->height=buffer[2];
1239   bitmap->size=bitmap->height*bitmap->width/8;
1240   
1241   if ((bitmap->height!=14) || (bitmap->width!=72)) {
1242 #ifdef DEBUG
1243     printf("Invalid Image Size (%dx%d).\n",bitmap->width,bitmap->height);
1244 #endif
1245     return GE_INVALIDIMAGESIZE;
1246   }
1247   
1248   fread(buffer, 1, 6, file); /* Unknown bytes. */
1249     
1250   for (i=0; i<bitmap->size; i++) {
1251     if (fread(buffer, 1, 8, file)==8){
1252       bitmap->bitmap[i]=0;
1253       for (j=7; j>=0;j--)
1254         if (buffer[7-j] == '1')
1255           bitmap->bitmap[i]|=(1<<j);
1256     }
1257     else
1258       return(GE_TOOSHORT);
1259   }
1260
1261 #ifdef DEBUG
1262   /* Some programs writes here fileinfo */
1263   if (fread(buffer, 1, 1, file)==1) {
1264     fprintf(stdout, _("Fileinfo: %c"),buffer[0]);
1265     while (fread(buffer, 1, 1, file)==1) {
1266       if (buffer[0]!=0x0A) fprintf(stdout,_("%c"),buffer[0]);
1267     }  
1268     fprintf(stdout, _("\n"));
1269   }
1270 #endif
1271   
1272   return(GE_NONE);
1273 }
1274
1275 GSM_Error loadnsl(FILE *file, GSM_Bitmap *bitmap)
1276 {
1277
1278   unsigned char block[6],buffer[505];
1279   int block_size;
1280
1281   bitmap->size=0;
1282   
1283   while (fread(block,1,6,file)==6) {
1284
1285     block_size=block[4]*256+block[5];
1286
1287 #ifdef DEBUG
1288     fprintf(stdout,_("Block %c%c%c%c, size %i\n"),block[0],block[1],block[2],block[3],block_size);
1289 #endif
1290
1291     if (!strncmp(block, "FORM", 4)) {
1292 #ifdef DEBUG
1293       fprintf(stdout,_("  File ID\n"));
1294 #endif
1295     } else
1296     {
1297       if (block_size>504) return(GE_INVALIDFILEFORMAT);
1298
1299       if (block_size!=0) {
1300
1301         fread(buffer,1,block_size,file);
1302         buffer[block_size]=0; //if it's string, we end it with \0
1303
1304 #ifdef DEBUG
1305         if (!strncmp(block, "VERS", 4)) fprintf(stdout,_("  File saved by: %s\n"),buffer);
1306         if (!strncmp(block, "MODL", 4)) fprintf(stdout,_("  Logo saved from: %s\n"),buffer);
1307         if (!strncmp(block, "COMM", 4)) fprintf(stdout,_("  Phone was connected to COM port: %s\n"),buffer);
1308 #endif
1309         
1310         if (!strncmp(block, "NSLD", 4)) {          
1311           bitmap->type=GSM_StartupLogo;
1312           bitmap->height=48;
1313           bitmap->width=84;
1314           bitmap->size=(bitmap->height*bitmap->width)/8;
1315
1316           memcpy(bitmap->bitmap,buffer,bitmap->size);
1317
1318 #ifdef DEBUG
1319           fprintf(stdout,_("  Startup logo (size %i)\n"),block_size);
1320 #endif
1321         }
1322       }
1323     }
1324   }
1325   
1326   if (bitmap->size==0) return(GE_TOOSHORT);
1327
1328   return(GE_NONE);
1329 }
1330
1331 GSM_Error loadnlm (FILE *file, GSM_Bitmap *bitmap)
1332 {
1333   unsigned char buffer[1000];
1334   int pos,pos2,x,y;
1335   div_t division;
1336
1337   fread(buffer,1,5,file);
1338   fread(buffer,1,1,file);
1339
1340   switch (buffer[0]) {
1341   case 0x00: bitmap->type=GSM_OperatorLogo; break;
1342   case 0x01: bitmap->type=GSM_CallerLogo;   break;
1343   case 0x02: bitmap->type=GSM_StartupLogo;  break;
1344   case 0x03: bitmap->type=GSM_PictureImage; break;
1345   default:
1346     return(GE_SUBFORMATNOTSUPPORTED);
1347   }
1348   
1349   fread(buffer,1,4,file);
1350   bitmap->width=buffer[1];
1351   bitmap->height=buffer[2];
1352
1353   if (bitmap->type==GSM_StartupLogo  && bitmap->width==96 && bitmap->height==65)
1354     bitmap->type=GSM_7110StartupLogo;
1355   if (bitmap->type==GSM_StartupLogo  && bitmap->width==96 && bitmap->height==60)
1356     bitmap->type=GSM_6210StartupLogo;
1357   if (bitmap->type==GSM_OperatorLogo && bitmap->width==78 && bitmap->height==21)
1358     bitmap->type=GSM_7110OperatorLogo;
1359
1360   bitmap->size=bitmap->width*bitmap->height/8;
1361
1362   division=div(bitmap->width,8);
1363   if (division.rem!=0) division.quot++; /* For startup logos */
1364   
1365   if (fread(buffer,1,(division.quot*bitmap->height),file)!=(division.quot*bitmap->height))
1366     return(GE_TOOSHORT);
1367     
1368   GSM_ClearBitmap(bitmap);
1369   
1370   pos=0;pos2=7;
1371   for (y=0;y<bitmap->height;y++) {
1372     for (x=0;x<bitmap->width;x++) {
1373       if ((buffer[pos]&(1<<pos2))>0) GSM_SetPointBitmap(bitmap,x,y);
1374       pos2--;
1375       if (pos2<0) {pos2=7;pos++;} //going to new byte
1376     }
1377     if (pos2!=7) {pos2=7;pos++;} //for startup logos-new line means new byte
1378   }
1379
1380   return (GE_NONE);
1381 }
1382
1383 GSM_Error loadota(FILE *file, GSM_Bitmap *bitmap)
1384 {
1385
1386   char buffer[4];
1387
1388   fread(buffer,1,4,file);
1389
1390   bitmap->width=buffer[1];
1391   bitmap->height=buffer[2];
1392   bitmap->size=bitmap->width*bitmap->height/8;
1393
1394   if ((bitmap->height==48) && (bitmap->width==84)) {
1395     bitmap->type=GSM_StartupLogo;
1396   }
1397   else if ((bitmap->height==14) && (bitmap->width==72)) {
1398     bitmap->type=GSM_CallerLogo;
1399   }
1400   else {
1401 #ifdef DEBUG
1402     printf("Invalid Image Size (%dx%d).\n",bitmap->width,bitmap->height);
1403 #endif
1404     return GE_INVALIDIMAGESIZE;
1405   }
1406   
1407   if (fread(bitmap->bitmap,1,bitmap->size,file)!=bitmap->size)
1408     return(GE_TOOSHORT);
1409
1410   return(GE_NONE);
1411 }
1412
1413 GSM_Error GSM_SaveBitmapFile(char *FileName, GSM_Bitmap *bitmap)
1414 {
1415
1416   FILE *file;
1417   bool done=false;
1418      
1419    file = fopen(FileName, "wb");
1420       
1421    if (!file)
1422      return(GE_CANTOPENFILE);
1423
1424    if (strstr(FileName,".xpm")) { savexpm(file, bitmap); done=true; }   
1425    if (strstr(FileName,".nlm")) { savenlm(file, bitmap); done=true; }
1426    if (strstr(FileName,".ngg")) { savengg(file, bitmap); done=true; }
1427    if (strstr(FileName,".nsl")) { savensl(file, bitmap); done=true; }
1428    if (strstr(FileName,".otb")) { saveota(file, bitmap); done=true; }
1429    if (strstr(FileName,".nol")) { savenol(file, bitmap); done=true; }
1430    if (strstr(FileName,".bmp") ||
1431        strstr(FileName,".ggp") ||
1432        strstr(FileName,".i61"))
1433    {
1434      savebmp(file, bitmap);
1435      done=true;
1436    }
1437    
1438    if (!done)
1439    {
1440      switch (bitmap->type) {
1441        case GSM_CallerLogo      : savengg(file, bitmap); break;
1442        case GSM_OperatorLogo    : savenol(file, bitmap); break;
1443        case GSM_7110OperatorLogo: savebmp(file, bitmap); break;
1444        case GSM_7110StartupLogo : savebmp(file, bitmap); break;
1445        case GSM_6210StartupLogo : savebmp(file, bitmap); break;
1446        case GSM_StartupLogo     : savensl(file, bitmap); break;
1447        case GSM_PictureImage    : savenlm(file, bitmap); break;
1448        case GSM_WelcomeNoteText :                        break;
1449        case GSM_DealerNoteText  :                        break;
1450        case GSM_None            :                        break;
1451      }      
1452    }
1453   
1454    fclose(file);
1455    
1456    return GE_NONE;
1457 }
1458
1459 void savexpm(FILE *file, GSM_Bitmap *bitmap)
1460 {
1461   int x,y;
1462
1463   fprintf(file,_("/* XPM */\n"));
1464   fprintf(file,_("static char * ala_xpm[] = {\n"));
1465   fprintf(file,_("\"%i %i 2 1\",\n"),bitmap->width,bitmap->height);
1466   fprintf(file,_("\".   s c     m #000000       g4 #000000      g #000000       c #000000\",\n"));
1467   fprintf(file,_("\"#   s c     m #ffffff       g4 #ffffff      g #ffffff       c #ffffff\",\n"));
1468
1469   for (y=0;y<bitmap->height;y++) {
1470     fprintf(file,_("\""));
1471     for (x=0;x<bitmap->width;x++)
1472       if (GSM_IsPointBitmap(bitmap,x,y))
1473         fprintf(file,_("."));
1474       else
1475         fprintf(file,_("#"));
1476     fprintf(file,_("\""));
1477     if (y==bitmap->height-1)
1478       fprintf(file,_("};\n"));
1479     else
1480       fprintf(file,_(",\n"));
1481   }
1482 }
1483
1484 /* Based on the article from the Polish Magazine "Bajtek" 11/92 */
1485                                      /* Marcin-Wiacek@Topnet.PL */
1486 void savebmp(FILE *file, GSM_Bitmap *bitmap)
1487 {
1488   int x,y,pos,i,sizeimage;
1489   unsigned char buffer[1];
1490   div_t division;
1491   
1492   unsigned char header[]={
1493 /*1'st header*/   'B','M',             /* BMP file ID */
1494                   0x00,0x00,0x00,0x00, /* Size of file */
1495                   0x00,0x00,           /* Reserved for future use */
1496                   0x00,0x00,           /* Reserved for future use */
1497                     62,0x00,0x00,0x00, /* Offset for image data */
1498                  
1499 /*2'nd header*/     40,0x00,0x00,0x00, /* Length of this part of header */
1500                   0x00,0x00,0x00,0x00, /* Width of image */
1501                   0x00,0x00,0x00,0x00, /* Height of image */             
1502                      1,0x00,           /* How many planes in target device */
1503                      1,0x00,           /* How many colors in image. 1 means 2^1=2 colors */
1504                   0x00,0x00,0x00,0x00, /* Type of compression. 0 means no compression */
1505 /*Sometimes */    0x00,0x00,0x00,0x00, /* Size of part with image data */
1506 /*ttttttt...*/    0xE8,0x03,0x00,0x00, /* XPelsPerMeter */
1507 /*hhiiiiissss*/   0xE8,0x03,0x00,0x00, /* YPelsPerMeter */                
1508 /*part of header*/2,0x00,0x00,0x00, /* How many colors from palette is used */
1509 /*doesn't exist*/ 0x00,0x00,0x00,0x00, /* How many colors from palette is required to display image. 0 means all */
1510                  
1511 /*Color palette*/ 0x00,0x00,0x00,      /* First color in palette in Blue, Green, Red. Here white */
1512                   0x00,                /* Each color in palette is end by 4'th byte */
1513                   102,204,102,      /* Second color in palette in Blue, Green, Red. Here green */
1514                   0x00};               /* Each color in palette is end by 4'th byte */
1515
1516   header[22]=bitmap->height;
1517   header[18]=bitmap->width;
1518      
1519   pos=7;
1520   sizeimage=0;
1521   for (y=bitmap->height-1;y>=0;y--) { //lines are written from the last to the first
1522     i=1;
1523     for (x=0;x<bitmap->width;x++) {
1524       if (pos==7) { //new byte !
1525         if (x!=0) sizeimage++;
1526         i++;
1527         if(i==5) i=1; //each line is written in multiply of 4 bytes
1528       }
1529       pos--;
1530       if (pos<0) pos=7; //going to new byte
1531     }
1532     pos=7; //going to new byte
1533     sizeimage++;
1534     if (i!=1) {
1535       while (i!=5) //each line is written in multiply of 4 bytes
1536       {
1537         sizeimage++;
1538         i++;
1539       }
1540     }
1541   }
1542 #ifdef DEBUG
1543   printf("Data size in BMP file: %i\n",sizeimage);
1544 #endif
1545   division=div(sizeimage,256);
1546   header[35]=division.quot;
1547   header[34]=sizeimage-(division.quot*256);
1548   
1549   sizeimage=sizeimage+sizeof(header);
1550 #ifdef DEBUG
1551   printf("Size of BMP file: %i\n",sizeimage);
1552 #endif
1553   division=div(sizeimage,256);
1554   header[3]=division.quot;
1555   header[2]=sizeimage-(division.quot*256);
1556        
1557   fwrite(header,1,sizeof(header),file);
1558
1559   pos=7;
1560   for (y=bitmap->height-1;y>=0;y--) { //lines are written from the last to the first
1561     i=1;
1562     for (x=0;x<bitmap->width;x++) {
1563       if (pos==7) { //new byte !
1564         if (x!=0) fwrite(buffer, 1, sizeof(buffer), file);
1565         i++;
1566         if(i==5) i=1; //each line is written in multiply of 4 bytes
1567         buffer[0]=0;
1568       }
1569       if (!GSM_IsPointBitmap(bitmap,x,y)) buffer[0]|=(1<<pos);
1570       pos--;
1571       if (pos<0) pos=7; //going to new byte
1572     }
1573     pos=7; //going to new byte
1574     fwrite(buffer, 1, sizeof(buffer), file);
1575     if (i!=1) {
1576       while (i!=5) //each line is written in multiply of 4 bytes
1577       {
1578         buffer[0]=0;
1579         fwrite(buffer, 1, sizeof(buffer), file);
1580         i++;
1581       }
1582     }
1583   }
1584 }
1585
1586 void savengg(FILE *file, GSM_Bitmap *bitmap)
1587 {
1588
1589   char header[]={'N','G','G',0x00,0x01,0x00,
1590                  0x00,0x00,           /* Width */
1591                  0x00,0x00,           /* Height */
1592                  0x01,0x00,0x01,0x00,
1593                  0x00,                /* Unknown.Can't be checksum - for */
1594                                       /* the same logo files can be different */
1595                  0x00};  
1596
1597   char buffer[8];
1598   int i,j;
1599   GSM_Bitmap copy;
1600   
1601   copy=*bitmap;
1602   
1603   GSM_ResizeBitmap(&copy,GSM_CallerLogo);
1604   
1605   header[6]=copy.width;
1606   header[8]=copy.height;
1607
1608   fwrite(header,1,sizeof(header),file);
1609
1610   for (i=0; i<copy.size; i++) {
1611     for (j=7; j>=0;j--)
1612       if ((copy.bitmap[i]&(1<<j))>0) {
1613         buffer[7-j] = '1';
1614       } else {
1615         buffer[7-j] = '0';
1616       }
1617     fwrite(buffer,1,8,file);
1618   }
1619 }
1620   
1621 void savenol(FILE *file, GSM_Bitmap *bitmap)
1622 {
1623
1624   char header[]={'N','O','L',0x00,0x01,0x00,
1625                  0x00,0x00,           /* MCC */
1626                  0x00,0x00,           /* MNC */
1627                  0x00,0x00,           /* Width */
1628                  0x00,0x00,           /* Height */
1629                  0x01,0x00,0x01,0x00,
1630                  0x00,                /* Unknown.Can't be checksum - for */
1631                                       /* the same logo files can be different */
1632                  0x00};
1633   char buffer[8];
1634   int i,j,country,net;
1635   GSM_Bitmap copy;
1636   
1637   copy=*bitmap;
1638   
1639   GSM_ResizeBitmap(&copy,GSM_OperatorLogo);
1640   
1641   sscanf(copy.netcode, "%d %d", &country, &net);
1642
1643   header[6]=country%256;
1644   header[7]=country/256;
1645   header[8]=net%256;
1646   header[9]=net/256;
1647   header[10]=copy.width;
1648   header[12]=copy.height;
1649
1650   fwrite(header,1,sizeof(header),file);
1651   
1652   for (i=0; i<copy.size; i++) {
1653     for (j=7; j>=0;j--)
1654       if ((copy.bitmap[i]&(1<<j))>0) {
1655         buffer[7-j] = '1';
1656       } else {
1657         buffer[7-j] = '0';
1658       }
1659     fwrite(buffer,1,8,file);
1660   }
1661 }
1662
1663 void savensl(FILE *file, GSM_Bitmap *bitmap)
1664 {
1665
1666   u8 header[]={'F','O','R','M', 0x01,0xFE,  /* File ID block,      size 1*256+0xFE=510*/
1667                'N','S','L','D', 0x01,0xF8}; /* Startup Logo block, size 1*256+0xF8=504*/
1668   GSM_Bitmap copy;
1669   
1670   copy=*bitmap;
1671   
1672   GSM_ResizeBitmap(&copy,GSM_StartupLogo);
1673   
1674   fwrite(header,1,sizeof(header),file);
1675
1676   fwrite(copy.bitmap,1,copy.size,file);
1677 }
1678
1679 void saveota(FILE *file, GSM_Bitmap *bitmap)
1680 {
1681
1682   char header[]={0x01,
1683                  0x00, /* Width */
1684                  0x00, /* Height */
1685                  0x01};
1686   GSM_Bitmap copy;
1687   
1688   copy=*bitmap;
1689   
1690   header[1]=copy.width;
1691   header[2]=copy.height;
1692     
1693   fwrite(header,1,sizeof(header),file);
1694
1695   fwrite(copy.bitmap,1,copy.size,file);
1696 }
1697
1698 void savenlm(FILE *file, GSM_Bitmap *bitmap)
1699 {
1700
1701   char header[]={'N','L','M', /* Nokia Logo Manager file ID. */
1702                  0x20,
1703                  0x01,
1704                  0x00,        /* 0x00 (OP), 0x01 (CLI), 0x02 (Startup), 0x03 (Picture)*/
1705                  0x00,        /* Number of images inside file - 1. 0x01==2 images, 0x03==4 images, etc. */
1706                  0x00,        /* Width. */
1707                  0x00,        /* Height. */
1708                  0x01};
1709                  
1710   unsigned char buffer[1000];
1711   int x,y,pos,pos2;
1712   div_t division;
1713   GSM_Bitmap copy;
1714   
1715   copy=*bitmap;
1716   
1717   switch (copy.type) {
1718   case GSM_OperatorLogo    : header[5]=0x00; break;
1719   case GSM_7110OperatorLogo: header[5]=0x00; break;
1720   case GSM_CallerLogo      : header[5]=0x01; break;
1721   case GSM_StartupLogo     : header[5]=0x02; break;
1722   case GSM_7110StartupLogo : header[5]=0x02; break;
1723   case GSM_6210StartupLogo : header[5]=0x02; break;
1724   case GSM_PictureImage    : header[5]=0x03; break;
1725   case GSM_WelcomeNoteText :                 break;
1726   case GSM_DealerNoteText  :                 break;
1727   case GSM_None            :                 break;
1728   }
1729   
1730   header[7]=copy.width;
1731   header[8]=copy.height;
1732   
1733   pos=0;pos2=7;
1734   for (y=0;y<copy.height;y++) {
1735     for (x=0;x<copy.width;x++) {
1736       if (pos2==7) buffer[pos]=0;
1737       
1738       if (GSM_IsPointBitmap(&copy,x,y)) buffer[pos]|=(1<<pos2);
1739       
1740       pos2--;
1741       if (pos2<0) {pos2=7;pos++;} //going to new line
1742     }
1743     if (pos2!=7) {pos2=7;pos++;} //for startup logos - new line with new byte
1744   }
1745   
1746   division=div(copy.width,8);
1747   if (division.rem!=0) division.quot++; /* For startup logos */
1748   
1749   fwrite(header,1,sizeof(header),file);
1750
1751   fwrite(buffer,1,(division.quot*copy.height),file);
1752 }
1753
1754 /* mode == 0 -> overwrite
1755  * mode == 1 -> ask
1756  * mode == 2 -> append
1757  */
1758 int GSM_SaveTextFile(char *FileName, char *text, int mode)
1759 {
1760
1761   FILE *file;
1762
1763   if (mode == 2) file = fopen(FileName, "a");
1764             else file = fopen(FileName, "w");
1765
1766   if (!file) return -1;
1767   
1768   fprintf(file, "%s\n\n", text);
1769   
1770   fclose(file);
1771
1772   return mode;
1773 }
1774
1775 GSM_Error GSM_SaveBackupFile(char *FileName, GSM_Backup *backup)
1776 {
1777
1778   FILE *file;
1779      
1780   file = fopen(FileName, "wb");
1781       
1782   if (!file) return(GE_CANTOPENFILE);
1783
1784   savelmb(file, backup);
1785
1786   fclose(file);
1787    
1788   return GE_NONE;
1789 }
1790
1791 void savelmbstartupentry(FILE *file, GSM_Bitmap startup, GSM_Bitmap text, bool available)
1792 {  
1793   /* Welcome note and logo header block */
1794   char req[1000] = {'W','E','L',' ',    /*block identifier*/
1795                     00,00,              /*block data size*/
1796                     0x02,00,00,00,00,00,
1797                     0x02};              /*number of blocks (like in 6110 frame)*/
1798
1799   int count=13;
1800
1801   if (!available) {
1802   } else {
1803     count=count+N6110_MakeStartupLogoFrame(req+13,startup);
1804   }
1805
1806   req[count++]=0x02;
1807   req[count++]=strlen(text.text);
1808   memcpy(req+count,text.text,strlen(text.text));
1809   count=count+strlen(text.text);
1810
1811   req[4]=(count-12)%256;
1812   req[5]=(count-12)/256;
1813
1814   fwrite(req, 1, count, file);
1815 }                    
1816
1817 void savelmbopentry(FILE *file, GSM_Bitmap bitmap)
1818 {  
1819   /* Operator logo header block */
1820   char req[500] = {'O','L','G',' ',        /*block identifier*/
1821                    0x88,00,                /*block data size*/
1822                    0x02,00,00,00,00,00,00};
1823
1824   int count=13;
1825
1826   count=count+N6110_MakeOperatorLogoFrame(req+13,bitmap);
1827
1828   req[4]=(count-7)%256;
1829   req[5]=(count-7)/256;
1830
1831   req[17]=req[17]+5; //we fix size of logo block
1832
1833   fwrite(req, 1, count, file);
1834 }                    
1835
1836 /* Work in progress ! */
1837 void savelmbspeedentry(FILE *file, GSM_SpeedDial speed)
1838 {  
1839   /* Speed dial header block */    
1840   char req[] = {'S','P','D',' ', /*block identifier*/
1841                 0x03,00,         /*block data size*/
1842                 0x02,00,
1843                 00,              /*number of speed dial*/
1844                 00,0xFF,00,
1845                 00,              /*number of speed dial*/
1846                 03,              /*memory type. ME=02;SM=03*/
1847                 00};             /*number of location assigned to speed dial*/
1848
1849   req[8]=req[12]=speed.Number;
1850
1851   if (speed.MemoryType==GMT_ME) req[13]=2; //memory type=GMT_ME    
1852
1853   req[14]=speed.Location;
1854
1855   fwrite(req, 1, 15, file);
1856 }                    
1857
1858 void savelmbcallerentry(FILE *file, GSM_Bitmap bitmap)
1859 {  
1860   char req[500] = {'C','G','R',' ',    /*block identifier*/
1861                    00,00,              /*block data size*/
1862                    02,00,              
1863                    00,                 /*group number=0,1,etc.*/
1864                    00,00,00};
1865
1866   int count=12;
1867
1868   req[8]=bitmap.number;
1869
1870   count=count+N6110_MakeCallerGroupFrame(req+12,bitmap);
1871
1872   req[4]=(count-11)%256;
1873   req[5]=(count-11)/256;
1874
1875   fwrite(req, 1, count, file);
1876 }                    
1877
1878 void savelmbpbkentry(FILE *file, GSM_PhonebookEntry entry)
1879 {
1880   char req[500] = {'P','B','E','2', /*block identifier*/
1881                    00,00,           /*block data size*/
1882                    00,00,           
1883                    00,              /*position of phonebook entry*/
1884                    00,              
1885                    03,              /*memory type. ME=02;SM=03*/
1886                    00,
1887                    00,              /*position of phonebook entry*/
1888                    00,
1889                    03,              /*memory type. ME=02;SM=03*/
1890                    00};
1891
1892   int count = 16, blocks;
1893
1894   req[8]=req[12]=entry.Location; //position of this entry
1895
1896   if (entry.MemoryType==GMT_ME) req[10]=req[14]=2;
1897
1898   /* There is NO full compatibility with files created by Logo Manager,
1899      anyway it works OK with files saved by this function - some bytes
1900      are probably random */
1901   count=count+N7110_MakePhonebookFrame(req+16, entry, &blocks);
1902
1903   req[4]=(count-12)%256;
1904   req[5]=(count-12)/256;
1905             
1906   fwrite(req, 1, count, file);      
1907 }
1908
1909 void savelmb(FILE *file, GSM_Backup *backup)
1910 {
1911   int i;
1912
1913   char LMBHeader[] = {'L','M','B',' '}; /*file identifier*/
1914     
1915   /* Phonebook header block */
1916   char PBKHeader[] = {'P','B','K',' ', /*block identifier*/
1917                       0x08,00,         /*block data size*/
1918                       0x02,00,         
1919                       03,              /*memory type. ME=02;SM=03*/
1920                       00,00,00,
1921                       00,00,           /*size of phonebook*/
1922                       14,              /*max length of each position*/
1923                       00,00,00,00,00};
1924                                       
1925   fwrite(LMBHeader, 1, sizeof(LMBHeader), file); /* Write the header of the file. */
1926
1927   if (backup->SIMPhonebookUsed!=0) {
1928     PBKHeader[12]=backup->SIMPhonebookSize%256;
1929     PBKHeader[13]=backup->SIMPhonebookSize/256;
1930     fwrite(PBKHeader, 1, sizeof(PBKHeader), file); 
1931
1932     for (i=0;i<backup->SIMPhonebookUsed;i++)
1933       savelmbpbkentry(file, backup->SIMPhonebook[i]);
1934   }
1935
1936   if (backup->PhonePhonebookUsed!=0) {
1937     PBKHeader[8]=2;     //memory type=GMT_ME
1938     PBKHeader[12]=backup->PhonePhonebookSize%256;
1939     PBKHeader[13]=backup->PhonePhonebookSize/256;
1940     PBKHeader[14]=0x16; //max size of one entry
1941     fwrite(PBKHeader, 1, sizeof(PBKHeader), file); 
1942
1943     for (i=0;i<backup->PhonePhonebookUsed;i++)
1944       savelmbpbkentry(file, backup->PhonePhonebook[i]);
1945   }
1946
1947   if (backup->CallerAvailable)
1948     for (i=0;i<5;i++) savelmbcallerentry(file,backup->CallerGroups[i]);
1949
1950   if (backup->SpeedAvailable)
1951     for (i=0;i<8;i++) savelmbspeedentry(file,backup->SpeedDials[i]);
1952
1953   if (backup->OperatorLogoAvailable) savelmbopentry(file,backup->OperatorLogo);
1954
1955   savelmbstartupentry(file,backup->StartupLogo,backup->StartupText,backup->StartupLogoAvailable);
1956 }
1957
1958 GSM_Error loadlmb(FILE *file, GSM_Backup *backup)
1959 {
1960
1961 //  fread(buffer, 1, 6, file);
1962   return(GE_NONE);
1963 }
1964
1965 GSM_Error GSM_ReadBackupFile(char *FileName, GSM_Backup *backup)
1966 {
1967
1968   FILE *file;
1969   unsigned char buffer[300];
1970   GSM_Error error;
1971   GSM_Filetypes filetype=None;
1972
1973   file = fopen(FileName, "rb");
1974
1975   if (!file) return(GE_CANTOPENFILE);
1976
1977   fread(buffer, 1, 4, file); /* Read the header of the file. */
1978
1979   /* Attempt to identify filetype */
1980
1981   if (memcmp(buffer, "LMB ",4)==0) {  /* LMB files have 'LMB ' at the start */
1982     filetype=LMB;
1983   } else filetype=None;
1984
1985   error=GE_NONE;
1986   
1987   rewind(file);
1988
1989   switch (filetype) {
1990     case LMB: error=loadlmb(file,backup); fclose(file); break;
1991     default : error=GE_INVALIDFILEFORMAT;
1992   }
1993
1994   return(error);
1995 }