1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id$ */
3 
4 /*  libtifiles - file format library, a part of the TiLP project
5  *  Copyright (C) 1999-2006  Romain Lievin
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software Foundation,
19  *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /*
23   This unit contains a TI file independent API
24 */
25 
26 #include <glib.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31 
32 #include "tifiles.h"
33 #include "error.h"
34 #include "files8x.h"
35 #include "files9x.h"
36 #include "filesnsp.h"
37 #include "logging.h"
38 
39 // Whether to print detailed information about FileContent, BackupContent, FlashContent instances throughout their lifecycle.
40 //#define TRACE_CONTENT_INSTANCES
41 
42 /**
43  * tifiles_content_create_regular:
44  * @model: a calculator model (required).
45  *
46  * Allocates a #FileContent structure.
47  *
48  * Return value: the allocated block.
49  **/
tifiles_content_create_regular(CalcModel model)50 TIEXPORT2 FileContent* TICALL tifiles_content_create_regular(CalcModel model)
51 {
52 	FileContent* content = g_malloc0(sizeof(FileContent));
53 
54 	if (content != NULL)
55 	{
56 		if ((unsigned int)model >= CALC_MAX)
57 		{
58 			tifiles_warning("Invalid calculator model");
59 		}
60 		content->model = content->model_dst = model;
61 		strncpy(content->comment, tifiles_comment_set_single(), sizeof(content->comment) - 1);
62 		content->comment[sizeof(content->comment) - 1] = 0;
63 	}
64 
65 #ifdef TRACE_CONTENT_INSTANCES
66 	tifiles_info("tifiles_content_create_regular: %p", content);
67 	tifiles_file_display_regular(content);
68 #endif
69 
70 	return content;
71 }
72 
73 /**
74  * tifiles_content_delete_regular:
75  *
76  * Free the whole content of a #FileContent structure.
77  *
78  * Return value: 0.
79  **/
tifiles_content_delete_regular(FileContent * content)80 TIEXPORT2 int TICALL tifiles_content_delete_regular(FileContent *content)
81 {
82 	unsigned int i;
83 
84 #ifdef TRACE_CONTENT_INSTANCES
85 	tifiles_info("tifiles_content_delete_regular: %p", content);
86 	tifiles_file_display_regular(content);
87 #endif
88 
89 	if (content != NULL)
90 	{
91 		for (i = 0; i < content->num_entries; i++)
92 		{
93 			VarEntry *entry = content->entries[i];
94 
95 			if (entry != NULL)
96 			{
97 				g_free(entry->data);
98 				g_free(entry);
99 			}
100 			else
101 			{
102 				tifiles_critical("tifiles_content_delete_regular(content with NULL entry)");
103 			}
104 		}
105 
106 		g_free(content->entries);
107 		g_free(content);
108 	}
109 	else
110 	{
111 		tifiles_critical("%s(NULL)", __FUNCTION__);
112 	}
113 
114 	return 0;
115 }
116 
117 /**
118  * tifiles_content_dup_regular:
119  *
120  * Allocates and copies a new #FileContent structure.
121  *
122  * Return value: none.
123  **/
tifiles_content_dup_regular(FileContent * content)124 TIEXPORT2 FileContent* TICALL tifiles_content_dup_regular(FileContent *content)
125 {
126 	FileContent *dup = NULL;
127 	unsigned int i;
128 
129 	if (content != NULL)
130 	{
131 		dup = tifiles_content_create_regular(content->model);
132 		if (dup != NULL)
133 		{
134 			memcpy(dup, content, sizeof(FileContent));
135 			dup->entries = tifiles_ve_create_array(content->num_entries);
136 
137 			if (dup->entries != NULL)
138 			{
139 				for (i = 0; i < content->num_entries; i++)
140 				{
141 					dup->entries[i] = tifiles_ve_dup(content->entries[i]);
142 					if (dup->entries[i] == NULL)
143 					{
144 						tifiles_content_delete_regular(dup);
145 						dup = NULL;
146 						break;
147 					}
148 				}
149 			}
150 			else
151 			{
152 				dup->num_entries = 0;
153 				tifiles_content_delete_regular(dup);
154 				dup = NULL;
155 			}
156 		}
157 	}
158 	else
159 	{
160 		tifiles_critical("%s(NULL)", __FUNCTION__);
161 	}
162 
163 #ifdef TRACE_CONTENT_INSTANCES
164 	tifiles_info("tifiles_content_dup_regular: %p", dup);
165 	tifiles_file_display_regular(dup);
166 #endif
167 
168 	return dup;
169 }
170 
171 /**
172  * tifiles_file_read_regular:
173  * @filename: name of single/group file to open.
174  * @content: where to store the file content.
175  *
176  * Load the single/group file into a FileContent structure.
177  *
178  * Structure content must be freed with #tifiles_content_delete_regular when
179  * no longer used. If an error occurs, the structure content is released for you.
180  *
181  * Return value: an error code, 0 otherwise.
182  **/
tifiles_file_read_regular(const char * filename,FileContent * content)183 TIEXPORT2 int tifiles_file_read_regular(const char *filename, FileContent *content)
184 {
185 	if (filename == NULL || content == NULL)
186 	{
187 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
188 		return ERR_INVALID_FILE;
189 	}
190 
191 #if !defined(DISABLE_TI8X)
192 	if (tifiles_calc_is_ti8x(tifiles_file_get_model(filename)))
193 	{
194 		return ti8x_file_read_regular(filename, (Ti8xRegular *)content);
195 	}
196 	else
197 #endif
198 #if !defined(DISABLE_TI9X)
199 	if (tifiles_calc_is_ti9x(tifiles_file_get_model(filename)))
200 	{
201 		return ti9x_file_read_regular(filename, (Ti9xRegular *)content);
202 	}
203 	else
204 #endif
205 	if (content->model == CALC_NSPIRE)
206 	{
207 		return tnsp_file_read_regular(filename, (FileContent *)content);
208 	}
209 	else
210 	{
211 		return ERR_BAD_CALC;
212 	}
213 }
214 
215 /**
216  * tifiles_file_write_regular:
217  * @filename: name of single/group file where to write or NULL.
218  * @content: the file content to write.
219  * @real_fname: pointer address or NULL. Must be freed if needed when no longer needed.
220  *
221  * Write one (or several) variable(s) into a single (group) file. If filename is set to NULL,
222  * the function build a filename from varname and allocates resulting filename in %real_fname.
223  * %filename and %real_filename can be NULL but not both !
224  *
225  * %real_filename must be freed when no longer used.
226  *
227  * Return value: an error code, 0 otherwise.
228  **/
tifiles_file_write_regular(const char * filename,FileContent * content,char ** real_fname)229 TIEXPORT2 int tifiles_file_write_regular(const char *filename, FileContent *content, char **real_fname)
230 {
231 	if (content == NULL || (filename == NULL && real_fname == NULL))
232 	{
233 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
234 		return ERR_INVALID_FILE;
235 	}
236 
237 #if !defined(DISABLE_TI8X)
238 	if (tifiles_calc_is_ti8x(content->model))
239 	{
240 		return ti8x_file_write_regular(filename, (Ti8xRegular *)content, real_fname);
241 	}
242 	else
243 #endif
244 #if !defined(DISABLE_TI9X)
245 	if (tifiles_calc_is_ti9x(content->model))
246 	{
247 		return ti9x_file_write_regular(filename, (Ti9xRegular *)content, real_fname);
248 	}
249 	else
250 #endif
251 	if (content->model == CALC_NSPIRE)
252 	{
253 		return tnsp_file_write_regular(filename, (FileContent *)content, real_fname);
254 	}
255 	else
256 	{
257 		return ERR_BAD_CALC;
258 	}
259 }
260 
261 /**
262  * tifiles_file_display_regular:
263  * @content: the file content to show, a FileContent pointer.
264  *
265  * Display file content information contained in a FileContent structure.
266  *
267  * Return value: an error code, 0 otherwise.
268  **/
tifiles_file_display_regular(FileContent * content)269 TIEXPORT2 int TICALL tifiles_file_display_regular(FileContent *content)
270 {
271 	unsigned int i;
272 	char trans[257];
273 	int model_supports_folder = 1;
274 
275 	if (content == NULL)
276 	{
277 		tifiles_critical("%s(NULL)", __FUNCTION__);
278 		return ERR_INVALID_FILE;
279 	}
280 
281 #if !defined(DISABLE_TI8X)
282 	if (tifiles_calc_is_ti8x(content->model))
283 	{
284 		tifiles_info("FileContent for TI-8x: %p", content);
285 		model_supports_folder = 0;
286 	}
287 	else
288 #endif
289 #if !defined(DISABLE_TI9X)
290 	if (tifiles_calc_is_ti9x(content->model))
291 	{
292 		tifiles_info("FileContent for TI-9x: %p", content);
293 	}
294 	else
295 #endif
296 	if (content->model == CALC_NSPIRE)
297 	{
298 		tifiles_info("FileContent for TI-Nspire: %p", content);
299 	}
300 	else
301 	{
302 		return ERR_BAD_CALC;
303 	}
304 
305 	tifiles_info("Model:             %02X (%u)", content->model, content->model);
306 	tifiles_info("Signature:         %s", tifiles_calctype2signature(content->model));
307 	tifiles_info("Comment:           %s", content->comment);
308 	if (model_supports_folder)
309 	{
310 		tifiles_info("Default folder:    %s", content->default_folder);
311 	}
312 	tifiles_info("Number of entries: %u", content->num_entries);
313 	tifiles_info("Entries:           %p", content->entries);
314 
315 	if (content->entries != NULL)
316 	{
317 		for (i = 0; i < content->num_entries; i++)
318 		{
319 			VarEntry * ve = content->entries[i];
320 			if (ve != NULL)
321 			{
322 				tifiles_info("Entry #%u %p", i, ve);
323 				if (model_supports_folder)
324 				{
325 					tifiles_info("  folder:      %s", ve->folder);
326 				}
327 				tifiles_info("  name:        %s", ticonv_varname_to_utf8_sn(content->model, ve->name, trans, sizeof(trans), ve->type));
328 				tifiles_info("  type:        %02X (%s)", ve->type, tifiles_vartype2string(content->model, ve->type));
329 				tifiles_info("  attr:        %s", tifiles_attribute_to_string(ve->attr));
330 				tifiles_info("  version:     %02X (%u)", ve->version, ve->version);
331 				tifiles_info("  length:      %04X (%u)", ve->size, ve->size);
332 				tifiles_info("  data:        %p", ve->data);
333 			}
334 			else
335 			{
336 				tifiles_critical("%s: an entry in content is NULL", __FUNCTION__);
337 			}
338 		}
339 	}
340 
341 	tifiles_info("Checksum:      %04X (%u)", content->checksum, content->checksum);
342 	tifiles_info("Dest model:    %02X (%u)", content->model_dst, content->model_dst);
343 
344 	return 0;
345 }
346 
347 /**
348  * tifiles_content_create_backup:
349  * @model: a calculator model or CALC_NONE.
350  *
351  * Allocates a BackupContent structure.
352  *
353  * Return value: the allocated block.
354  **/
tifiles_content_create_backup(CalcModel model)355 TIEXPORT2 BackupContent* TICALL tifiles_content_create_backup(CalcModel model)
356 {
357 	BackupContent* content = g_malloc0(sizeof(BackupContent));
358 
359 	if (content != NULL)
360 	{
361 		if ((unsigned int)model >= CALC_MAX)
362 		{
363 			tifiles_warning("Invalid calculator model");
364 		}
365 		content->model = model;
366 		strncpy(content->comment, tifiles_comment_set_backup(), sizeof(content->comment) - 1);
367 		content->comment[sizeof(content->comment) - 1] = 0;
368 	}
369 
370 #ifdef TRACE_CONTENT_INSTANCES
371 	tifiles_info("tifiles_content_create_backup: %p", content);
372 	tifiles_file_display_backup(content);
373 #endif
374 
375 	return content;
376 }
377 
378 /**
379  * tifiles_content_delete_backup:
380  *
381  * Free the whole content of a BackupContent structure.
382  *
383  * Return value: none.
384  **/
tifiles_content_delete_backup(BackupContent * content)385 TIEXPORT2 int TICALL tifiles_content_delete_backup(BackupContent *content)
386 {
387 #ifdef TRACE_CONTENT_INSTANCES
388 	tifiles_info("tifiles_content_delete_backup: %p", content);
389 	tifiles_file_display_backup(content);
390 #endif
391 
392 	if (content != NULL)
393 	{
394 		if (tifiles_calc_is_ti9x(content->model))
395 		{
396 			g_free(content->data_part);
397 		}
398 		else if (tifiles_calc_is_ti8x(content->model))
399 		{
400 			g_free(content->data_part1);
401 			g_free(content->data_part2);
402 			g_free(content->data_part3);
403 			g_free(content->data_part4);
404 		}
405 
406 		g_free(content);
407 	}
408 	else
409 	{
410 		tifiles_critical("%s(NULL)", __FUNCTION__);
411 	}
412 
413 	return 0;
414 }
415 
416 /**
417  * tifiles_file_read_backup:
418  * @filename: name of backup file to open.
419  * @content: where to store the file content.
420  *
421  * Load the backup file into a BackupContent structure.
422  *
423  * Structure content must be freed with #tifiles_content_delete_backup when
424  * no longer used.
425  *
426  * Return value: an error code, 0 otherwise.
427  **/
tifiles_file_read_backup(const char * filename,BackupContent * content)428 TIEXPORT2 int tifiles_file_read_backup(const char *filename, BackupContent *content)
429 {
430 	if (filename == NULL || content == NULL)
431 	{
432 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
433 		return ERR_INVALID_FILE;
434 	}
435 
436 #if !defined(DISABLE_TI8X)
437 	if (tifiles_calc_is_ti8x(tifiles_file_get_model(filename)))
438 	{
439 		return ti8x_file_read_backup(filename, content);
440 	}
441 	else
442 #endif
443 #if !defined(DISABLE_TI9X)
444 	if (tifiles_calc_is_ti9x(tifiles_file_get_model(filename)))
445 	{
446 		return ti9x_file_read_backup(filename, content);
447 	}
448 	else
449 #endif
450 	return ERR_BAD_CALC;
451 }
452 
453 /**
454  * tifiles_file_write_backup:
455  * @filename: name of backup file where to write.
456  * @content: the file content to write.
457  *
458  * Write backup into file.
459  *
460  * Return value: an error code, 0 otherwise.
461  **/
tifiles_file_write_backup(const char * filename,BackupContent * content)462 TIEXPORT2 int tifiles_file_write_backup(const char *filename, BackupContent *content)
463 {
464 	if (filename == NULL || content == NULL)
465 	{
466 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
467 		return ERR_INVALID_FILE;
468 	}
469 
470 #if !defined(DISABLE_TI8X)
471 	if (tifiles_calc_is_ti8x(content->model))
472 	{
473 		return ti8x_file_write_backup(filename, content);
474 	}
475 	else
476 #endif
477 #if !defined(DISABLE_TI9X)
478 	if (tifiles_calc_is_ti9x(content->model))
479 	{
480 		return ti9x_file_write_backup(filename, content);
481 	}
482 	else
483 #endif
484 	return ERR_BAD_CALC;
485 }
486 
487 /**
488  * tifiles_file_display_backup:
489  * @content: the file content to show.
490  *
491  * Display file content information.
492  *
493  * Return value: an error code, 0 otherwise.
494  **/
tifiles_file_display_backup(BackupContent * content)495 TIEXPORT2 int TICALL tifiles_file_display_backup(BackupContent *content)
496 {
497 	if (content == NULL)
498 	{
499 		tifiles_critical("%s(NULL)", __FUNCTION__);
500 		return ERR_INVALID_FILE;
501 	}
502 
503 #if !defined(DISABLE_TI8X)
504 	if (tifiles_calc_is_ti8x(content->model))
505 	{
506 		return ti8x_content_display_backup(content);
507 	}
508 	else
509 #endif
510 #if !defined(DISABLE_TI9X)
511 	if (tifiles_calc_is_ti9x(content->model))
512 	{
513 		return ti9x_content_display_backup(content);
514 	}
515 	else
516 #endif
517 	return ERR_BAD_CALC;
518 }
519 
520 /**
521  * tifiles_content_create_flash:
522  * @model: a calculator model (compulsory).
523  *
524  * Allocates a #FlashContent structure.
525  *
526  * Return value: the allocated block.
527  **/
tifiles_content_create_flash(CalcModel model)528 TIEXPORT2 FlashContent* TICALL tifiles_content_create_flash(CalcModel model)
529 {
530 	FlashContent* content = g_malloc0(sizeof(FlashContent));
531 
532 	if (content != NULL)
533 	{
534 		if ((unsigned int)model >= CALC_MAX)
535 		{
536 			tifiles_warning("Invalid calculator model");
537 		}
538 		content->model = content->model_dst = model;
539 		if (tifiles_calc_is_ti9x(content->model))
540 		{
541 			time_t tt;
542 			struct tm *lt;
543 
544 			time(&tt);
545 			lt = localtime(&tt);
546 			content->revision_major = 1;
547 			content->revision_minor = 0;
548 			content->flags = 0;
549 			content->object_type = 0;
550 			content->revision_day = lt->tm_mday;
551 			content->revision_month = lt->tm_mon;
552 			content->revision_year = lt->tm_year + 1900;
553 		}
554 	}
555 
556 #ifdef TRACE_CONTENT_INSTANCES
557 	tifiles_info("tifiles_content_create_flash: %p", content);
558 	tifiles_file_display_flash(content);
559 #endif
560 
561 	return content;
562 }
563 
564 /**
565  * tifiles_content_delete_flash:
566  *
567  * Free the whole content of a #FlashContent structure.
568  *
569  * Return value: always 0.
570  **/
tifiles_content_delete_flash(FlashContent * content)571 TIEXPORT2 int TICALL tifiles_content_delete_flash(FlashContent *content)
572 {
573 #ifdef TRACE_CONTENT_INSTANCES
574 	tifiles_info("tifiles_content_delete_flash: %p", content);
575 	tifiles_file_display_flash(content);
576 #endif
577 
578 	if (content != NULL)
579 	{
580 #if !defined(DISABLE_TI8X) && !defined(DISABLE_TI9X)
581 		FlashContent *ptr;
582 		unsigned int i;
583 
584 		g_free(content->data_part);
585 
586 		ptr = content->next;
587 		while (ptr != NULL)
588 		{
589 			FlashContent *next = ptr->next;
590 
591 			g_free(ptr->data_part);
592 			g_free(ptr);
593 
594 			for (i = 0; i < content->num_pages; i++)
595 			{
596 				g_free(content->pages[i]->data);
597 				g_free(content->pages[i]);
598 			}
599 			g_free(content->pages);
600 
601 			ptr = next;
602 		}
603 
604 		g_free(content);
605 #else
606 		return ERR_BAD_CALC;
607 #endif
608 	}
609 	else
610 	{
611 		tifiles_critical("%s(NULL)", __FUNCTION__);
612 	}
613 
614 	return 0;
615 }
616 
617 /**
618  * tifiles_content_dup_flash:
619  *
620  * Allocates and copies a new FlashContent structure.
621  *
622  * Return value: none.
623  **/
tifiles_content_dup_flash(FlashContent * content)624 TIEXPORT2 FlashContent* TICALL tifiles_content_dup_flash(FlashContent *content)
625 {
626 	FlashContent *dup = NULL;
627 	FlashContent *p, *q;
628 
629 	if (content != NULL)
630 	{
631 		dup = tifiles_content_create_flash(content->model);
632 		if (dup != NULL)
633 		{
634 			for (p = content, q = dup; p; p = p->next, q = q->next)
635 			{
636 				memcpy(q, p, sizeof(FlashContent));
637 
638 				// TI-68k or TI-eZ80 part
639 				if (tifiles_calc_is_ti9x(content->model) || ticonv_model_is_tiez80(content->model))
640 				{
641 					if (p->data_part)
642 					{
643 						q->data_part = (uint8_t *)g_malloc0(p->data_length+1);
644 						memcpy(q->data_part, p->data_part, p->data_length+1);
645 					}
646 				}
647 
648 				// TI-Z80 part
649 				if (tifiles_calc_is_ti8x(content->model))
650 				{
651 					unsigned int i;
652 
653 					// copy pages
654 					q->pages = tifiles_fp_create_array(p->num_pages);
655 					for (i = 0; i < content->num_pages; i++)
656 					{
657 						q->pages[i] = (FlashPage *)g_malloc0(sizeof(FlashPage));
658 						memcpy(q->pages[i], p->pages[i], sizeof(FlashPage));
659 
660 						q->pages[i]->data = (uint8_t *) g_malloc0(p->pages[i]->size);
661 						memcpy(q->pages[i]->data, p->pages[i]->data, p->pages[i]->size);
662 					}
663 				}
664 
665 				if (p->next)
666 				{
667 					q->next = tifiles_content_create_flash(p->model);
668 				}
669 			}
670 		}
671 	}
672 	else
673 	{
674 		tifiles_critical("%s(NULL)", __FUNCTION__);
675 	}
676 
677 #ifdef TRACE_CONTENT_INSTANCES
678 	tifiles_info("tifiles_content_dup_flash: %p", dup);
679 	tifiles_file_display_flash(dup);
680 #endif
681 
682 	return dup;
683 }
684 
685 /**
686  * tifiles_file_read_flash:
687  * @filename: name of FLASH file to open.
688  * @content: where to store the file content.
689  *
690  * Load the FLASH file into a FlashContent structure.
691  *
692  * Structure content must be freed with #tifiles_content_delete_flash when
693  * no longer used.
694  *
695  * Return value: an error code, 0 otherwise.
696  **/
tifiles_file_read_flash(const char * filename,FlashContent * content)697 TIEXPORT2 int tifiles_file_read_flash(const char *filename, FlashContent *content)
698 {
699 	if (filename == NULL || content == NULL)
700 	{
701 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
702 		return ERR_INVALID_FILE;
703 	}
704 
705 #if !defined(DISABLE_TI8X)
706 	if (tifiles_calc_is_ti8x(tifiles_file_get_model(filename)))
707 	{
708 		return ti8x_file_read_flash(filename, content);
709 	}
710 	else
711 #endif
712 #if !defined(DISABLE_TI9X)
713 	if (tifiles_calc_is_ti9x(tifiles_file_get_model(filename)) || tifiles_file_is_tib(filename))
714 	{
715 		return ti9x_file_read_flash(filename, content);
716 	}
717 	else
718 #endif
719 	if (content->model == CALC_NSPIRE)
720 	{
721 		return tnsp_file_read_flash(filename, content);
722 	}
723 	else
724 	{
725 		return ERR_BAD_CALC;
726 	}
727 }
728 
729 /**
730  * tifiles_file_write_flash2:
731  * @filename: name of flash file where to write or NULL.
732  * @content: the file content to write.
733  * @real_fname: pointer address or NULL. Must be freed if needed when no longer needed.
734  *
735  * Write a FLASH content to a file. If filename is set to NULL, the function build a filename
736  * from appname and allocates resulting filename in %real_fname.
737  * %filename and %real_fname can be NULL but not both !
738  *
739  * %real_fname must be freed when no longer used.
740  *
741  * Return value: an error code, 0 otherwise.
742  **/
tifiles_file_write_flash2(const char * filename,FlashContent * content,char ** real_fname)743 TIEXPORT2 int tifiles_file_write_flash2(const char *filename, FlashContent *content, char **real_fname)
744 {
745 	if (content == NULL || (filename == NULL && real_fname == NULL))
746 	{
747 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
748 		return ERR_INVALID_FILE;
749 	}
750 
751 #if !defined(DISABLE_TI8X)
752 	if (tifiles_calc_is_ti8x(content->model))
753 	{
754 		return ti8x_file_write_flash(filename, content, real_fname);
755 	}
756 	else
757 #endif
758 #if !defined(DISABLE_TI9X)
759 	if (tifiles_calc_is_ti9x(content->model))
760 	{
761 		return ti9x_file_write_flash(filename, content, real_fname);
762 	}
763 	else
764 #endif
765 	return ERR_BAD_CALC;
766 }
767 
768 /**
769  * tifiles_file_write_flash:
770  * @filename: name of flash file where to write or NULL.
771  * @content: the file content to write.
772  *
773  * Write a FLASH content to a file. If filename is set to NULL, the function build a filename
774  * from appname.
775  *
776  * Return value: an error code, 0 otherwise.
777  **/
tifiles_file_write_flash(const char * filename,FlashContent * content)778 TIEXPORT2 int tifiles_file_write_flash(const char *filename, FlashContent *content)
779 {
780 	return tifiles_file_write_flash2(filename, content, NULL);
781 }
782 
783 /**
784  * tifiles_file_display_flash:
785  * @content: the file content to show.
786  *
787  * Display file content information.
788  *
789  * Return value: an error code, 0 otherwise.
790  **/
tifiles_file_display_flash(FlashContent * content)791 TIEXPORT2 int TICALL tifiles_file_display_flash(FlashContent *content)
792 {
793 	if (content == NULL)
794 	{
795 		tifiles_critical("%s(NULL)", __FUNCTION__);
796 		return ERR_INVALID_FILE;
797 	}
798 
799 #if !defined(DISABLE_TI8X)
800 	if (tifiles_calc_is_ti8x(content->model))
801 	{
802 		return ti8x_content_display_flash(content);
803 	}
804 	else
805 #endif
806 #if !defined(DISABLE_TI9X)
807 	if (tifiles_calc_is_ti9x(content->model))
808 	{
809 		return ti9x_content_display_flash(content);
810 	}
811 	else
812 #endif
813 	return ERR_BAD_CALC;
814 }
815 
816 /**
817  * tifiles_file_display:
818  * @filename: a TI file.
819  *
820  * Determine file class and display internal content.
821  *
822  * Return value: an error code, 0 otherwise.
823  **/
tifiles_file_display(const char * filename)824 TIEXPORT2 int TICALL tifiles_file_display(const char *filename)
825 {
826 	if (tifiles_file_is_tigroup(filename))
827 	{
828 		return tifiles_file_display_tigroup(filename);
829 	}
830 #if !defined(DISABLE_TI8X)
831 	if (tifiles_calc_is_ti8x(tifiles_file_get_model(filename)))
832 	{
833 		return ti8x_file_display(filename);
834 	}
835 	else
836 #endif
837 #if !defined(DISABLE_TI9X)
838 	if (tifiles_calc_is_ti9x(tifiles_file_get_model(filename)))
839 	{
840 		return ti9x_file_display(filename);
841 	}
842 	else
843 #endif
844 	return ERR_BAD_CALC;
845 }
846 
847 /*****************/
848 /* Miscellaneous */
849 /*****************/
850 
851 /**
852  * tifiles_create_table_of_entries:
853  * @content: a TI file content structure.
854  * @nfolders: returns the number of folders in the file.
855  *
856  * The goal of this function is to parse the file content structure in order to build
857  * a table of entries so that it's easy to write it just after the header in a group
858  * file. Mainly used as an helper.
859  * The returned 'table' is an NULL-terminated array of int* pointers.
860  * Each pointers points on an integer array. Each cell are an index on the 'VarEntry*
861  * entries' array.
862  *
863  * In fact, this array represents a kind of tree. The array of pointer is the folder list
864  * and each pointer is the variable list for each folder.
865  * For accessing the entry, we use the index.
866  *
867  * This function may be difficult to understand but it avoids to use trees (and
868  * linked list) which will require an implementation.
869  *
870  * Return value: a 2-dimensions allocated integer array. Must be freed with tifiles_free_table_of_entries when
871  * no longer used.
872  **/
tifiles_create_table_of_entries(FileContent * content,unsigned int * nfolders)873 TIEXPORT2 int** tifiles_create_table_of_entries(FileContent *content, unsigned int *nfolders)
874 {
875 	unsigned int num_folders = 0;
876 	unsigned int i, j;
877 	char **ptr, *folder_list[32768] = { 0 };
878 	int **table;
879 
880 	if (content == NULL)
881 	{
882 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
883 		return NULL;
884 	}
885 
886 	if (content->entries != NULL)
887 	{
888 		// determine how many folders we have
889 		for (i = 0; i < content->num_entries; i++)
890 		{
891 			VarEntry *entry = content->entries[i];
892 
893 			// scan for an existing folder entry
894 			for (ptr = folder_list; *ptr != NULL; ptr++)
895 			{
896 				if (!strcmp(*ptr, entry->folder))
897 				{
898 					//printf("break: %s\n", entry->folder);
899 					break;
900 				}
901 			}
902 			if (*ptr == NULL)
903 			{		// add new folder entry
904 				folder_list[num_folders] = (char *) g_malloc0(257);
905 				//printf("%i: adding '%s'\n", num_folders, entry->folder);
906 				strncpy(folder_list[num_folders], entry->folder, sizeof(folder_list[num_folders]) - 1);
907 				folder_list[num_folders][sizeof(folder_list[num_folders]) - 1] = 0;
908 				folder_list[num_folders + 1] = NULL;
909 				num_folders++;
910 			}
911 		}
912 		if (tifiles_calc_is_ti8x(content->model))
913 		{
914 			num_folders++;
915 		}
916 	}
917 	if (nfolders != NULL)
918 	{
919 		*nfolders = num_folders;
920 	}
921 
922 	// allocate the folder list
923 	table = (int **) g_malloc0((num_folders + 1) * sizeof(int *));
924 	table[num_folders] = NULL;
925 
926 	// for each folder, determine how many variables we have
927 	// and allocate array with indexes
928 	for (j = 0; j < num_folders; j++)
929 	{
930 		int k;
931 
932 		for (i = 0, k = 0; i < content->num_entries; i++)
933 		{
934 			VarEntry *entry = content->entries[i];
935 
936 			if (!strcmp(folder_list[j], entry->folder))
937 			{
938 				table[j] = (int *) g_realloc(table[j], (k + 2) * sizeof(int));
939 				table[j][k] = i;
940 				//printf("%i %i: adding %i\n", j, k, i);
941 				table[j][k + 1] = -1;
942 				k++;
943 			}
944 		}
945 	}
946 
947 	// g_free( memory
948 	for (j = 0; j < num_folders + 1; j++)
949 	{
950 		g_free(folder_list[j]);
951 	}
952 
953 	return table;
954 }
955 
tifiles_free_table_of_entries(int ** table)956 TIEXPORT2 void tifiles_free_table_of_entries(int ** table)
957 {
958 	if (table != NULL)
959 	{
960 		int ** ptr = table;
961 		while (*ptr != NULL)
962 		{
963 			g_free(*ptr);
964 			ptr++;
965 		}
966 		g_free(table);
967 	}
968 }
969