1 /*
2
3 $Id$
4
5 G N O K I I
6
7 A Linux/Unix toolset and driver for the mobile phones.
8
9 This file is part of gnokii.
10
11 Gnokii is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
15
16 Gnokii is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with gnokii; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24
25 Copyright (C) 1999-2000 Hugh Blemings & Pavel Jan�k ml.
26 Copyright (C) 2000-2001 Chris Kemp, Marcin Wiacek
27 Copyright (C) 2000-2004 Pawel Kot
28 Copyright (C) 2001 Bartek Klepacz
29 Copyright (C) 2002 Markus Plail, Pavel Machek
30 Copyright (C) 2002-2004 BORBELY Zoltan
31 Copyright (C) 2003 Tomi Ollila
32
33 Functions to read and write common file types.
34
35 */
36
37 #include "config.h"
38 #include "compat.h"
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <ctype.h>
43 #include <sys/stat.h>
44
45 #include "gnokii-internal.h"
46 #include "gnokii.h"
47 #include "gsm-filetypes.h"
48 #include "phones/nokia.h"
49
50 #ifdef HAVE_X11_XPM_H
51 # include <X11/xpm.h>
52 #endif
53
54 #include "misc.h"
55
56 /* Ringtone File Functions */
57 /* ####################### */
58
59
60 /* Function to convert scale field in to correct number. */
ringtone_get_duration(char * num)61 static int ringtone_get_duration(char *num)
62 {
63 int duration = 0;
64
65 switch (atoi(num)) {
66 case 1:
67 duration = 128;
68 break;
69 case 2:
70 duration = 64;
71 break;
72 case 4:
73 duration = 32;
74 break;
75 case 8:
76 duration = 16;
77 break;
78 case 16:
79 duration = 8;
80 break;
81 case 32:
82 duration = 4;
83 break;
84 }
85 return duration;
86 }
87
88
ringtone_get_scale(char * num)89 static int ringtone_get_scale(char *num)
90 {
91 /* This may well need improving. */
92 int scale=0;
93
94 if ((atoi(num)) < 4) scale = (atoi(num));
95 if ((atoi(num)) > 4) scale = (atoi(num)) - 4;
96
97 return scale;
98 }
99
100
101 /* Currently only reads rtttl and ott files - can be later extended to midi etc. */
102
gn_file_ringtone_read(char * filename,gn_ringtone * ringtone)103 gn_error gn_file_ringtone_read(char *filename, gn_ringtone *ringtone)
104 {
105 FILE *file;
106 gn_error error;
107 gn_filetypes filetype;
108
109 file = fopen(filename, "rb");
110
111 if (!file)
112 return GN_ERR_FAILED;
113
114 /* FIXME: for now identify the filetype based on the extension */
115 /* I don't like this but I haven't got any .ott files to work out a better way */
116
117 filetype = GN_FT_RTTTL;
118 if (strstr(filename, ".ott")) filetype = GN_FT_OTT; /* OTT files saved by NCDS3 */
119 else if (strstr(filename, ".mid")) filetype = GN_FT_MIDI;
120 else if (strstr(filename, ".raw")) filetype = GN_FT_NOKRAW_TONE;
121
122 error = GN_ERR_NONE;
123
124 rewind(file); /* Not necessary for now but safer */
125
126 switch (filetype) {
127 case GN_FT_RTTTL:
128 error = file_rtttl_load(file, ringtone);
129 fclose(file);
130 break;
131 case GN_FT_OTT:
132 error = file_ott_load(file, ringtone);
133 fclose(file);
134 break;
135 case GN_FT_MIDI:
136 error = file_midi_load(file, ringtone);
137 fclose(file);
138 break;
139 case GN_FT_NOKRAW_TONE:
140 error = file_nokraw_load(file, ringtone);
141 fclose(file);
142 break;
143 default:
144 error = GN_ERR_WRONGDATAFORMAT;
145 break;
146 }
147
148 return error;
149 }
150
151
file_ott_load(FILE * file,gn_ringtone * ringtone)152 gn_error file_ott_load(FILE *file, gn_ringtone *ringtone)
153 {
154 char buffer[2000];
155 int i;
156
157 i = fread(buffer, 1, 2000, file);
158 if (!feof(file)) return GN_ERR_INVALIDSIZE;
159 return gn_ringtone_unpack(ringtone, buffer, i);
160 }
161
162
file_rtttl_load(FILE * file,gn_ringtone * ringtone)163 gn_error file_rtttl_load(FILE *file, gn_ringtone *ringtone)
164 {
165 int nr_note = 0;
166
167 int default_note_scale = 2;
168 int default_note_duration = 4;
169 unsigned char buffer[2000];
170 unsigned char *def, *notes, *ptr;
171
172 if (fread(buffer, 1, 2000, file) < 1)
173 return GN_ERR_FAILED;
174
175 /* This is for buggy RTTTL ringtones without name. */
176 if (buffer[0] != RTTTL_SEP[0]) {
177 strtok(buffer, RTTTL_SEP);
178 snprintf(ringtone->name, sizeof(ringtone->name), "%s", buffer);
179 def = strtok(NULL, RTTTL_SEP);
180 notes = strtok(NULL, RTTTL_SEP);
181 } else {
182 snprintf(ringtone->name, sizeof(ringtone->name), "GNOKII");
183 def = strtok(buffer, RTTTL_SEP);
184 notes = strtok(NULL, RTTTL_SEP);
185 }
186
187 ptr = strtok(def, ", ");
188 /* Parsing the <defaults> section. */
189 ringtone->tempo = 63;
190
191 while (ptr) {
192
193 switch(*ptr) {
194 case 'd':
195 case 'D':
196 default_note_duration = ringtone_get_duration(ptr+2);
197 break;
198 case 'o':
199 case 'O':
200 default_note_scale = ringtone_get_scale(ptr+2);
201 break;
202 case 'b':
203 case 'B':
204 ringtone->tempo = atoi(ptr+2);
205 break;
206 }
207
208 ptr = strtok(NULL,", ");
209 }
210
211 dprintf("default_note_duration = %d\n", default_note_duration);
212 dprintf("default_note_scale = %d\n", default_note_scale);
213
214 /* Parsing the <note-command>+ section. */
215 ptr = strtok(notes, ", ");
216 while (ptr && (nr_note < GN_RINGTONE_MAX_NOTES)) {
217
218 /* [<duration>] */
219 ringtone->notes[nr_note].duration = ringtone_get_duration(ptr);
220 if (ringtone->notes[nr_note].duration == 0)
221 ringtone->notes[nr_note].duration = default_note_duration;
222
223 /* Skip all numbers in duration specification. */
224 while (isdigit(*ptr))
225 ptr++;
226
227 /* <note> */
228
229 if ((*ptr >= 'a') && (*ptr <= 'g')) ringtone->notes[nr_note].note = ((*ptr - 'a') * 2) + 10;
230 else if ((*ptr >= 'A') && (*ptr <= 'G')) ringtone->notes[nr_note].note = ((*ptr - 'A') * 2) + 10;
231 else if ((*ptr == 'H') || (*ptr == 'h')) ringtone->notes[nr_note].note = 12;
232 else ringtone->notes[nr_note].note = 255;
233
234 if ((ringtone->notes[nr_note].note > 13) && (ringtone->notes[nr_note].note != 255))
235 ringtone->notes[nr_note].note -= 14;
236
237 ptr++;
238
239 if (*ptr == '#') {
240 ringtone->notes[nr_note].note++;
241 if ((ringtone->notes[nr_note].note == 5) || (ringtone->notes[nr_note].note == 13))
242 ringtone->notes[nr_note].note++;
243 ptr++;
244 }
245
246 /* Check for dodgy rtttl */
247 /* [<special-duration>] */
248 if (*ptr == '.') {
249 ringtone->notes[nr_note].duration *= 1.5;
250 ptr++;
251 }
252
253 /* [<scale>] */
254 if (ringtone->notes[nr_note].note != 255) {
255 if (isdigit(*ptr)) {
256 ringtone->notes[nr_note].note += ringtone_get_scale(ptr) * 14;
257 ptr++;
258 } else {
259 ringtone->notes[nr_note].note += default_note_scale * 14;
260 }
261 }
262
263 /* [<special-duration>] */
264 if (*ptr == '.') {
265 ringtone->notes[nr_note].duration *= 1.5;
266 ptr++;
267 }
268
269 nr_note++;
270 ptr = strtok(NULL, ", ");
271 }
272
273 ringtone->notes_count = nr_note;
274
275 return GN_ERR_NONE;
276 }
277
278
file_nokraw_load(FILE * file,gn_ringtone * ringtone)279 gn_error file_nokraw_load(FILE *file, gn_ringtone *ringtone)
280 {
281 unsigned char buf[4096];
282 int n;
283 gn_error err;
284
285 snprintf(ringtone->name, sizeof(ringtone->name), "GNOKII");
286
287 if ((n = fread(buf, 1, sizeof(buf), file)) < 0) return GN_ERR_UNKNOWN;
288
289 if (buf[0] == 0x00 && buf[1] == 0x02 && buf[2] == 0xfc && buf[3] == 0x09)
290 err = pnok_ringtone_from_raw(ringtone, buf + 4, n - 4);
291 else if (buf[0] == 0x02 && buf[1] == 0xfc && buf[2] == 0x09)
292 err = pnok_ringtone_from_raw(ringtone, buf + 3, n - 3);
293 else
294 err = pnok_ringtone_from_raw(ringtone, buf, n);
295
296 return err;
297 }
298
299
300 /* Save the ringtone file - this will overwrite the file */
301 /* Confirming must be done before this is called */
gn_file_ringtone_save(char * filename,gn_ringtone * ringtone)302 gn_error gn_file_ringtone_save(char *filename, gn_ringtone *ringtone)
303 {
304 FILE *file;
305 gn_error error;
306
307 file = fopen(filename, "wb");
308
309 if (!file) return GN_ERR_FAILED;
310
311 /* FIXME... */
312 /* We need a way of passing these functions a filetype rather than rely on the extension */
313 if (strstr(filename, ".ott")) {
314 error = file_ott_save(file, ringtone);
315 } else if (strstr(filename, ".mid")) {
316 error = file_midi_save(file, ringtone);
317 } else if (strstr(filename, ".raw3")) {
318 error = file_nokraw_save(file, ringtone, 0);
319 } else if (strstr(filename, ".raw")) {
320 error = file_nokraw_save(file, ringtone, 1);
321 } else {
322 error = file_rtttl_save(file, ringtone);
323 }
324 fclose(file);
325 return error;
326 }
327
328
file_ott_save(FILE * file,gn_ringtone * ringtone)329 gn_error file_ott_save(FILE *file, gn_ringtone *ringtone)
330 {
331 char buffer[2000];
332 int i = 2000;
333
334 /* PackRingtone writes up to i chars and returns in i the number written */
335 gn_ringtone_pack(ringtone, buffer, &i);
336
337 if (i < 2000) {
338 fwrite(buffer, 1, i, file);
339 return GN_ERR_NONE;
340 } else {
341 return GN_ERR_INVALIDSIZE;
342 }
343 }
344
file_rtttl_save(FILE * file,gn_ringtone * ringtone)345 gn_error file_rtttl_save(FILE *file, gn_ringtone *ringtone)
346 {
347 int default_duration, default_scale = 2, current_note;
348 int buffer[6];
349 int i, j, k = 0;
350
351 /* Saves ringtone name */
352 fprintf(file, "%s:", ringtone->name);
353
354 /* Find the most frequently used duration and use this for the default */
355 for (i = 0; i < 6; i++) buffer[i] = 0;
356 for (i = 0; i < ringtone->notes_count; i++) {
357 switch (ringtone->notes[i].duration) {
358 case 192:
359 buffer[0]++; break;
360 case 128:
361 buffer[0]++; break;
362 case 96:
363 buffer[1]++; break;
364 case 64:
365 buffer[1]++; break;
366 case 48:
367 buffer[2]++; break;
368 case 32:
369 buffer[2]++; break;
370 case 24:
371 buffer[3]++; break;
372 case 16:
373 buffer[3]++; break;
374 case 12:
375 buffer[4]++; break;
376 case 8:
377 buffer[4]++; break;
378 case 6:
379 buffer[5]++; break;
380 case 4:
381 buffer[5]++; break;
382 }
383 }
384
385 /* Now find the most frequently used */
386 j = 0;
387 for (i = 0; i < 6; i++) {
388 if (buffer[i] > j) {
389 k = i;
390 j = buffer[i];
391 }
392 }
393
394 /* Finally convert and save the default duration */
395 switch (k) {
396 case 0:
397 default_duration = 128;
398 fprintf(file, "d=1,");
399 break;
400 case 1:
401 default_duration = 64;
402 fprintf(file, "d=2,");
403 break;
404 case 2:
405 default_duration = 32;
406 fprintf(file, "d=4,");
407 break;
408 case 3:
409 default_duration = 16;
410 fprintf(file, "d=8,");
411 break;
412 case 4:
413 default_duration = 8;
414 fprintf(file, "d=16,");
415 break;
416 case 5:
417 default_duration = 4;
418 fprintf(file, "d=32,");
419 break;
420 default:
421 default_duration = 16;
422 fprintf(file, "d=8,");
423 break;
424 }
425
426 /* Find the most frequently used scale and use this for the default */
427 for (i = 0; i < 6; i++) buffer[i] = 0;
428 for (i = 0; i < ringtone->notes_count; i++) {
429 if (ringtone->notes[i].note != 255) {
430 buffer[ringtone->notes[i].note/14]++;
431 }
432 }
433 j = 0;
434 for (i = 0; i < 6; i++) {
435 if (buffer[i] > j) {
436 default_scale = i;
437 j = buffer[i];
438 }
439 }
440
441 /* Save the default scale and tempo */
442 fprintf(file, "o=%i,", default_scale+4);
443 fprintf(file, "b=%i:", ringtone->tempo);
444
445 dprintf("default_note_duration=%d\n", default_duration);
446 dprintf("default_note_scale=%d\n", default_scale);
447 dprintf("Number of notes=%d\n",ringtone->notes_count);
448
449 /* Now loop round for each note */
450 for (i = 0; i < ringtone->notes_count; i++) {
451 current_note = ringtone->notes[i].note;
452
453 /* This note has a duration different than the default. We must save it */
454 if (ringtone->notes[i].duration != default_duration) {
455 switch (ringtone->notes[i].duration) {
456 case 192: //192=128*1.5
457 fprintf(file, "1"); break;
458 case 128:
459 fprintf(file, "1"); break;
460 case 96: //96=64*1.5
461 fprintf(file, "2"); break;
462 case 64:
463 fprintf(file, "2"); break;
464 case 48: //48=32*1.5
465 fprintf(file, "4"); break;
466 case 32:
467 fprintf(file, "4"); break;
468 case 24: //24=16*1.5;
469 fprintf(file, "8"); break;
470 case 16:
471 fprintf(file, "8"); break;
472 case 12: //12=8*1.5
473 fprintf(file, "16"); break;
474 case 8:
475 fprintf(file, "16"); break;
476 case 6: //6=4*1.5
477 fprintf(file, "32"); break;
478 case 4:
479 fprintf(file, "32"); break;
480 default:
481 break;
482 }
483 }
484
485 /* Now save the actual note */
486 switch (gn_note_get(current_note)) {
487 case GN_RINGTONE_Note_C : fprintf(file, "c"); break;
488 case GN_RINGTONE_Note_Cis: fprintf(file, "c#"); break;
489 case GN_RINGTONE_Note_D : fprintf(file, "d"); break;
490 case GN_RINGTONE_Note_Dis: fprintf(file, "d#"); break;
491 case GN_RINGTONE_Note_E : fprintf(file, "e"); break;
492 case GN_RINGTONE_Note_F : fprintf(file, "f"); break;
493 case GN_RINGTONE_Note_Fis: fprintf(file, "f#"); break;
494 case GN_RINGTONE_Note_G : fprintf(file, "g"); break;
495 case GN_RINGTONE_Note_Gis: fprintf(file, "g#"); break;
496 case GN_RINGTONE_Note_A : fprintf(file, "a"); break;
497 case GN_RINGTONE_Note_Ais: fprintf(file, "a#"); break;
498 case GN_RINGTONE_Note_H : fprintf(file, "h"); break;
499 default : fprintf(file, "p"); break; /*Pause ? */
500 }
501
502 /* Saving info about special duration */
503 if (ringtone->notes[i].duration == 128 * 1.5 ||
504 ringtone->notes[i].duration == 64 * 1.5 ||
505 ringtone->notes[i].duration == 32 * 1.5 ||
506 ringtone->notes[i].duration == 16 * 1.5 ||
507 ringtone->notes[i].duration == 8 * 1.5 ||
508 ringtone->notes[i].duration == 4 * 1.5)
509 fprintf(file, ".");
510
511 /* This note has a scale different than the default, so save it */
512 if ( (current_note != 255) && (current_note/14 != default_scale))
513 fprintf(file, "%i",(current_note/14) + 4);
514
515 /* And a separator before next note */
516 if (i != ringtone->notes_count - 1)
517 fprintf(file, ",");
518 }
519
520 return GN_ERR_NONE;
521 }
522
file_nokraw_save(FILE * file,gn_ringtone * ringtone,int dct4)523 gn_error file_nokraw_save(FILE *file, gn_ringtone *ringtone, int dct4)
524 {
525 int n;
526 char buf[4096];
527 gn_error err;
528
529 n = sizeof(buf);
530 if ((err = pnok_ringtone_to_raw(buf, &n, ringtone, dct4)) != GN_ERR_NONE) return err;
531
532 if (fwrite(buf, n, 1, file) != 1) return GN_ERR_UNKNOWN;
533
534 return GN_ERR_NONE;
535 }
536
537 /* Bitmap file functions */
538 /* ##################### */
539
gn_file_bitmap_read(char * filename,gn_bmp * bitmap,gn_phone * info)540 gn_error gn_file_bitmap_read(char *filename, gn_bmp *bitmap, gn_phone *info)
541 {
542 FILE *file;
543 unsigned char buffer[9];
544 int error;
545 size_t count;
546 gn_filetypes filetype = GN_FT_None;
547
548 file = fopen(filename, "rb");
549
550 if (!file)
551 return GN_ERR_FAILED;
552
553 count = fread(buffer, 1, 9, file); /* Read the header of the file. */
554
555 /* Attempt to identify filetype */
556
557 if (count >= 3 && memcmp(buffer, "NOL", 3) == 0) { /* NOL files have 'NOL' at the start */
558 filetype = GN_FT_NOL;
559 } else if (count >= 3 && memcmp(buffer, "NGG", 3) == 0) { /* NGG files have 'NGG' at the start */
560 filetype = GN_FT_NGG;
561 } else if (count >= 4 && memcmp(buffer, "FORM", 4) == 0) { /* NSL files have 'FORM' at the start */
562 filetype = GN_FT_NSL;
563 } else if (count >= 3 && memcmp(buffer, "NLM", 3) == 0) { /* NLM files have 'NLM' at the start */
564 filetype = GN_FT_NLM;
565 } else if (count >= 2 && memcmp(buffer, "BM", 2) == 0) { /* BMP, I61 and GGP files have 'BM' at the start */
566 filetype = GN_FT_BMP;
567 } else if (count >= 9 && memcmp(buffer, "/* XPM */", 9) == 0) { /* XPM files have 'XPM' at the start */
568 filetype = GN_FT_XPMF;
569 }
570
571 if ((filetype == GN_FT_None) && strstr(filename, ".otb")) filetype = GN_FT_OTA; /* OTA files saved by NCDS3 */
572
573 rewind(file);
574
575 switch (filetype) {
576 case GN_FT_NOL:
577 error = file_nol_load(file, bitmap, info);
578 break;
579 case GN_FT_NGG:
580 error = file_ngg_load(file, bitmap, info);
581 break;
582 case GN_FT_NSL:
583 error = file_nsl_load(file, bitmap);
584 break;
585 case GN_FT_NLM:
586 error = file_nlm_load(file, bitmap);
587 break;
588 case GN_FT_OTA:
589 error = file_ota_load(file, bitmap, info);
590 break;
591 case GN_FT_BMP:
592 error = file_bmp_load(file, bitmap);
593 break;
594 case GN_FT_XPMF:
595 #ifdef XPM
596 error = file_xpm_load(filename, bitmap);
597 break;
598 #else
599 fprintf(stderr, _("Sorry, gnokii was not compiled with XPM support.\n"));
600 /* FALLTHRU */
601 #endif
602 default:
603 error = GN_ERR_WRONGDATAFORMAT;
604 break;
605 }
606
607 if (file) fclose(file);
608 return error;
609 }
610
611
612 #ifdef XPM
file_xpm_load(char * filename,gn_bmp * bitmap)613 gn_error file_xpm_load(char *filename, gn_bmp *bitmap)
614 {
615 int error, x, y;
616 XpmImage image;
617 XpmInfo info;
618
619 error = XpmReadFileToXpmImage(filename, &image, &info);
620
621 switch (error) {
622 case XpmColorError:
623 return GN_ERR_WRONGDATAFORMAT;
624 case XpmColorFailed:
625 return GN_ERR_WRONGDATAFORMAT;
626 case XpmOpenFailed:
627 return GN_ERR_FAILED;
628 case XpmFileInvalid:
629 return GN_ERR_WRONGDATAFORMAT;
630 case XpmSuccess:
631 default:
632 break;
633 }
634
635 if (image.ncolors != 2) return GN_ERR_WRONGDATAFORMAT;
636
637 bitmap->height = image.height;
638 bitmap->width = image.width;
639 bitmap->size = ((bitmap->width + 7) / 8) * bitmap->height;
640
641 if (bitmap->size > GN_BMP_MAX_SIZE) {
642 fprintf(stderr, _("Bitmap too large\n"));
643 return GN_ERR_INVALIDSIZE;
644 }
645
646 gn_bmp_clear(bitmap);
647
648 for (y = 0; y < image.height; y++) {
649 for (x = 0; x < image.width; x++) {
650 if (image.data[y * image.width + x] == 0) gn_bmp_point_set(bitmap, x, y);
651 }
652 }
653
654 return GN_ERR_NONE;
655 }
656 #endif
657
658 /* Based on the article from the Polish Magazine "Bajtek" 11/92 */
659 /* Marcin-Wiacek@Topnet.PL */
660
661 /* This loads the image as a startup logo - but is resized as necessary later */
file_bmp_load(FILE * file,gn_bmp * bitmap)662 gn_error file_bmp_load(FILE *file, gn_bmp *bitmap)
663 {
664 unsigned char buffer[34];
665 bool first_black;
666 int w, h, pos, y, x, i, sizeimage;
667
668 gn_bmp_clear(bitmap);
669
670 /* required part of header */
671 if (fread(buffer, 1, 34, file) != 34)
672 return GN_ERR_FAILED;
673
674 h = buffer[22] + 256 * buffer[21]; /* height of image in the file */
675 w = buffer[18] + 256 * buffer[17]; /* width of image in the file */
676 dprintf("Image Size in BMP file: %dx%d\n", w, h);
677 bitmap->width = w;
678 bitmap->height = h;
679 bitmap->size = bitmap->width * bitmap->height / 8;
680
681 dprintf("Number of colors in BMP file: ");
682 switch (buffer[28]) {
683 case 1:
684 dprintf("2 (supported by gnokii)\n");
685 break;
686 case 4:
687 dprintf("16 (not supported by gnokii)\n");
688 return GN_ERR_WRONGDATAFORMAT;
689 case 8:
690 dprintf("256 (not supported by gnokii)\n");
691 return GN_ERR_WRONGDATAFORMAT;
692 case 24:
693 dprintf("True Color (not supported by gnokii)\n");
694 return GN_ERR_WRONGDATAFORMAT;
695 default:
696 dprintf("unknown color type (not supported by gnokii)\n");
697 return GN_ERR_WRONGDATAFORMAT;
698 }
699
700 dprintf("Compression in BMP file: ");
701 switch (buffer[30]) {
702 case 0:
703 dprintf("no compression (supported by gnokii)\n");
704 break;
705 case 1:
706 dprintf("RLE8 (not supported by gnokii)\n");
707 return GN_ERR_WRONGDATAFORMAT;
708 case 2:
709 dprintf("RLE4 (not supported by gnokii)\n");
710 return GN_ERR_WRONGDATAFORMAT;
711 default:
712 dprintf("unknown compression type (not supported by gnokii)\n");
713 return GN_ERR_WRONGDATAFORMAT;
714 }
715
716 pos = buffer[10] - 34;
717 /* the rest of the header (if exists) and the color palette */
718 if (fread(buffer, 1, pos, file) != pos)
719 return GN_ERR_WRONGDATAFORMAT;
720
721 dprintf("First color in BMP file: %i %i %i ", buffer[pos-8], buffer[pos-7], buffer[pos-6]);
722 if (buffer[pos-8] == 0x00 && buffer[pos-7] == 0x00 && buffer[pos-6] == 0x00) dprintf("(black)");
723 if (buffer[pos-8] == 0xff && buffer[pos-7] == 0xff && buffer[pos-6] == 0xff) dprintf("(white)");
724 if (buffer[pos-8] == 0x66 && buffer[pos-7] == 0xbb && buffer[pos-6] == 0x66) dprintf("(green)");
725 dprintf("\n");
726
727 dprintf("Second color in BMP file: %i %i %i ", buffer[pos-4], buffer[pos-3], buffer[pos-2]);
728 if (buffer[pos-4] == 0x00 && buffer[pos-3] == 0x00 && buffer[pos-2] == 0x00) dprintf("(black)");
729 if (buffer[pos-4] == 0xff && buffer[pos-3] == 0xff && buffer[pos-2] == 0xff) dprintf("(white)");
730 if (buffer[pos-4] == 0x66 && buffer[pos-3] == 0xbb && buffer[pos-2] == 0x66) dprintf("(green)");
731 dprintf("\n");
732
733 first_black = false;
734 if (buffer[pos-8] == 0 && buffer[pos-7] == 0 && buffer[pos-6] == 0) first_black = true;
735
736 sizeimage = 0;
737 pos = 7;
738 for (y = h-1; y >= 0; y--) { /* the lines are written from the last one to the first one */
739 i = 1;
740 for (x = 0; x < w; x++) {
741 if (pos == 7) { /* new byte ! */
742 if (fread(buffer, 1, 1, file) != 1)
743 return GN_ERR_WRONGDATAFORMAT;
744 sizeimage++;
745 i++;
746 if (i == 5) i = 1; /* each line is written in multiples of 4 bytes */
747 }
748 if (x <= bitmap->width && y <= bitmap->height) { /* we have top left corner ! */
749 if (first_black) {
750 if ((buffer[0] & (1 << pos)) <= 0) gn_bmp_point_set(bitmap, x, y);
751 } else {
752 if ((buffer[0] & (1 << pos)) > 0) gn_bmp_point_set(bitmap, x, y);
753 }
754 }
755 pos--;
756 if (pos < 0) pos = 7; /* going to the new byte */
757 }
758 pos = 7; /* going to the new byte */
759 if (i != 1) {
760 while (i != 5) { /* each line is written in multiples of 4 bytes */
761 if (fread(buffer, 1, 1, file) != 1)
762 return GN_ERR_WRONGDATAFORMAT;
763 sizeimage++;
764 i++;
765 }
766 }
767 }
768 dprintf("Data size in BMP file: %i\n", sizeimage);
769 return GN_ERR_NONE;
770 }
771
file_nol_load(FILE * file,gn_bmp * bitmap,gn_phone * info)772 gn_error file_nol_load(FILE *file, gn_bmp *bitmap, gn_phone *info)
773 {
774 unsigned char buffer[GN_BMP_MAX_SIZE + 20];
775 int i, j;
776
777 if (fread(buffer, 1, 20, file) != 20)
778 return GN_ERR_FAILED;
779 snprintf(bitmap->netcode, sizeof(bitmap->netcode), "%d %02d", buffer[6] + 256 * buffer[7], buffer[8]);
780
781 bitmap->width = buffer[10];
782 bitmap->height = buffer[12];
783 bitmap->type = GN_BMP_OperatorLogo;
784 bitmap->size = ceiling_to_octet(bitmap->height * bitmap->width);
785
786 if (((bitmap->height != 14) || (bitmap->width != 72)) && /* standard size */
787 ((bitmap->height != 21) || (bitmap->width != 78)) && /* standard size */
788 (!info || (bitmap->height != info->operator_logo_height) || (bitmap->width != info->operator_logo_width))) {
789 dprintf("Invalid Image Size (%dx%d).\n", bitmap->width, bitmap->height);
790 return GN_ERR_INVALIDSIZE;
791 }
792
793 for (i = 0; i < bitmap->size; i++) {
794 if (fread(buffer, 1, 8, file) == 8) {
795 bitmap->bitmap[i] = 0;
796 for (j = 7; j >= 0; j--)
797 if (buffer[7-j] == '1')
798 bitmap->bitmap[i] |= (1 << j);
799 } else {
800 dprintf("too short\n");
801 return GN_ERR_INVALIDSIZE;
802 }
803 }
804
805 /* Some programs writes here fileinfo */
806 if (fread(buffer, 1, 1, file) == 1) {
807 dprintf("Fileinfo: %c", buffer[0]);
808 while (fread(buffer, 1, 1, file) == 1) {
809 if (buffer[0] != 0x0A) dprintf("%c", buffer[0]);
810 }
811 dprintf("\n");
812 }
813 return GN_ERR_NONE;
814 }
815
file_ngg_load(FILE * file,gn_bmp * bitmap,gn_phone * info)816 gn_error file_ngg_load(FILE *file, gn_bmp *bitmap, gn_phone *info)
817 {
818 unsigned char buffer[2000];
819 int i, j;
820
821 bitmap->type = GN_BMP_CallerLogo;
822
823 if (fread(buffer, 1, 16, file) != 16)
824 return GN_ERR_FAILED;
825 bitmap->width = buffer[6];
826 bitmap->height = buffer[8];
827 bitmap->size = bitmap->height * bitmap->width / 8;
828
829 if (((bitmap->height != 14) || (bitmap->width != 72)) && /* standard size */
830 ((bitmap->height != 21) || (bitmap->width != 78)) && /* standard size */
831 (!info || (bitmap->height != info->operator_logo_height) || (bitmap->width != info->operator_logo_width))) {
832 dprintf("Invalid Image Size (%dx%d).\n", bitmap->width, bitmap->height);
833 return GN_ERR_INVALIDSIZE;
834 }
835
836 for (i = 0; i < bitmap->size; i++) {
837 if (fread(buffer, 1, 8, file) == 8){
838 bitmap->bitmap[i] = 0;
839 for (j = 7; j >= 0;j--)
840 if (buffer[7-j] == '1')
841 bitmap->bitmap[i] |= (1 << j);
842 } else {
843 return GN_ERR_INVALIDSIZE;
844 }
845 }
846
847 /* Some programs writes here fileinfo */
848 if (fread(buffer, 1, 1, file) == 1) {
849 dprintf("Fileinfo: %c",buffer[0]);
850 while (fread(buffer, 1, 1, file) == 1) {
851 if (buffer[0] != 0x0A) dprintf("%c", buffer[0]);
852 }
853 dprintf("\n");
854 }
855 return GN_ERR_NONE;
856 }
857
file_nsl_load(FILE * file,gn_bmp * bitmap)858 gn_error file_nsl_load(FILE *file, gn_bmp *bitmap)
859 {
860 unsigned char block[6], buffer[870];
861 int block_size, count;
862
863 bitmap->size = 0;
864
865 while (fread(block, 1, 6, file) == 6) {
866 block_size = block[4] * 256 + block[5];
867 dprintf("Block %c%c%c%c, size %i\n", block[0], block[1], block[2], block[3], block_size);
868 if (!strncmp(block, "FORM", 4)) {
869 dprintf(" File ID\n");
870 } else {
871 if (block_size > 864) return GN_ERR_WRONGDATAFORMAT;
872
873 if (block_size != 0) {
874
875 count = fread(buffer, 1, block_size, file);
876 buffer[count] = 0;
877
878 if (!strncmp(block, "VERS", 4)) dprintf(" File saved by: %s\n", buffer);
879 if (!strncmp(block, "MODL", 4)) dprintf(" Logo saved from: %s\n", buffer);
880 if (!strncmp(block, "COMM", 4)) dprintf(" Phone was connected to COM port: %s\n", buffer);
881
882 if (!strncmp(block, "NSLD", 4)) {
883 bitmap->size = block[4] * 256 + block[5];
884 switch (bitmap->size) {
885 case 864: /* 6510/7110 startup logo */
886 bitmap->height = 65;
887 bitmap->width = 96;
888 break;
889 case 504: /* 6110 startup logo */
890 bitmap->height = 48;
891 bitmap->width = 84;
892 break;
893 case 768: /* 6210 */
894 bitmap->height = 60;
895 bitmap->width = 96;
896 break;
897 default:
898 dprintf("Unknown startup logo!\n");
899 return GN_ERR_WRONGDATAFORMAT;
900 }
901 bitmap->type = GN_BMP_StartupLogo;
902 memcpy(bitmap->bitmap, buffer, bitmap->size);
903 dprintf(" Startup logo (size %i)\n", block_size);
904 }
905 }
906 }
907 }
908 if (bitmap->size == 0) return GN_ERR_INVALIDSIZE;
909 return GN_ERR_NONE;
910 }
911
file_nlm_load(FILE * file,gn_bmp * bitmap)912 gn_error file_nlm_load(FILE *file, gn_bmp *bitmap)
913 {
914 unsigned char buffer[84*48];
915 int pos, pos2, x, y;
916 div_t division;
917
918 if (fread(buffer, 1, 5, file) != 5)
919 return GN_ERR_FAILED;
920 if (fread(buffer, 1, 1, file) != 1)
921 return GN_ERR_FAILED;
922
923 switch (buffer[0]) {
924 case 0x00:
925 bitmap->type = GN_BMP_OperatorLogo;
926 break;
927 case 0x01:
928 bitmap->type = GN_BMP_CallerLogo;
929 break;
930 case 0x02:
931 bitmap->type = GN_BMP_StartupLogo;
932 break;
933 case 0x03:
934 bitmap->type = GN_BMP_PictureMessage;
935 break;
936 default:
937 return GN_ERR_WRONGDATAFORMAT;
938 }
939
940 if (fread(buffer, 1, 4, file) != 4)
941 return GN_ERR_FAILED;
942 bitmap->width = buffer[1];
943 bitmap->height = buffer[2];
944 bitmap->size = bitmap->width * bitmap->height / 8;
945
946 division = div(bitmap->width, 8);
947 if (division.rem != 0) division.quot++; /* For startup logos */
948
949 if (fread(buffer, 1, (division.quot * bitmap->height), file) != (division.quot * bitmap->height))
950 return GN_ERR_INVALIDSIZE;
951
952 gn_bmp_clear(bitmap);
953
954 pos = 0; pos2 = 7;
955 for (y = 0; y < bitmap->height; y++) {
956 for (x = 0; x < bitmap->width; x++) {
957 if ((buffer[pos] & (1 << pos2)) > 0) gn_bmp_point_set(bitmap, x, y);
958 pos2--;
959 if (pos2 < 0) {pos2 = 7; pos++;} /*going to new byte */
960 }
961 if (pos2 != 7) {pos2 = 7; pos++;} /* for startup logos-new line means new byte */
962 }
963 return GN_ERR_NONE;
964 }
965
file_ota_load(FILE * file,gn_bmp * bitmap,gn_phone * info)966 gn_error file_ota_load(FILE *file, gn_bmp *bitmap, gn_phone *info)
967 {
968 char buffer[4];
969
970 /* We could check for extended info here - indicated by the 7th bit being set in the first byte */
971 if (fread(buffer, 1, 4, file) != 4)
972 return GN_ERR_FAILED;
973
974 bitmap->width = buffer[1];
975 bitmap->height = buffer[2];
976 bitmap->size = bitmap->width * bitmap->height / 8;
977
978 if (((bitmap->height == 48) && (bitmap->width == 84)) || /* standard size */
979 ((bitmap->height == 60) && (bitmap->width == 96)) || /* standard size */
980 (info && ((bitmap->height == info->startup_logo_height) && (bitmap->width == info->startup_logo_width)))) {
981 bitmap->type = GN_BMP_StartupLogo;
982 } else if (((bitmap->height == 14) && (bitmap->width == 72)) || /* standard size */
983 (info && ((bitmap->height == info->caller_logo_height) && (bitmap->width == info->caller_logo_width)))) {
984 bitmap->type = GN_BMP_CallerLogo;
985 } else {
986 dprintf("Invalid Image Size (%dx%d).\n", bitmap->width, bitmap->height);
987 return GN_ERR_INVALIDSIZE;
988 }
989 if (fread(bitmap->bitmap, 1, bitmap->size,file) != bitmap->size)
990 return GN_ERR_INVALIDSIZE;
991 return GN_ERR_NONE;
992 }
993
994 /* This overwrites an existing file - so this must be checked before calling */
gn_file_bitmap_save(char * filename,gn_bmp * bitmap,gn_phone * info)995 gn_error gn_file_bitmap_save(char *filename, gn_bmp *bitmap, gn_phone *info)
996 {
997 FILE *file;
998 bool done = false;
999
1000 /* XPMs are a bit messy because we have to pass it the filename */
1001
1002 #ifdef XPM
1003 if (strstr(filename, ".xpm")) {
1004 file_xpm_save(filename, bitmap);
1005 } else {
1006 #endif
1007
1008 file = fopen(filename, "wb");
1009
1010 if (!file) return GN_ERR_FAILED;
1011
1012 if (strstr(filename, ".nlm")) {
1013 file_nlm_save(file, bitmap);
1014 done = true;
1015 }
1016 if (strstr(filename, ".ngg")) {
1017 file_ngg_save(file, bitmap, info);
1018 done = true;
1019 }
1020 if (strstr(filename, ".nsl")) {
1021 file_nsl_save(file, bitmap, info);
1022 done = true;
1023 }
1024 if (strstr(filename, ".otb")) {
1025 file_ota_save(file, bitmap);
1026 done = true;
1027 }
1028 if (strstr(filename, ".nol")) {
1029 file_nol_save(file, bitmap, info);
1030 done = true;
1031 }
1032 if (strstr(filename, ".bmp") ||
1033 strstr(filename, ".ggp") ||
1034 strstr(filename, ".i61")) {
1035 file_bmp_save(file, bitmap);
1036 done = true;
1037 }
1038
1039 if (!done) {
1040 switch (bitmap->type) {
1041 case GN_BMP_CallerLogo:
1042 file_ngg_save(file, bitmap, info);
1043 break;
1044 case GN_BMP_OperatorLogo:
1045 case GN_BMP_NewOperatorLogo:
1046 file_nol_save(file, bitmap, info);
1047 break;
1048 case GN_BMP_StartupLogo:
1049 file_nsl_save(file, bitmap, info);
1050 break;
1051 case GN_BMP_PictureMessage:
1052 file_nlm_save(file, bitmap);
1053 break;
1054 case GN_BMP_WelcomeNoteText:
1055 case GN_BMP_DealerNoteText:
1056 case GN_BMP_None:
1057 default:
1058 break;
1059 }
1060 }
1061 fclose(file);
1062 #ifdef XPM
1063 }
1064 #endif
1065 return GN_ERR_NONE;
1066 }
1067
1068 #ifdef XPM
file_xpm_save(char * filename,gn_bmp * bitmap)1069 void file_xpm_save(char *filename, gn_bmp *bitmap)
1070 {
1071 XpmColor colors[2] = {{".","c","#000000","#000000","#000000","#000000"},
1072 {"#","c","#ffffff","#ffffff","#ffffff","#ffffff"}};
1073 XpmImage image;
1074 unsigned int data[6240];
1075 int x, y;
1076
1077 image.height = bitmap->height;
1078 image.width = bitmap->width;
1079 image.cpp = 1;
1080 image.ncolors = 2;
1081 image.colorTable = colors;
1082 image.data = data;
1083
1084 for (y = 0; y < image.height; y++) {
1085 for (x = 0; x < image.width; x++)
1086 if (gn_bmp_point(bitmap, x, y))
1087 data[y * image.width + x] = 0;
1088 else
1089 data[y * image.width + x] = 1;
1090 }
1091
1092 XpmWriteFileFromXpmImage(filename, &image, NULL);
1093 }
1094 #endif
1095
1096 /* Based on the article from the Polish Magazine "Bajtek" 11/92 */
1097 /* Marcin-Wiacek@Topnet.PL */
file_bmp_save(FILE * file,gn_bmp * bitmap)1098 void file_bmp_save(FILE *file, gn_bmp *bitmap)
1099 {
1100 int x, y, pos, i, sizeimage;
1101 unsigned char buffer[1];
1102 div_t division;
1103
1104 char header[] = {
1105 /* 1st header */ 'B', 'M', /* BMP file ID */
1106 0x00, 0x00, 0x00, 0x00, /* Size of file */
1107 0x00, 0x00, /* Reserved for future use */
1108 0x00, 0x00, /* Reserved for future use */
1109 62, 0x00, 0x00, 0x00, /* Offset for image data */
1110
1111 /* 2nd header */ 40, 0x00, 0x00, 0x00, /* Length of this part of header */
1112 0x00, 0x00, 0x00, 0x00, /* Width of image */
1113 0x00, 0x00, 0x00, 0x00, /* Height of image */
1114 0x01, 0x00, /* How many planes in target device */
1115 0x01, 0x00, /* How many colors in image. 1 means 2^1=2 colors */
1116 0x00, 0x00, 0x00, 0x00, /* Type of compression. 0 means no compression */
1117 /* Sometimes */ 0x00, 0x00, 0x00, 0x00, /* Size of part with image data */
1118 /* ttttttt...*/ 0xE8, 0x03, 0x00, 0x00, /* XPelsPerMeter */
1119 /*hhiiiiissss*/ 0xE8, 0x03, 0x00, 0x00, /* YPelsPerMeter */
1120 /* part of the
1121 header */ 0x02, 0x00, 0x00, 0x00, /* How many colors from palette is used */
1122 /* doesn't
1123 exist */ 0x00, 0x00, 0x00, 0x00, /* How many colors from palette is required to display image. 0 means all */
1124
1125 /* Color
1126 palette */ 0xFF, 0xFF, 0xFF, /* First color in palette in Blue, Green, Red. Here white */
1127 0x00, /* Each color in palette is end by 4'th byte */
1128 0x00, 0x00, 0x00, /* Second color in palette in Blue, Green, Red. Here black */
1129 0x00}; /* Each color in palette is end by 4'th byte */
1130
1131 header[22] = bitmap->height;
1132 header[18] = bitmap->width;
1133
1134 pos = 7;
1135 sizeimage = 0;
1136 for (y = bitmap->height - 1; y >= 0; y--) { //lines are written from the last to the first
1137 i = 1;
1138 for (x = 0; x < bitmap->width; x++) {
1139 if (pos == 7) { //new byte !
1140 sizeimage++;
1141 i++;
1142 if (i == 5) i = 1; //each line is written in multiples of 4 bytes
1143 }
1144 pos--;
1145 if (pos < 0) pos = 7; //going to new byte
1146 }
1147 pos = 7; //going to new byte
1148 while (i != 5) { //each line is written in multiples of 4 bytes
1149 sizeimage++;
1150 i++;
1151 }
1152 }
1153 dprintf("Data size in BMP file: %i\n", sizeimage);
1154 division = div(sizeimage, 256);
1155 header[35] = division.quot;
1156 header[34] = sizeimage - (division.quot * 256);
1157
1158 sizeimage = sizeimage + sizeof(header);
1159 dprintf("Size of BMP file: %i\n", sizeimage);
1160 division = div(sizeimage, 256);
1161 header[3] = division.quot;
1162 header[2] = sizeimage - (division.quot * 256);
1163
1164 fwrite(header, 1, sizeof(header), file);
1165
1166 pos = 7;
1167 for (y = bitmap->height - 1; y >= 0; y--) { //lines are written from the last to the first
1168 i = 1;
1169 for (x = 0; x < bitmap->width; x++) {
1170 if (pos == 7) { //new byte !
1171 if (x != 0) fwrite(buffer, 1, sizeof(buffer), file);
1172 i++;
1173 if(i == 5) i = 1; //each line is written in multiples of 4 bytes
1174 buffer[0] = 0;
1175 }
1176 if (gn_bmp_point(bitmap, x, y)) buffer[0] |= (1 << pos);
1177 pos--;
1178 if (pos < 0) pos = 7; //going to new byte
1179 }
1180 pos = 7; //going to new byte
1181 fwrite(buffer, 1, sizeof(buffer), file);
1182 while (i != 5) { //each line is written in multiples of 4 bytes
1183 buffer[0] = 0;
1184 fwrite(buffer, 1, sizeof(buffer), file);
1185 i++;
1186 }
1187 }
1188 }
1189
file_ngg_save(FILE * file,gn_bmp * bitmap,gn_phone * info)1190 void file_ngg_save(FILE *file, gn_bmp *bitmap, gn_phone *info)
1191 {
1192
1193 char header[] = {'N', 'G', 'G', 0x00, 0x01, 0x00,
1194 0x00, 0x00, /* Width */
1195 0x00, 0x00, /* Height */
1196 0x01, 0x00, 0x01, 0x00,
1197 0x00, 0x00 /* Unknown.Can't be checksum - for */
1198 /* the same logo files can be different */
1199 };
1200
1201 char buffer[8];
1202 int i, j;
1203
1204 gn_bmp_resize(bitmap, GN_BMP_CallerLogo, info);
1205
1206 header[6] = bitmap->width;
1207 header[8] = bitmap->height;
1208
1209 fwrite(header, 1, sizeof(header), file);
1210
1211 for (i = 0; i < bitmap->size; i++) {
1212 for (j = 7; j >= 0;j--)
1213 if ((bitmap->bitmap[i] & (1 << j)) > 0) {
1214 buffer[7-j] = '1';
1215 } else {
1216 buffer[7-j] = '0';
1217 }
1218 fwrite(buffer, 1, 8, file);
1219 }
1220 }
1221
file_nol_save(FILE * file,gn_bmp * bitmap,gn_phone * info)1222 void file_nol_save(FILE *file, gn_bmp *bitmap, gn_phone *info)
1223 {
1224
1225 char header[] = {'N','O','L',0x00,0x01,0x00,
1226 0x00,0x00, /* MCC */
1227 0x00,0x00, /* MNC */
1228 0x00,0x00, /* Width */
1229 0x00,0x00, /* Height */
1230 0x01,0x00,0x01,0x00,
1231 0x00, /* Unknown.Can't be checksum - for */
1232 /* the same logo files can be different */
1233 0x00};
1234 char buffer[8];
1235 int i, j, country, net;
1236
1237 gn_bmp_resize(bitmap, GN_BMP_OperatorLogo, info);
1238
1239 sscanf(bitmap->netcode, "%d %d", &country, &net);
1240
1241 header[6] = country % 256;
1242 header[7] = country / 256;
1243 header[8] = net % 256;
1244 header[9] = net / 256;
1245 header[10] = bitmap->width;
1246 header[12] = bitmap->height;
1247
1248 fwrite(header, 1, sizeof(header), file);
1249
1250 for (i = 0; i < bitmap->size; i++) {
1251 for (j = 7; j >= 0; j--)
1252 if ((bitmap->bitmap[i] & (1 << j)) > 0) {
1253 buffer[7-j] = '1';
1254 } else {
1255 buffer[7-j] = '0';
1256 }
1257 fwrite(buffer, 1, 8, file);
1258 }
1259 }
1260
file_nsl_save(FILE * file,gn_bmp * bitmap,gn_phone * info)1261 void file_nsl_save(FILE *file, gn_bmp *bitmap, gn_phone *info)
1262 {
1263
1264 u8 header[] = {'F','O','R','M', 0x01,0xFE, /* File ID block, size 1*256+0xFE=510*/
1265 'N','S','L','D', 0x01,0xF8}; /* Startup Logo block, size 1*256+0xF8=504*/
1266
1267 gn_bmp_resize(bitmap, GN_BMP_StartupLogo, info);
1268
1269 header[4] = (bitmap->size + 6) / 256;
1270 header[5] = (bitmap->size + 6) % 256;
1271 header[10] = bitmap->size / 256;
1272 header[11] = bitmap->size % 256;
1273 fwrite(header, 1, sizeof(header), file);
1274
1275 fwrite(bitmap->bitmap, 1, bitmap->size, file);
1276 }
1277
file_ota_save(FILE * file,gn_bmp * bitmap)1278 void file_ota_save(FILE *file, gn_bmp *bitmap)
1279 {
1280 char header[] = {0x01,
1281 0x00, /* Width */
1282 0x00, /* Height */
1283 0x01};
1284
1285 header[1] = bitmap->width;
1286 header[2] = bitmap->height;
1287
1288 fwrite(header, 1, sizeof(header), file);
1289
1290 fwrite(bitmap->bitmap, 1, bitmap->size, file);
1291 }
1292
file_nlm_save(FILE * file,gn_bmp * bitmap)1293 void file_nlm_save(FILE *file, gn_bmp *bitmap)
1294 {
1295 char header[] = {'N','L','M', /* Nokia Logo Manager file ID. */
1296 0x20,
1297 0x01,
1298 0x00, /* 0x00 (OP), 0x01 (CLI), 0x02 (Startup), 0x03 (Picture)*/
1299 0x00,
1300 0x00, /* Width. */
1301 0x00, /* Height. */
1302 0x01};
1303
1304 unsigned char buffer[17 * 48];
1305 int x, y, pos, pos2;
1306 div_t division;
1307
1308 switch (bitmap->type) {
1309 case GN_BMP_OperatorLogo:
1310 case GN_BMP_NewOperatorLogo:
1311 header[5] = 0x00;
1312 break;
1313 case GN_BMP_CallerLogo:
1314 header[5] = 0x01;
1315 break;
1316 case GN_BMP_StartupLogo:
1317 header[5] = 0x02;
1318 break;
1319 case GN_BMP_PictureMessage:
1320 header[5] = 0x03;
1321 break;
1322 case GN_BMP_WelcomeNoteText:
1323 case GN_BMP_DealerNoteText:
1324 case GN_BMP_None:
1325 default:
1326 break;
1327 }
1328
1329 header[7] = bitmap->width;
1330 header[8] = bitmap->height;
1331
1332 pos = 0; pos2 = 7;
1333 for (y = 0; y < bitmap->height; y++) {
1334 for (x = 0; x < bitmap->width; x++) {
1335 if (pos2 == 7) buffer[pos] = 0;
1336 if (gn_bmp_point(bitmap, x, y)) buffer[pos] |= (1 << pos2);
1337 pos2--;
1338 if (pos2 < 0) {pos2 = 7; pos++;} /* going to new line */
1339 }
1340 if (pos2 != 7) {pos2 = 7; pos++;} /* for startup logos - new line with new byte */
1341 }
1342
1343 division = div(bitmap->width, 8);
1344 if (division.rem != 0) division.quot++; /* For startup logos */
1345
1346 fwrite(header, 1, sizeof(header), file);
1347 fwrite(buffer, 1, (division.quot * bitmap->height), file);
1348 }
1349
gn_file_bitmap_show(char * filename)1350 gn_error gn_file_bitmap_show(char *filename)
1351 {
1352 int i, j;
1353 gn_bmp bitmap;
1354 gn_error error;
1355
1356 error = gn_file_bitmap_read(filename, &bitmap, NULL);
1357 if (error != GN_ERR_NONE)
1358 return error;
1359
1360 for (i = 0; i < bitmap.height; i++) {
1361 for (j = 0; j < bitmap.width; j++) {
1362 fprintf(stdout, "%c", gn_bmp_point(&bitmap, j, i) ? '#' : ' ');
1363 }
1364 fprintf(stdout, "\n");
1365 }
1366
1367 return GN_ERR_NONE;
1368 }
1369
1370 /* returns number of the characters before the next delimiter, including it */
get_next_token(char * src,int delim)1371 static int get_next_token(char *src, int delim)
1372 {
1373 int i, len = strlen(src);
1374 int slash_state = 0;
1375
1376 for (i = 0; i < len; i++) {
1377 switch (src[i]) {
1378 case '\\':
1379 if (slash_state) {
1380 slash_state = 0;
1381 } else {
1382 slash_state = 1;
1383 }
1384 break;
1385 case ';':
1386 if (slash_state)
1387 slash_state = 0;
1388 else
1389 return i + 1;
1390 break;
1391 default:
1392 if (slash_state)
1393 slash_state = 0;
1394 break;
1395 }
1396 }
1397 return i + 1;
1398 }
1399
1400 #define BUG(x) \
1401 do { \
1402 if (x) { \
1403 return GN_ERR_WRONGDATAFORMAT; \
1404 } \
1405 } while (0)
1406
1407 #define MAX_INPUT_LINE_LEN 512
1408
1409 #define GET_NEXT_TOKEN() o = get_next_token(line + offset, ';')
1410 #define STORE_TOKEN(a) strip_slashes(a, line + offset, sizeof(a) - 1, o - 1)
1411
local_atoi(char * str,int len)1412 inline int local_atoi(char *str, int len)
1413 {
1414 int retval;
1415 char *aux = strndup(str, len);
1416 retval = atoi(aux);
1417 free(aux);
1418 return retval;
1419 }
1420
gn_file_phonebook_raw_parse(gn_phonebook_entry * entry,char * line)1421 GNOKII_API gn_error gn_file_phonebook_raw_parse(gn_phonebook_entry *entry, char *line)
1422 {
1423 char memory_type_char[3];
1424 char number[GN_PHONEBOOK_NUMBER_MAX_LENGTH];
1425 int length, o, offset = 0;
1426 gn_error error = GN_ERR_NONE;
1427
1428 memset(entry, 0, sizeof(gn_phonebook_entry));
1429
1430 length = strlen(line);
1431 entry->empty = true;
1432 memory_type_char[2] = 0;
1433
1434 GET_NEXT_TOKEN();
1435 switch (o) {
1436 case 0:
1437 return GN_ERR_WRONGDATAFORMAT;
1438 case 1:
1439 /* empty name: this is a request to delete the entry */
1440 break;
1441 default:
1442 break;
1443 }
1444 STORE_TOKEN(entry->name);
1445 offset += o;
1446
1447 BUG(offset >= length);
1448
1449 GET_NEXT_TOKEN();
1450 switch (o) {
1451 case 0:
1452 return GN_ERR_WRONGDATAFORMAT;
1453 default:
1454 break;
1455 }
1456 STORE_TOKEN(entry->number);
1457 offset += o;
1458
1459 BUG(offset >= length);
1460
1461 GET_NEXT_TOKEN();
1462 switch (o) {
1463 case 3:
1464 break;
1465 default:
1466 return GN_ERR_WRONGDATAFORMAT;
1467 }
1468 STORE_TOKEN(memory_type_char);
1469 offset += o;
1470
1471 BUG(offset >= length);
1472
1473 entry->memory_type = gn_str2memory_type(memory_type_char);
1474 /* We can store addressbook entries only in ME or SM (or ON on some models) */
1475 if (entry->memory_type != GN_MT_ME &&
1476 entry->memory_type != GN_MT_SM &&
1477 entry->memory_type != GN_MT_ON) {
1478 return GN_ERR_INVALIDMEMORYTYPE;
1479 }
1480
1481 BUG(offset >= length);
1482
1483 memset(number, 0, sizeof(number));
1484 GET_NEXT_TOKEN();
1485 STORE_TOKEN(number);
1486 switch (o) {
1487 case 0:
1488 return GN_ERR_WRONGDATAFORMAT;
1489 case 1:
1490 entry->location = 0;
1491 break;
1492 default:
1493 entry->location = atoi(number);
1494 break;
1495 }
1496 offset += o;
1497
1498 BUG(offset >= length);
1499
1500 memset(number, 0, sizeof(number));
1501 GET_NEXT_TOKEN();
1502 STORE_TOKEN(number);
1503 switch (o) {
1504 case 0:
1505 return GN_ERR_WRONGDATAFORMAT;
1506 case 1:
1507 entry->caller_group = 0;
1508 break;
1509 default:
1510 entry->caller_group = atoi(number);
1511 break;
1512 }
1513 offset += o;
1514
1515 entry->empty = false;
1516
1517 for (entry->subentries_count = 0; offset < length; entry->subentries_count++) {
1518 if (entry->subentries_count == GN_PHONEBOOK_SUBENTRIES_MAX_NUMBER) {
1519 fprintf(stderr, _("Formatting error: too many subentries\n"));
1520 error = GN_ERR_WRONGDATAFORMAT;
1521 goto endloop;
1522 }
1523 memset(number, 0, sizeof(number));
1524 GET_NEXT_TOKEN();
1525 STORE_TOKEN(number);
1526 switch (o) {
1527 case 0:
1528 fprintf(stderr, _("Formatting error: unknown error while reading subentry type\n"));
1529 error = GN_ERR_WRONGDATAFORMAT;
1530 goto endloop;
1531 case 1:
1532 fprintf(stderr, _("Formatting error: empty entry type\n"));
1533 entry->subentries[entry->subentries_count].entry_type = 0;
1534 break;
1535 default:
1536 entry->subentries[entry->subentries_count].entry_type = atoi(number);
1537 break;
1538 }
1539 offset += o;
1540
1541 if (offset > length) {
1542 fprintf(stderr, _("Formatting error: subentry has only entry type field\n"));
1543 break;
1544 }
1545
1546 memset(number, 0, sizeof(number));
1547 GET_NEXT_TOKEN();
1548 STORE_TOKEN(number);
1549 switch (o) {
1550 case 0:
1551 fprintf(stderr, _("Formatting error: unknown error while reading subentry number type\n"));
1552 error = GN_ERR_WRONGDATAFORMAT;
1553 goto endloop;
1554 case 1:
1555 fprintf(stderr, _("Formatting error: empty number type\n"));
1556 entry->subentries[entry->subentries_count].number_type = 0;
1557 /* Number type is required with Number entry type */
1558 if (entry->subentries[entry->subentries_count].entry_type == GN_PHONEBOOK_ENTRY_Number) {
1559 error = GN_ERR_WRONGDATAFORMAT;
1560 goto endloop;
1561 }
1562 break;
1563 default:
1564 entry->subentries[entry->subentries_count].number_type = atoi(number);
1565 break;
1566 }
1567 offset += o;
1568
1569 if (offset > length) {
1570 fprintf(stderr, _("Formatting error: subentry has only entry and number type fields\n"));
1571 break;
1572 }
1573
1574 memset(number, 0, sizeof(number));
1575 GET_NEXT_TOKEN();
1576 STORE_TOKEN(number);
1577 switch (o) {
1578 case 0:
1579 fprintf(stderr, _("Formatting error: unknown error while reading subentry id\n"));
1580 error = GN_ERR_WRONGDATAFORMAT;
1581 goto endloop;
1582 case 1:
1583 fprintf(stderr, _("Formatting error: empty id\n"));
1584 entry->subentries[entry->subentries_count].id = 0;
1585 break;
1586 default:
1587 entry->subentries[entry->subentries_count].id = atoi(number);
1588 break;
1589 }
1590 offset += o;
1591
1592 if (offset > length) {
1593 fprintf(stderr, _("Formatting error: subentry has only entry and number type fields\n"));
1594 break;
1595 }
1596
1597 GET_NEXT_TOKEN();
1598 switch (entry->subentries[entry->subentries_count].entry_type) {
1599 case GN_PHONEBOOK_ENTRY_Date:
1600 case GN_PHONEBOOK_ENTRY_Birthday:
1601 entry->subentries[entry->subentries_count].data.date.year = local_atoi(line + offset, 4);
1602 entry->subentries[entry->subentries_count].data.date.month = local_atoi(line + offset + 4, 2);
1603 entry->subentries[entry->subentries_count].data.date.day = local_atoi(line + offset + 6, 2);
1604 entry->subentries[entry->subentries_count].data.date.hour = local_atoi(line + offset + 8, 2);
1605 entry->subentries[entry->subentries_count].data.date.minute = local_atoi(line + offset + 10, 2);
1606 entry->subentries[entry->subentries_count].data.date.second = local_atoi(line + offset + 12, 2);
1607 break;
1608 case GN_PHONEBOOK_ENTRY_ExtGroup:
1609 entry->subentries[entry->subentries_count].data.id = local_atoi(line + offset, 3);
1610 break;
1611 default:
1612 STORE_TOKEN(entry->subentries[entry->subentries_count].data.number);
1613 break;
1614 }
1615 switch (o) {
1616 case 0:
1617 fprintf(stderr, _("Formatting error: unknown error while reading subentry contents\n"));
1618 error = GN_ERR_WRONGDATAFORMAT;
1619 goto endloop;
1620 case 1:
1621 fprintf(stderr, _("Formatting error: empty subentry contents\n"));
1622 break;
1623 default:
1624 break;
1625 }
1626 offset += o;
1627 }
1628
1629 endloop:
1630 /* Fake subentry: this is to send other exports (like from 6110) to 7110 */
1631 if (!entry->subentries_count) {
1632 entry->subentries[entry->subentries_count].entry_type = GN_PHONEBOOK_ENTRY_Number;
1633 entry->subentries[entry->subentries_count].number_type = GN_PHONEBOOK_NUMBER_General;
1634 entry->subentries[entry->subentries_count].id = 2;
1635 snprintf(entry->subentries[entry->subentries_count].data.number,
1636 sizeof(entry->subentries[entry->subentries_count].data.number), "%s", entry->number);
1637 entry->subentries_count = 1;
1638 }
1639 return error;
1640 }
1641
gn_file_phonebook_raw_write(FILE * f,gn_phonebook_entry * entry,char * memory_type_string)1642 GNOKII_API gn_error gn_file_phonebook_raw_write(FILE *f, gn_phonebook_entry *entry, char *memory_type_string)
1643 {
1644 char escaped_name[2 * GN_PHONEBOOK_NAME_MAX_LENGTH];
1645 int i;
1646
1647 add_slashes(escaped_name, entry->name, sizeof(escaped_name), strlen(entry->name));
1648 fprintf(f, "%s;%s;%s;%d;%d", escaped_name,
1649 entry->number, memory_type_string,
1650 entry->location, entry->caller_group);
1651 if (entry->person.has_person) {
1652 if (entry->person.honorific_prefixes[0])
1653 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_FormalName,
1654 entry->person.honorific_prefixes);
1655 if (entry->person.given_name[0])
1656 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_FirstName,
1657 entry->person.given_name);
1658 if (entry->person.family_name[0])
1659 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_LastName,
1660 entry->person.family_name);
1661 }
1662 if (entry->address.has_address) {
1663 if (entry->address.post_office_box[0])
1664 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_Postal,
1665 entry->address.post_office_box);
1666 if (entry->address.extended_address[0])
1667 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_ExtendedAddress,
1668 entry->address.extended_address);
1669 if (entry->address.street[0])
1670 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_Street,
1671 entry->address.street);
1672 if (entry->address.city[0])
1673 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_City,
1674 entry->address.city);
1675 if (entry->address.state_province[0])
1676 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_StateProvince,
1677 entry->address.state_province);
1678 if (entry->address.zipcode[0])
1679 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_ZipCode,
1680 entry->address.zipcode);
1681 if (entry->address.country[0])
1682 fprintf(f, ";%d;0;0;%s", GN_PHONEBOOK_ENTRY_Country,
1683 entry->address.country);
1684 }
1685
1686 for (i = 0; i < entry->subentries_count; i++) {
1687 switch (entry->subentries[i].entry_type) {
1688 case GN_PHONEBOOK_ENTRY_Birthday:
1689 case GN_PHONEBOOK_ENTRY_Date:
1690 fprintf(f, ";%d;0;%d;%04u%02u%02u%02u%02u%02u", entry->subentries[i].entry_type, entry->subentries[i].id,
1691 entry->subentries[i].data.date.year, entry->subentries[i].data.date.month, entry->subentries[i].data.date.day,
1692 entry->subentries[i].data.date.hour, entry->subentries[i].data.date.minute, entry->subentries[i].data.date.second);
1693 break;
1694 case GN_PHONEBOOK_ENTRY_ExtGroup:
1695 fprintf(f, ";%d;%d;%d;%03d",
1696 entry->subentries[i].entry_type,
1697 entry->subentries[i].number_type,
1698 entry->subentries[i].id,
1699 entry->subentries[i].data.id);
1700 break;
1701 default:
1702 add_slashes(escaped_name, entry->subentries[i].data.number, sizeof(escaped_name), strlen(entry->subentries[i].data.number));
1703 fprintf(f, ";%d;%d;%d;%s",
1704 entry->subentries[i].entry_type,
1705 entry->subentries[i].number_type,
1706 entry->subentries[i].id,
1707 escaped_name);
1708 break;
1709 }
1710 }
1711 if ((entry->memory_type == GN_MT_MC ||
1712 entry->memory_type == GN_MT_DC ||
1713 entry->memory_type == GN_MT_RC) &&
1714 entry->date.day != 0)
1715 fprintf(f, ";%d;0;0;%04u%02u%02u%02u%02u%02u", GN_PHONEBOOK_ENTRY_Date,
1716 entry->date.year, entry->date.month, entry->date.day, entry->date.hour, entry->date.minute, entry->date.second);
1717 fprintf(f, "\n");
1718 return GN_ERR_NONE;
1719 }
1720