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