1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id: typesxx.c 912 2005-03-30 20:49:06Z roms $ */
3
4 /* libtifiles - file format library, a part of the TiLP project
5 * Copyright (C) 1999-2005 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 #include <ctype.h>
23 #include <glib/gstdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "gettext.h"
28 #include "tifiles.h"
29 #include "logging.h"
30 #include "internal.h"
31 #include "typesxx.h"
32 #include "error.h"
33 #include "rwfile.h"
34
35 // Whether to enable strict file extension checking.
36 #define CHECK_FILE_EXTENSIONS
37
38 /****************/
39 /* Global types */
40 /****************/
41
42 static const char GROUP_FILE_EXT[CALC_MAX + 1][4] =
43 {
44 "",
45 "73g", "82g", "83g", "8Xg", "8Xg", "85g", "86g",
46 "89g", "89g", "92g", "9Xg", "V2g", "8Xg", "89g",
47 "", "", "8Xg", "8Xg", "8Xg", "8Xg", "8Xg",
48 "8xg",
49 ""
50 };
51
52 static const char BACKUP_FILE_EXT[CALC_MAX + 1][4] =
53 {
54 "",
55 "73b", "82b", "83b", "8Xb", "8Xb", "85b", "86b",
56 "89g", "89g", "92b", "9Xg", "V2g", "8Xg", "89g",
57 "", "", "8Cb", "8Cb", "", "", "8Xb",
58 "8Xb",
59 ""
60 };
61
62 /*******************/
63 /* File extensions */
64 /*******************/
65
66 /**
67 * tifiles_fext_of_group:
68 * @model: a calculator model.
69 *
70 * Returns file extension of a group file.
71 *
72 * Return value: a file extension as string (like "83g").
73 **/
tifiles_fext_of_group(CalcModel model)74 TIEXPORT2 const char * TICALL tifiles_fext_of_group (CalcModel model)
75 {
76 switch (model)
77 {
78 case CALC_NONE:
79 return NULL;
80 case CALC_TI73:
81 return "73g";
82 case CALC_TI80:
83 return NULL;
84 case CALC_TI82:
85 return "82g";
86 case CALC_TI83:
87 return "83g";
88 case CALC_TI83P:
89 case CALC_TI84P:
90 case CALC_TI84PC:
91 case CALC_TI84P_USB:
92 case CALC_TI84PC_USB:
93 case CALC_TI83PCE_USB:
94 case CALC_TI84PCE_USB:
95 case CALC_TI82A_USB:
96 case CALC_TI84PT_USB:
97 return "8Xg"; // There's also 8Cg.
98 case CALC_TI85:
99 return "85g";
100 case CALC_TI86:
101 return "86g";
102 case CALC_TI89:
103 case CALC_TI89T:
104 case CALC_TI89T_USB:
105 return "89g";
106 case CALC_TI92:
107 return "92g";
108 case CALC_TI92P:
109 return "9Xg";
110 case CALC_V200:
111 return "V2g";
112 case CALC_NSPIRE:
113 return NULL;
114 default:
115 tifiles_critical("%s: invalid model argument", __FUNCTION__);
116 break;
117 }
118
119 return NULL;
120 }
121
122 /**
123 * tifiles_fext_of_backup:
124 * @model: a calculator model.
125 *
126 * Returns file extension of a backup file.
127 *
128 * Return value: a file extension as string (like "83b").
129 **/
tifiles_fext_of_backup(CalcModel model)130 TIEXPORT2 const char * TICALL tifiles_fext_of_backup (CalcModel model)
131 {
132 switch (model)
133 {
134 case CALC_NONE:
135 return "??b";
136 case CALC_TI73:
137 return "73b";
138 case CALC_TI80:
139 return NULL;
140 case CALC_TI82:
141 return "82b";
142 case CALC_TI83:
143 return "83b";
144 case CALC_TI83P:
145 case CALC_TI84P:
146 case CALC_TI84P_USB:
147 case CALC_TI82A_USB:
148 case CALC_TI84PT_USB:
149 return "8Xb";
150 case CALC_TI84PC:
151 case CALC_TI84PC_USB:
152 return "8Cb";
153 case CALC_TI83PCE_USB:
154 case CALC_TI84PCE_USB:
155 return NULL;
156 case CALC_TI85:
157 return "85b";
158 case CALC_TI86:
159 return "86b";
160 case CALC_TI89:
161 case CALC_TI89T:
162 case CALC_TI89T_USB:
163 return "89g";
164 case CALC_TI92:
165 return "92b";
166 case CALC_TI92P:
167 return "9Xg";
168 case CALC_V200:
169 return "V2g";
170 case CALC_NSPIRE:
171 return NULL;
172 default:
173 tifiles_critical("%s: invalid model argument", __FUNCTION__);
174 break;
175 }
176
177 return NULL;
178 }
179
180 /**
181 * tifiles_fext_of_flash_app:
182 * @model: a calculator model.
183 *
184 * Returns file extension of a FLASH application file.
185 *
186 * Return value: a file extension as string (like "89k").
187 **/
tifiles_fext_of_flash_app(CalcModel model)188 TIEXPORT2 const char * TICALL tifiles_fext_of_flash_app (CalcModel model)
189 {
190 switch (model)
191 {
192 case CALC_NONE:
193 return "??k";
194 case CALC_TI73:
195 return "73k";
196 case CALC_TI80:
197 return NULL;
198 case CALC_TI82:
199 return NULL;
200 case CALC_TI83:
201 return NULL;
202 case CALC_TI83P:
203 case CALC_TI84P:
204 case CALC_TI84P_USB:
205 return "8Xk";
206 case CALC_TI84PC:
207 case CALC_TI84PC_USB:
208 return "8Ck";
209 case CALC_TI83PCE_USB:
210 return "8Ek";
211 case CALC_TI84PCE_USB:
212 return "8Ek";
213 case CALC_TI82A_USB:
214 case CALC_TI84PT_USB:
215 return NULL;
216 case CALC_TI85:
217 return NULL;
218 case CALC_TI86:
219 return NULL;
220 case CALC_TI89:
221 case CALC_TI89T:
222 case CALC_TI89T_USB:
223 return "89k";
224 case CALC_TI92:
225 return NULL;
226 case CALC_TI92P:
227 return "9Xk";
228 case CALC_V200:
229 return "V2k";
230 case CALC_NSPIRE:
231 return NULL;
232 default:
233 tifiles_critical("%s: invalid model argument", __FUNCTION__);
234 break;
235 }
236
237 return NULL;
238 }
239
240 /**
241 * tifiles_fext_of_flash_os:
242 * @model: a calculator model.
243 *
244 * Returns file extension of a FLASH Operating System file.
245 *
246 * Return value: a file extension as string (like "89u").
247 **/
tifiles_fext_of_flash_os(CalcModel model)248 TIEXPORT2 const char * TICALL tifiles_fext_of_flash_os(CalcModel model)
249 {
250 switch (model)
251 {
252 case CALC_NONE:
253 return NULL;
254 case CALC_TI73:
255 return "73u";
256 case CALC_TI80:
257 return NULL;
258 case CALC_TI82:
259 return NULL;
260 case CALC_TI83:
261 return NULL;
262 case CALC_TI83P:
263 case CALC_TI84P:
264 case CALC_TI84P_USB:
265 return "8Xu";
266 case CALC_TI84PC:
267 case CALC_TI84PC_USB:
268 return "8Cu";
269 case CALC_TI83PCE_USB:
270 return "8Pu";
271 case CALC_TI84PCE_USB:
272 return "8Eu";
273 case CALC_TI82A_USB:
274 return "82u";
275 case CALC_TI84PT_USB:
276 return "8Xu";
277 case CALC_TI85:
278 return NULL;
279 case CALC_TI86:
280 return NULL;
281 case CALC_TI89:
282 case CALC_TI89T:
283 case CALC_TI89T_USB:
284 return "89u";
285 case CALC_TI92:
286 return NULL;
287 case CALC_TI92P:
288 return "9Xu";
289 case CALC_V200:
290 return "V2u";
291 case CALC_NSPIRE:
292 return "tno"; // Actually, it depends on the sub-model...
293 default:
294 tifiles_critical("%s: invalid model argument", __FUNCTION__);
295 break;
296 }
297
298 return NULL;
299 }
300
301 /**
302 * tifiles_fext_of_certif:
303 * @model: a calculator model.
304 *
305 * Returns file extension of certificate file.
306 *
307 * Return value: a file extension as string (like "89q").
308 **/
tifiles_fext_of_certif(CalcModel model)309 TIEXPORT2 const char * TICALL tifiles_fext_of_certif(CalcModel model)
310 {
311 switch (model)
312 {
313 case CALC_NONE:
314 return "??q";
315 case CALC_TI73:
316 return "73q";
317 case CALC_TI80:
318 return NULL;
319 case CALC_TI82:
320 return NULL;
321 case CALC_TI83:
322 return NULL;
323 case CALC_TI83P:
324 case CALC_TI84P:
325 case CALC_TI84P_USB:
326 return "8Xq";
327 case CALC_TI84PC:
328 case CALC_TI84PC_USB:
329 return "8Cq";
330 case CALC_TI83PCE_USB:
331 return "8Pq";
332 case CALC_TI84PCE_USB:
333 return "8Eq";
334 case CALC_TI82A_USB:
335 case CALC_TI84PT_USB:
336 return NULL;
337 case CALC_TI85:
338 return NULL;
339 case CALC_TI86:
340 return NULL;
341 case CALC_TI89:
342 case CALC_TI89T:
343 case CALC_TI89T_USB:
344 return "89q";
345 case CALC_TI92:
346 return NULL;
347 case CALC_TI92P:
348 return "9Xq";
349 case CALC_V200:
350 return "V2q";
351 case CALC_NSPIRE:
352 return NULL;
353 default:
354 tifiles_critical("%s: invalid calc_type argument", __FUNCTION__);
355 break;
356 }
357
358 return NULL;
359 }
360
361 /**
362 * tifiles_fext_get:
363 * @filename: a filename as string.
364 *
365 * Returns file extension part.
366 *
367 * Return value: a file extension without dot as string (like "89g").
368 **/
tifiles_fext_get(const char * filename)369 TIEXPORT2 char * TICALL tifiles_fext_get(const char *filename)
370 {
371 if (filename != NULL)
372 {
373 char * d = strrchr(filename, '.');
374 if (d != NULL)
375 {
376 return (++d);
377 }
378 }
379 else
380 {
381 tifiles_critical("%s(NULL)", __FUNCTION__);
382 }
383
384 return (char *)"";
385 }
386
387 /**
388 * tifiles_fext_dup:
389 * @filename: a filename as string.
390 *
391 * Returns a copy of file extension part.
392 *
393 * Return value: a file extension without dot as string (like "89g").
394 * Needs to be freed with tifiles_fext_free() when no longer needed.
395 **/
tifiles_fext_dup(const char * filename)396 TIEXPORT2 char * TICALL tifiles_fext_dup(const char *filename)
397 {
398 return g_strdup(tifiles_fext_get(filename));
399 }
400
401 /**
402 * tifiles_fext_free:
403 * @filename: a filename as string.
404 *
405 * Frees a file extension part previously allocated with tifiles_fext_dup().
406 **/
tifiles_fext_free(char * filename)407 TIEXPORT2 void TICALL tifiles_fext_free(char *filename)
408 {
409 g_free(filename);
410 }
411
412 /**********************/
413 /* Signature checking */
414 /**********************/
415
416 /**
417 * tifiles_file_has_ti_header:
418 * @filename: a filename as string.
419 *
420 * Check whether file has a TI magic number in the header.
421 *
422 * Return value: a boolean value.
423 **/
tifiles_file_has_ti_header(const char * filename)424 TIEXPORT2 int TICALL tifiles_file_has_ti_header(const char *filename)
425 {
426 FILE *f;
427 char buf[9];
428 char *p;
429 int ret = 0;
430
431 if (filename != NULL)
432 {
433 f = g_fopen(filename, "rb");
434 if (f != NULL)
435 {
436 memset(buf, 0, sizeof(buf));
437 if (fread_8_chars(f, buf) == 0)
438 {
439 for (p = buf; *p != '\0'; p++)
440 {
441 *p = toupper(*p);
442 }
443
444 if (!strcmp(buf, "**TI73**") || !strcmp(buf, "**TI82**") ||
445 !strcmp(buf, "**TI83**") || !strcmp(buf, "**TI83F*") ||
446 !strcmp(buf, "**TI85**") || !strcmp(buf, "**TI86**") ||
447 !strcmp(buf, "**TI89**") || !strcmp(buf, "**TI92**") ||
448 !strcmp(buf, "**TI92P*") || !strcmp(buf, "**V200**") ||
449 !strcmp(buf, "**TIFL**") ||
450 !strncmp(buf, "*TI", 3))
451 {
452 ret = !0;
453 }
454 }
455 fclose(f);
456 }
457 }
458
459 return ret;
460 }
461
462 #define TIB_SIGNATURE "Advanced Mathematics Software"
463
464 /**
465 * tifiles_file_has_tib_header:
466 * @filename: a filename as string.
467 *
468 * Check whether file has a TIB magic number in the header.
469 *
470 * Return value: a boolean value.
471 **/
tifiles_file_has_tib_header(const char * filename)472 TIEXPORT2 int TICALL tifiles_file_has_tib_header(const char *filename)
473 {
474 FILE *f;
475 char str[64];
476 int ret = 0;
477
478 if (filename != NULL)
479 {
480 #ifdef CHECK_FILE_EXTENSIONS
481 char *e = tifiles_fext_get(filename);
482
483 if ( e[0] == 0
484 || g_ascii_strcasecmp(e, "tib"))
485 {
486 return 0;
487 }
488 #endif
489
490 f = g_fopen(filename, "rb");
491 if (f != NULL)
492 {
493 if (fread_n_chars(f, 22 + sizeof(TIB_SIGNATURE) + 1, str) == 0)
494 {
495 str[22 + sizeof(TIB_SIGNATURE) + 1] = '\0';
496 if (!strcmp(str + 22, TIB_SIGNATURE))
497 {
498 ret = !0;
499 }
500 }
501 fclose(f);
502 }
503 }
504
505 return ret;
506 }
507
508 #define TIG_SIGNATURE "PK\x03\x04" // 0x04034b50
509 #define TIG_SIGNATURE2 "PK\x05\x06" // 0x06054b50
510
511 /**
512 * tifiles_file_has_tig_header:
513 * @filename: a filename as string.
514 *
515 * Check whether file has a ZIP file header.
516 *
517 * Return value: a boolean value.
518 **/
tifiles_file_has_tig_header(const char * filename)519 TIEXPORT2 int TICALL tifiles_file_has_tig_header(const char *filename)
520 {
521 FILE *f;
522 char str[5];
523 int ret = 0;
524
525 if (filename != NULL)
526 {
527 #ifdef CHECK_FILE_EXTENSIONS
528 char *e = tifiles_fext_get(filename);
529
530 if ( e[0] == 0
531 || g_ascii_strcasecmp(e, "tig"))
532 {
533 return 0;
534 }
535 #endif
536
537 f = g_fopen(filename, "rb");
538 if (f != NULL)
539 {
540 if (fread_n_chars(f, strlen(TIG_SIGNATURE), str) == 0)
541 {
542 str[strlen(TIG_SIGNATURE)] = '\0';
543 if (!strcmp(str, TIG_SIGNATURE) || !strcmp(str, TIG_SIGNATURE2))
544 {
545 ret = !0;
546 }
547 }
548 fclose(f);
549 }
550 }
551
552 return ret;
553 }
554
555 /**
556 * tifiles_file_has_tifl_header:
557 * @filename: a filename as string.
558 *
559 * Check whether file has a TI Flash file magic number in the header, and
560 * fill device type and data type for the last entry in the file.
561 *
562 * Return value: a boolean value.
563 **/
tifiles_file_has_tifl_header(const char * filename,uint8_t * dev_type,uint8_t * data_type)564 TIEXPORT2 int TICALL tifiles_file_has_tifl_header(const char *filename, uint8_t *dev_type, uint8_t *data_type)
565 {
566 FILE *f;
567 uint8_t buf[78];
568 uint32_t len;
569 int ret = 0;
570
571 if (filename != NULL)
572 {
573 f = g_fopen(filename, "rb");
574 if (f != NULL)
575 {
576 while (fread(buf, 1, 78, f) == 78)
577 {
578 if (strncmp((char *) buf, "**TIFL**", 8))
579 {
580 break;
581 }
582
583 ret = 1;
584
585 /* if there are multiple entries (old OS files with license
586 agreement, old app files with certificate), return the type
587 of the last entry */
588 if (dev_type != NULL)
589 {
590 *dev_type = buf[48];
591 }
592 if (data_type != NULL)
593 {
594 *data_type = buf[49];
595 }
596
597 len = buf[74] | (((uint32_t)buf[75]) << 8) | (((uint32_t)buf[76]) << 16) | (((uint32_t)buf[77]) << 24);
598 if (fseek(f, len, SEEK_CUR))
599 {
600 break;
601 }
602 }
603
604 fclose(f);
605 }
606 }
607
608 return ret;
609 }
610
611 #define TNO_SIGNATURE "TI-Nspire.tno "
612 #define TNO_NOSAMPLES_SIGNATURE "TI-Nspire.nosamples.tno "
613 #define TNC_SIGNATURE "TI-Nspire.tnc "
614 #define TCO_SIGNATURE "TI-Nspire.tco "
615 #define TCC_SIGNATURE "TI-Nspire.tcc "
616 #define TMO_SIGNATURE "TI-Nspire.tmo "
617 #define TMC_SIGNATURE "TI-Nspire.tmc "
618 #define OSEXT1_SIGNATURE "__OSEXT__1 "
619
620 /**
621 * tifiles_file_has_tno_header:
622 * @filename: a filename as string.
623 *
624 * Check whether file has a Nspire OS / OS extension file header.
625 *
626 * Return value: a boolean value.
627 **/
tifiles_file_has_tno_header(const char * filename)628 TIEXPORT2 int TICALL tifiles_file_has_tno_header(const char *filename)
629 {
630 FILE *f;
631 char str[128];
632 int ret = 0;
633
634 if (filename != NULL)
635 {
636 #ifdef CHECK_FILE_EXTENSIONS
637 char *e = tifiles_fext_get(filename);
638
639 if ( e[0] == 0
640 || ( g_ascii_strcasecmp(e, "tno") && g_ascii_strcasecmp(e, "tnc")
641 && g_ascii_strcasecmp(e, "tco") && g_ascii_strcasecmp(e, "tcc")
642 && g_ascii_strcasecmp(e, "tmo") && g_ascii_strcasecmp(e, "tmc")
643 )
644 )
645 {
646 return 0;
647 }
648 #endif
649
650 f = g_fopen(filename, "rb");
651 if (f != NULL)
652 {
653 if (fread_n_chars(f, 63, str) == 0)
654 {
655 if ( !strncmp(str, TNO_SIGNATURE, 14)
656 || !strncmp(str, TNC_SIGNATURE, 14)
657 || !strncmp(str, TNO_NOSAMPLES_SIGNATURE, 24)
658 || !strncmp(str, TCO_SIGNATURE, 14)
659 || !strncmp(str, TCC_SIGNATURE, 14)
660 || !strncmp(str, TMO_SIGNATURE, 14)
661 || !strncmp(str, TMC_SIGNATURE, 14)
662 || !strncmp(str, OSEXT1_SIGNATURE, 11)
663 )
664 {
665 ret = !0;
666 }
667 }
668
669 fclose(f);
670 }
671 }
672
673 return ret;
674 }
675
676 /**
677 * tifiles_model_to_dev_type:
678 * @model: a calculator model
679 *
680 * Converts the calculator model to FlashApp DeviceType.
681 *
682 * Return value: FlashApp DeviceType if that calculator model supports FlashApps, -1 otherwise.
683 **/
tifiles_model_to_dev_type(CalcModel model)684 TIEXPORT2 int TICALL tifiles_model_to_dev_type(CalcModel model)
685 {
686 switch (model) {
687 case CALC_TI73:
688 return DEVICE_TYPE_73;
689
690 case CALC_TI83P:
691 case CALC_TI84P:
692 case CALC_TI84P_USB:
693 case CALC_TI84PC:
694 case CALC_TI84PC_USB:
695 case CALC_TI83PCE_USB:
696 case CALC_TI84PCE_USB:
697 case CALC_TI82A_USB:
698 case CALC_TI84PT_USB:
699 return DEVICE_TYPE_83P;
700
701 case CALC_TI89:
702 case CALC_TI89T:
703 case CALC_TI89T_USB:
704 return DEVICE_TYPE_89;
705
706 case CALC_TI92P:
707 case CALC_V200:
708 return DEVICE_TYPE_92P;
709
710 default:
711 return -1;
712 }
713 }
714
715
716 /**************/
717 /* File types */
718 /**************/
719
720 #ifndef __WIN32__
721 #include <sys/types.h>
722 #include <sys/stat.h>
723 #include <unistd.h>
724 #endif
725
is_regfile(const char * filename)726 static int is_regfile(const char *filename)
727 {
728 #ifndef __WIN32__
729 struct stat buf;
730
731 if (stat(filename, &buf) < 0)
732 {
733 return 0;
734 }
735
736 if (S_ISREG(buf.st_mode))
737 {
738 return !0;
739 }
740 else
741 {
742 return 0;
743 }
744 #else
745 return !0;
746 #endif
747 }
748
749 /**
750 * tifiles_file_is_ti:
751 * @filename: a filename as string.
752 *
753 * Check whether file is a TI file by checking the signature.
754 *
755 * Return value: a boolean value.
756 **/
tifiles_file_is_ti(const char * filename)757 TIEXPORT2 int TICALL tifiles_file_is_ti(const char *filename)
758 {
759 char *e;
760
761 if (filename != NULL)
762 {
763 // bug: check that file is not a FIFO
764 if (!is_regfile(filename))
765 {
766 return 0;
767 }
768
769 if ( tifiles_file_has_ti_header(filename)
770 || tifiles_file_has_tib_header(filename)
771 || tifiles_file_has_tig_header(filename)
772 || tifiles_file_has_tifl_header(filename, NULL, NULL)
773 || tifiles_file_has_tno_header(filename))
774 {
775 return !0;
776 }
777
778 e = tifiles_fext_get(filename);
779
780 #ifdef CHECK_FILE_EXTENSIONS
781 if (e[0] == 0)
782 {
783 return 0;
784 }
785 #endif
786
787 if (!g_ascii_strcasecmp(e, "tns"))
788 {
789 return !0;
790 }
791 }
792 else
793 {
794 tifiles_critical("%s(NULL)", __FUNCTION__);
795 }
796
797 return 0;
798 }
799
800 /**
801 * tifiles_file_is_single:
802 * @filename: a filename as string.
803 *
804 * Check whether file is a single TI file (like program, function, ...).
805 *
806 * Return value: a boolean value.
807 **/
tifiles_file_is_single(const char * filename)808 TIEXPORT2 int TICALL tifiles_file_is_single(const char *filename)
809 {
810 if (!tifiles_file_is_ti(filename))
811 {
812 return 0;
813 }
814
815 if (!tifiles_file_is_group(filename) &&
816 !tifiles_file_is_backup(filename) &&
817 !tifiles_file_is_flash(filename) &&
818 !tifiles_file_is_tigroup(filename))
819 {
820 return !0;
821 }
822
823 return 0;
824 }
825
826 /**
827 * tifiles_file_is_group:
828 * @filename: a filename as string.
829 *
830 * Check whether file is a group file.
831 *
832 * Return value: a boolean value.
833 **/
tifiles_file_is_group(const char * filename)834 TIEXPORT2 int TICALL tifiles_file_is_group(const char *filename)
835 {
836 int i;
837 char *e = tifiles_fext_get(filename);
838
839 #ifdef CHECK_FILE_EXTENSIONS
840 if (e[0] == 0)
841 {
842 return 0;
843 }
844 #endif
845 if (!tifiles_file_is_ti(filename))
846 {
847 return 0;
848 }
849
850 for (i = 1; i < CALC_MAX + 1; i++)
851 {
852 if (GROUP_FILE_EXT[i][0] != 0 && !g_ascii_strcasecmp(e, GROUP_FILE_EXT[i]))
853 {
854 return !0;
855 }
856 }
857
858 return 0;
859 }
860
861 /**
862 * tifiles_file_is_regular:
863 * @filename: a filename as string.
864 *
865 * Check whether file is a single or group file.
866 *
867 * Return value: a boolean value.
868 **/
tifiles_file_is_regular(const char * filename)869 TIEXPORT2 int TICALL tifiles_file_is_regular(const char *filename)
870 {
871 if (!tifiles_file_is_ti(filename))
872 {
873 return 0;
874 }
875
876 return (tifiles_file_is_single(filename) || tifiles_file_is_group(filename));
877 }
878
879 /**
880 * tifiles_file_is_backup:
881 * @filename: a filename as string.
882 *
883 * Check whether file is a backup file.
884 *
885 * Return value: a boolean value.
886 **/
tifiles_file_is_backup(const char * filename)887 TIEXPORT2 int TICALL tifiles_file_is_backup(const char *filename)
888 {
889 int i;
890 char *e = tifiles_fext_get(filename);
891
892 #ifdef CHECK_FILE_EXTENSIONS
893 if (e[0] == 0)
894 {
895 return 0;
896 }
897 #endif
898 if (!tifiles_file_is_ti(filename))
899 {
900 return 0;
901 }
902
903 for (i = 1; i < CALC_MAX + 1; i++)
904 {
905 if (BACKUP_FILE_EXT[i][0] != 0 && !g_ascii_strcasecmp(e, BACKUP_FILE_EXT[i]))
906 {
907 return !0;
908 }
909 }
910
911 return 0;
912 }
913
914 /**
915 * tifiles_file_is_os:
916 * @filename: a filename as string.
917 *
918 * Check whether file is a FLASH OS file (tib or XXu)
919 *
920 * Return value: a boolean value.
921 **/
tifiles_file_is_os(const char * filename)922 TIEXPORT2 int TICALL tifiles_file_is_os(const char *filename)
923 {
924 uint8_t type;
925
926 if (!tifiles_file_is_ti(filename))
927 {
928 return 0;
929 }
930
931 if ( tifiles_file_is_tib(filename)
932 || tifiles_file_is_tno(filename)
933 || (tifiles_file_has_tifl_header(filename, NULL, &type) && type == TI83p_AMS))
934 {
935 return !0;
936 }
937
938 return 0;
939 }
940
941 /**
942 * tifiles_file_is_app:
943 * @filename: a filename as string.
944 *
945 * Check whether file is a FLASH app file
946 *
947 * Return value: a boolean value.
948 **/
tifiles_file_is_app(const char * filename)949 TIEXPORT2 int TICALL tifiles_file_is_app(const char *filename)
950 {
951 uint8_t type;
952
953 if (!tifiles_file_is_ti(filename))
954 {
955 return 0;
956 }
957
958 if (tifiles_file_has_tifl_header(filename, NULL, &type) && type == TI83p_APPL)
959 {
960 return !0;
961 }
962
963 return 0;
964 }
965
966 /**
967 * tifiles_file_is_flash:
968 * @filename: a filename as string.
969 *
970 * Check whether file is a FLASH file (os or app).
971 *
972 * Return value: a boolean value.
973 **/
tifiles_file_is_flash(const char * filename)974 TIEXPORT2 int TICALL tifiles_file_is_flash(const char *filename)
975 {
976 return (tifiles_file_is_tib(filename) ||
977 tifiles_file_is_tno(filename) ||
978 tifiles_file_has_tifl_header(filename, NULL, NULL));
979 }
980
981 /**
982 * tifiles_file_is_tib:
983 * @filename: a filename as string.
984 *
985 * Check whether file is a TIB formatted file.
986 *
987 * Return value: a boolean value.
988 **/
tifiles_file_is_tib(const char * filename)989 TIEXPORT2 int TICALL tifiles_file_is_tib(const char *filename)
990 {
991 return tifiles_file_has_tib_header(filename);
992 }
993
994 /**
995 * tifiles_file_is_tigroup:
996 * @filename: a filename as string.
997 *
998 * Check whether file is a TiGroup formatted file.
999 *
1000 * Return value: a boolean value.
1001 **/
tifiles_file_is_tigroup(const char * filename)1002 TIEXPORT2 int TICALL tifiles_file_is_tigroup(const char *filename)
1003 {
1004 return tifiles_file_has_tig_header(filename);
1005 }
1006
1007 /**
1008 * tifiles_file_is_tno:
1009 * @filename: a filename as string.
1010 *
1011 * Check whether file is a TNO formatted file.
1012 *
1013 * Return value: a boolean value.
1014 **/
tifiles_file_is_tno(const char * filename)1015 TIEXPORT2 int TICALL tifiles_file_is_tno(const char *filename)
1016 {
1017 return tifiles_file_has_tno_header(filename);
1018 }
1019
1020 /**
1021 * tifiles_file_test:
1022 * @filename: a filename as string.
1023 * @type: type to check
1024 * @target: hand-held model or CALC_NONE for no filtering
1025 *
1026 * Check whether #filename is a TI file of type #type useable on a #target model.
1027 * This function is a generic one which overwrap and extends the tifiles_file_is_*
1028 * functions.
1029 *
1030 * This is a powerful function which allows checking of a specific file type for
1031 * a given target.
1032 *
1033 * Return value: a boolean value.
1034 **/
tifiles_file_test(const char * filename,FileClass type,CalcModel target)1035 TIEXPORT2 int TICALL tifiles_file_test(const char *filename, FileClass type, CalcModel target)
1036 {
1037 char *e = tifiles_fext_get(filename);
1038 uint8_t ctype, dtype;
1039
1040 #ifdef CHECK_FILE_EXTENSIONS
1041 if (e[0] == 0)
1042 {
1043 return 0;
1044 }
1045 #endif
1046
1047 if (target > CALC_MAX)
1048 {
1049 tifiles_critical("%s: invalid target argument", __FUNCTION__);
1050 return 0;
1051 }
1052
1053 if (!tifiles_file_is_ti(filename))
1054 {
1055 return 0;
1056 }
1057
1058 if (type & TIFILE_SINGLE)
1059 {
1060 if (GROUP_FILE_EXT[target][0] != 0 && !g_ascii_strncasecmp(e, GROUP_FILE_EXT[target], 2))
1061 {
1062 return !0;
1063 }
1064 else
1065 {
1066 return tifiles_file_is_single(filename);
1067 }
1068 }
1069
1070 if (type & TIFILE_GROUP)
1071 {
1072 if (GROUP_FILE_EXT[target][0] != 0 && !g_ascii_strcasecmp(e, GROUP_FILE_EXT[target]))
1073 {
1074 return !0;
1075 }
1076 else
1077 {
1078 return tifiles_file_is_group(filename);
1079 }
1080 }
1081
1082 if (type & TIFILE_REGULAR)
1083 {
1084 return tifiles_file_test(filename, TIFILE_SINGLE, target) || tifiles_file_test(filename, TIFILE_GROUP, target);
1085 }
1086
1087 if (type & TIFILE_BACKUP)
1088 {
1089 if (BACKUP_FILE_EXT[target][0] != 0 && !g_ascii_strcasecmp(e, BACKUP_FILE_EXT[target]))
1090 {
1091 return !0;
1092 }
1093 else
1094 {
1095 return tifiles_file_is_backup(filename);
1096 }
1097 }
1098
1099 if (type & TIFILE_OS)
1100 {
1101 if (target && tifiles_file_has_tifl_header(filename, &ctype, &dtype))
1102 {
1103 return (ctype == tifiles_model_to_dev_type(target) && dtype == TI83p_AMS);
1104 }
1105 else if (target && tifiles_file_is_tib(filename))
1106 {
1107 FILE *f;
1108 uint8_t data[16];
1109
1110 f = g_fopen(filename, "rb");
1111 if (f == NULL)
1112 {
1113 return 0;
1114 }
1115
1116 fread_n_chars(f, 16, (char *)data);
1117 fclose(f);
1118
1119 switch(data[8])
1120 {
1121 case 1:
1122 {
1123 if (target != CALC_TI92P)
1124 {
1125 return 0;
1126 }
1127 break;
1128 }
1129 case 3:
1130 {
1131 if (target != CALC_TI89)
1132 {
1133 return 0;
1134 }
1135 break;
1136 }
1137 case 8:
1138 {
1139 if (target != CALC_V200)
1140 {
1141 return 0;
1142 }
1143 break;
1144 }
1145 case 9:
1146 {
1147 if (target != CALC_TI89T)
1148 {
1149 return 0;
1150 }
1151 break;
1152 }
1153 default:
1154 return 0;
1155 }
1156
1157 return !0;
1158 }
1159 else
1160 {
1161 return tifiles_file_is_os(filename);
1162 }
1163 }
1164
1165 if (type & TIFILE_APP)
1166 {
1167 if(target && tifiles_file_has_tifl_header(filename, &ctype, &dtype))
1168 {
1169 return (ctype == tifiles_model_to_dev_type(target) && dtype == TI83p_APPL);
1170 }
1171 else
1172 {
1173 return tifiles_file_is_app(filename);
1174 }
1175 }
1176
1177 if (type & TIFILE_FLASH)
1178 {
1179 return tifiles_file_test(filename, TIFILE_OS, target) || tifiles_file_test(filename, TIFILE_APP, target);
1180 }
1181
1182 if (type & TIFILE_TIGROUP)
1183 {
1184 if (target)
1185 {
1186 // No easy/light way for this part: we have to load the whole file
1187 // and to parse the TigEntry structures.
1188 TigContent *content;
1189 int ret, ok=0;
1190 unsigned int k;
1191
1192 if (!tifiles_file_has_tig_header(filename))
1193 {
1194 return 0;
1195 }
1196
1197 content = tifiles_content_create_tigroup(CALC_NONE, 0);
1198 ret = tifiles_file_read_tigroup(filename, content);
1199 if (ret)
1200 {
1201 tifiles_content_delete_tigroup(content);
1202 return 0;
1203 }
1204
1205 for (k = 0; k < content->n_apps; k++)
1206 {
1207 TigEntry *te = content->app_entries[k];
1208
1209 if(tifiles_calc_are_compat(te->content.regular->model, target))
1210 {
1211 ok++;
1212 }
1213 }
1214
1215 for (k = 0; k < content->n_vars; k++)
1216 {
1217 TigEntry *te = content->var_entries[k];
1218
1219 if(tifiles_calc_are_compat(te->content.regular->model, target))
1220 {
1221 ok++;
1222 }
1223 }
1224
1225 tifiles_content_delete_tigroup(content);
1226 return ok;
1227 }
1228 else
1229 {
1230 return tifiles_file_is_tigroup(filename);
1231 }
1232 }
1233
1234 return 0;
1235 }
1236
1237 /********/
1238 /* Misc */
1239 /********/
1240
1241 /**
1242 * tifiles_fext_to_model:
1243 * @filename: a file extension.
1244 *
1245 * Returns the calculator model corresponding best to this file extension.
1246 *
1247 * Return value: a model taken in #CalcModel.
1248 **/
tifiles_fext_to_model(const char * ext)1249 TIEXPORT2 CalcModel TICALL tifiles_fext_to_model(const char *ext)
1250 {
1251 int type = CALC_NONE;
1252
1253 if (ext == NULL)
1254 {
1255 tifiles_critical("%s(NULL)", __FUNCTION__);
1256 return CALC_NONE;
1257 }
1258
1259 if (ext[0] != 0 && ext[1] != 0 && ext[2] != 0)
1260 {
1261 char c1 = g_ascii_tolower(ext[0]);
1262 char c2 = g_ascii_tolower(ext[1]);
1263 char c3 = g_ascii_tolower(ext[2]);
1264
1265 if (c1 == '7' && c2 == '3')
1266 {
1267 type = CALC_TI73;
1268 }
1269 else if (c1 == '8')
1270 {
1271 if (c2 == '2')
1272 {
1273 if (c3 == 'u')
1274 {
1275 type = CALC_TI82A_USB;
1276 }
1277 else
1278 {
1279 type = CALC_TI82;
1280 }
1281 }
1282 else if (c2 == '3')
1283 {
1284 type = CALC_TI83;
1285 }
1286 else if (c2 == 'x')
1287 {
1288 type = CALC_TI83P;
1289 }
1290 else if (c2 == 'c')
1291 {
1292 type = CALC_TI84PC;
1293 }
1294 else if (c2 == 'p')
1295 {
1296 type = CALC_TI83PCE_USB;
1297 }
1298 else if (c2 == 'e')
1299 {
1300 type = CALC_TI84PCE_USB;
1301 }
1302 else if (c2 == '5')
1303 {
1304 type = CALC_TI85;
1305 }
1306 else if (c2 == '6')
1307 {
1308 type = CALC_TI86;
1309 }
1310 else if (c2 == '9')
1311 {
1312 type = CALC_TI89;
1313 }
1314 // else fall through.
1315 }
1316 else if (c1 == '9')
1317 {
1318 if (c2 == '2')
1319 {
1320 type = CALC_TI92;
1321 }
1322 else if (c2 == 'x')
1323 {
1324 type = CALC_TI92P;
1325 }
1326 // else fall through.
1327 }
1328 else if (c1 == 'v' && c2 == '2')
1329 {
1330 type = CALC_V200;
1331 }
1332 //else if (!g_ascii_strcasecmp(str, "tib"))
1333 //type = CALC_TI89; // consider .tib as TI89
1334 else if (c1 == 't')
1335 {
1336 if (c2 == 'n' || c2 == 'c' || c2 == 'm')
1337 {
1338 if (c3 == 's' || c3 == 'c' || c3 == 'o')
1339 {
1340 type = CALC_NSPIRE;
1341 }
1342 // else fall through.
1343 }
1344 // else fall through.
1345 }
1346 // else fall through.
1347 }
1348
1349 return type;
1350 }
1351
1352 /* Note: a better way should be to open the file and read the signature */
1353 /**
1354 * tifiles_file_get_model:
1355 * @filename: a filename as string.
1356 *
1357 * Returns the calculator model targeted by this file.
1358 *
1359 * Return value: a model taken in #CalcModel.
1360 **/
tifiles_file_get_model(const char * filename)1361 TIEXPORT2 CalcModel TICALL tifiles_file_get_model(const char *filename)
1362 {
1363 char *e = tifiles_fext_get(filename);
1364 return tifiles_fext_to_model(e);
1365 }
1366
1367 /**
1368 * tifiles_file_get_class:
1369 * @filename: a filename as string.
1370 *
1371 * Returns the file class (single, group, backup, flash, tigroup).
1372 *
1373 * Return value: a value in #FileClass.
1374 **/
tifiles_file_get_class(const char * filename)1375 TIEXPORT2 FileClass TICALL tifiles_file_get_class(const char *filename)
1376 {
1377 if (tifiles_file_is_single(filename))
1378 {
1379 return TIFILE_SINGLE;
1380 }
1381 else if (tifiles_file_is_group(filename))
1382 {
1383 return TIFILE_GROUP;
1384 }
1385 else if (tifiles_file_is_backup(filename))
1386 {
1387 return TIFILE_BACKUP;
1388 }
1389 else if (tifiles_file_is_flash(filename))
1390 {
1391 return TIFILE_FLASH;
1392 }
1393 else if (tifiles_file_is_tigroup(filename))
1394 {
1395 return TIFILE_TIGROUP;
1396 }
1397 else
1398 {
1399 return 0;
1400 }
1401 }
1402
1403 /**
1404 * tifiles_file_get_type:
1405 * @filename: a filename as string.
1406 *
1407 * Returns the type of file (function, program, ...).
1408 *
1409 * Return value: a string like "Assembly Program" (localized).
1410 **/
tifiles_file_get_type(const char * filename)1411 TIEXPORT2 const char *TICALL tifiles_file_get_type(const char *filename)
1412 {
1413 char *e = tifiles_fext_get(filename);
1414 #ifdef CHECK_FILE_EXTENSIONS
1415 if (e[0] == 0)
1416 {
1417 return "";
1418 }
1419 #endif
1420 if ( !g_ascii_strcasecmp(e, "tib")
1421 || !g_ascii_strcasecmp(e, "tno") || !g_ascii_strcasecmp(e, "tnc")
1422 || !g_ascii_strcasecmp(e, "tco") || !g_ascii_strcasecmp(e, "tcc")
1423 || !g_ascii_strcasecmp(e, "tmo") || !g_ascii_strcasecmp(e, "tmc")
1424 )
1425 {
1426 return _("OS upgrade");
1427 }
1428
1429 if (!tifiles_file_is_ti(filename))
1430 {
1431 return "";
1432 }
1433
1434 if (tifiles_file_is_tigroup(filename))
1435 {
1436 return _("TIGroup");
1437 }
1438
1439 if (tifiles_file_is_group(filename))
1440 {
1441 switch (tifiles_file_get_model(filename))
1442 {
1443 case CALC_TI89:
1444 case CALC_TI89T:
1445 case CALC_TI89T_USB:
1446 case CALC_TI92P:
1447 case CALC_V200:
1448 return _("Group/Backup");
1449 default:
1450 return _("Group");
1451 }
1452 }
1453
1454 switch (tifiles_file_get_model(filename))
1455 {
1456 #ifndef DISABLE_TI8X
1457 case CALC_TI73:
1458 return tixx_byte2desc(TI73_CONST, TI73_MAXTYPES, tixx_fext2byte(TI73_CONST, TI73_MAXTYPES, e));
1459 case CALC_TI82:
1460 return tixx_byte2desc(TI82_CONST, TI82_MAXTYPES, tixx_fext2byte(TI82_CONST, TI82_MAXTYPES, e));
1461 case CALC_TI83:
1462 return tixx_byte2desc(TI83_CONST, TI83_MAXTYPES, tixx_fext2byte(TI83_CONST, TI83_MAXTYPES, e));
1463 case CALC_TI83P:
1464 return ti83p_byte2desc(ti83p_fext2byte(e));
1465 case CALC_TI84P:
1466 case CALC_TI84P_USB:
1467 return ti84p_byte2desc(ti84p_fext2byte(e));
1468 case CALC_TI82A_USB:
1469 return ti82a_byte2desc(ti82a_fext2byte(e));
1470 case CALC_TI84PT_USB:
1471 return ti84pt_byte2desc(ti84pt_fext2byte(e));
1472 case CALC_TI84PC:
1473 case CALC_TI84PC_USB:
1474 return ti84pc_byte2desc(ti84pc_fext2byte(e));
1475 case CALC_TI83PCE_USB:
1476 return ti83pce_byte2desc(ti83pce_fext2byte(e));
1477 case CALC_TI84PCE_USB:
1478 return ti84pce_byte2desc(ti84pce_fext2byte(e));
1479 case CALC_TI85:
1480 return tixx_byte2desc(TI85_CONST, TI85_MAXTYPES, tixx_fext2byte(TI85_CONST, TI85_MAXTYPES, e));
1481 case CALC_TI86:
1482 return tixx_byte2desc(TI86_CONST, TI86_MAXTYPES, tixx_fext2byte(TI86_CONST, TI86_MAXTYPES, e));
1483 #endif
1484 #ifndef DISABLE_TI9X
1485 case CALC_TI89:
1486 return ti89_byte2desc(ti89_fext2byte(e));
1487 case CALC_TI89T:
1488 case CALC_TI89T_USB:
1489 return ti89t_byte2desc(ti89_fext2byte(e));
1490 case CALC_TI92:
1491 return ti92_byte2desc(ti92_fext2byte(e));
1492 case CALC_TI92P:
1493 return ti92p_byte2desc(ti92p_fext2byte(e));
1494 case CALC_V200:
1495 return v200_byte2desc(v200_fext2byte(e));
1496 #endif
1497 case CALC_NSPIRE:
1498 return tixx_byte2desc(NSP_CONST, NSP_MAXTYPES, tixx_fext2byte(NSP_CONST, NSP_MAXTYPES, e));
1499 case CALC_NONE:
1500 default:
1501 return "";
1502 }
1503
1504 return "";
1505 }
1506
1507 /**
1508 * tifiles_file_get_icon:
1509 * @filename: a filename as string.
1510 *
1511 * Returns the type of file (function, program, ...).
1512 *
1513 * Return value: a string like "Assembly Program" (non localized).
1514 **/
tifiles_file_get_icon(const char * filename)1515 TIEXPORT2 const char *TICALL tifiles_file_get_icon(const char *filename)
1516 {
1517 char *e = tifiles_fext_get(filename);
1518 #ifdef CHECK_FILE_EXTENSIONS
1519 if (e[0] == 0)
1520 {
1521 return "";
1522 }
1523 #endif
1524 if ( !g_ascii_strcasecmp(e, "tib")
1525 || !g_ascii_strcasecmp(e, "tno") || !g_ascii_strcasecmp(e, "tnc")
1526 || !g_ascii_strcasecmp(e, "tco") || !g_ascii_strcasecmp(e, "tcc")
1527 || !g_ascii_strcasecmp(e, "tmo") || !g_ascii_strcasecmp(e, "tmc")
1528 )
1529 {
1530 return _("OS upgrade");
1531 }
1532
1533 if (!tifiles_file_is_ti(filename))
1534 {
1535 return "";
1536 }
1537
1538 if (tifiles_file_is_tigroup(filename))
1539 {
1540 return _("TIGroup");
1541 }
1542
1543 if (tifiles_file_is_group(filename))
1544 {
1545 switch (tifiles_file_get_model(filename))
1546 {
1547 case CALC_TI89:
1548 case CALC_TI89T:
1549 case CALC_TI89T_USB:
1550 case CALC_TI92P:
1551 case CALC_V200:
1552 return _("Group/Backup");
1553 default:
1554 return _("Group");
1555 }
1556 }
1557
1558 switch (tifiles_file_get_model(filename))
1559 {
1560 #ifndef DISABLE_TI8X
1561 case CALC_TI73:
1562 return tixx_byte2icon(TI73_CONST, TI73_MAXTYPES, tixx_fext2byte(TI73_CONST, TI73_MAXTYPES, e));
1563 case CALC_TI82:
1564 return tixx_byte2icon(TI82_CONST, TI82_MAXTYPES, tixx_fext2byte(TI82_CONST, TI82_MAXTYPES, e));
1565 case CALC_TI83:
1566 return tixx_byte2icon(TI83_CONST, TI83_MAXTYPES, tixx_fext2byte(TI83_CONST, TI83_MAXTYPES, e));
1567 case CALC_TI83P:
1568 return ti83p_byte2icon(ti83p_fext2byte(e));
1569 case CALC_TI84P:
1570 case CALC_TI84P_USB:
1571 return ti84p_byte2icon(ti83p_fext2byte(e));
1572 case CALC_TI82A_USB:
1573 return ti82a_byte2icon(ti82a_fext2byte(e));
1574 case CALC_TI84PT_USB:
1575 return ti84pt_byte2icon(ti84pt_fext2byte(e));
1576 case CALC_TI84PC:
1577 case CALC_TI84PC_USB:
1578 return ti84pc_byte2icon(ti84pc_fext2byte(e));
1579 case CALC_TI83PCE_USB:
1580 return ti83pce_byte2icon(ti83pce_fext2byte(e));
1581 case CALC_TI84PCE_USB:
1582 return ti84pce_byte2icon(ti84pce_fext2byte(e));
1583 case CALC_TI85:
1584 return tixx_byte2icon(TI85_CONST, TI85_MAXTYPES, tixx_fext2byte(TI85_CONST, TI85_MAXTYPES, e));
1585 case CALC_TI86:
1586 return tixx_byte2icon(TI86_CONST, TI86_MAXTYPES, tixx_fext2byte(TI86_CONST, TI86_MAXTYPES, e));
1587 #endif
1588 #ifndef DISABLE_TI9X
1589 case CALC_TI89:
1590 return ti89_byte2icon(ti89_fext2byte(e));
1591 case CALC_TI89T:
1592 case CALC_TI89T_USB:
1593 return ti89t_byte2icon(ti89_fext2byte(e));
1594 case CALC_TI92:
1595 return ti92_byte2icon(ti92_fext2byte(e));
1596 case CALC_TI92P:
1597 return ti92p_byte2icon(ti92p_fext2byte(e));
1598 case CALC_V200:
1599 return v200_byte2icon(v200_fext2byte(e));
1600 #endif
1601 case CALC_NSPIRE:
1602 return tixx_byte2icon(NSP_CONST, NSP_MAXTYPES, tixx_fext2byte(NSP_CONST, NSP_MAXTYPES, e));
1603 case CALC_NONE:
1604 default:
1605 return "";
1606 }
1607
1608 return "";
1609 }
1610