1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id$ */
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 /*
23 TI File Format handling routines
24 Calcs: 89/89tm/92/92+/V200
25 */
26
27 #include <glib/gstdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <time.h>
31
32 #include <ticonv.h>
33 #include "tifiles.h"
34 #include "error.h"
35 #include "logging.h"
36 #include "macros.h"
37 #include "typesxx.h"
38 #include "files9x.h"
39 #include "rwfile.h"
40
41 #ifndef DISABLE_TI9X
42
43 /********/
44 /* Misc */
45 /********/
46
47 static int fsignature[2] = { 1, 0 };
48
49 /***********/
50 /* Reading */
51 /***********/
52
53 /**
54 * ti9x_file_read_regular:
55 * @filename: name of single/group file to open.
56 * @content: where to store the file content.
57 *
58 * Load the single/group file into a Ti9xRegular structure.
59 *
60 * Structure content must be freed with #tifiles_content_delete_regular when
61 * no longer used. If error occurs, the structure content is released for you.
62 *
63 * Return value: an error code, 0 otherwise.
64 **/
ti9x_file_read_regular(const char * filename,Ti9xRegular * content)65 int ti9x_file_read_regular(const char *filename, Ti9xRegular *content)
66 {
67 FILE *f;
68 long cur_pos;
69 char default_folder[FLDNAME_MAX];
70 char current_folder[FLDNAME_MAX];
71 uint32_t curr_offset = 0;
72 uint32_t next_offset = 0;
73 uint32_t file_size;
74 uint16_t tmp;
75 unsigned int i, j;
76 char signature[9];
77 char varname[VARNAME_MAX];
78 int ret = ERR_FILE_IO;
79
80 if (content == NULL)
81 {
82 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
83 return ERR_INVALID_FILE;
84 }
85
86 if (!tifiles_file_is_regular(filename))
87 {
88 ret = ERR_INVALID_FILE;
89 goto tfrr2;
90 }
91
92 f = g_fopen(filename, "rb");
93 if (f == NULL)
94 {
95 ret = ERR_FILE_OPEN;
96 goto tfrr2;
97 }
98
99 // Get file size, then rewind.
100 if (fseek(f, 0, SEEK_END) < 0) goto tfrr;
101 cur_pos = ftell(f);
102 if (cur_pos < 0) goto tfrr;
103 if (fseek(f, 0, SEEK_SET) < 0) goto tfrr;
104
105 // The TI-68k series' members have at best 4 MB of Flash (TODO: modify this code if this no longer holds).
106 // Regular / group files larger than that size are highly dubious, files larger than twice that size are insane.
107 if (cur_pos >= (8L << 20))
108 {
109 ret = ERR_INVALID_FILE;
110 goto tfrr;
111 }
112 file_size = (uint32_t)cur_pos;
113
114 if (fread_8_chars(f, signature) < 0) goto tfrr; // Offset 0
115 content->model = tifiles_signature2calctype(signature);
116 if (content->model == CALC_NONE)
117 {
118 ret = ERR_INVALID_FILE;
119 goto tfrr;
120 }
121 if (content->model_dst == CALC_NONE)
122 {
123 content->model_dst = content->model;
124 }
125
126 if (fread_word(f, NULL) < 0) goto tfrr; // Offset 0x8
127 if (fread_8_chars(f, default_folder) < 0) goto tfrr; // Offset 0xA
128 ticonv_varname_from_tifile_sn(content->model_dst, default_folder, content->default_folder, sizeof(content->default_folder), -1);
129 strncpy(current_folder, content->default_folder, sizeof(current_folder) - 1);
130 current_folder[sizeof(current_folder) - 1] = 0;
131 if (fread_n_chars(f, 40, content->comment) < 0) goto tfrr; // Offset 0x12
132 if (fread_word(f, &tmp) < 0) goto tfrr; // Offset 0x3A
133 content->num_entries = tmp;
134
135 content->entries = g_malloc0((content->num_entries + 1) * sizeof(VarEntry*));
136 if (content->entries == NULL)
137 {
138 ret = ERR_MALLOC;
139 goto tfrr;
140 }
141
142 for (i = 0, j = 0; i < content->num_entries; i++)
143 {
144 VarEntry *entry = content->entries[j] = g_malloc0(sizeof(VarEntry));
145
146 if (fread_long(f, &curr_offset) < 0) goto tfrr; // Offset N, 0x3C for the first entry
147 if (curr_offset > file_size)
148 {
149 ret = ERR_INVALID_FILE;
150 goto tfrr;
151 }
152 if (fread_8_chars(f, varname) < 0) goto tfrr; // Offset N+4, 0x40 for the first entry
153 ticonv_varname_from_tifile_sn(content->model_dst, varname, entry->name, sizeof(entry->name), entry->type);
154 if (fread_byte(f, &(entry->type)) < 0) goto tfrr; // Offset N+12, 0x48 for the first entry
155 if (fread_byte(f, &(entry->attr)) < 0) goto tfrr; // Offset N+13, 0x49 for the first entry
156 entry->attr = (entry->attr == 2 || entry->attr == 3) ? ATTRB_ARCHIVED : entry->attr;
157 if (fread_word(f, NULL) < 0) goto tfrr; // Offset N+14, 0x4A for the first entry
158
159 if (entry->type == TI92_DIR) // same as TI89_DIR, TI89t_DIR, ...
160 {
161 strncpy(current_folder, entry->name,sizeof(current_folder) - 1);
162 current_folder[sizeof(current_folder) - 1] = 0;
163 g_free(entry);
164 continue; // folder: skip entry
165 }
166 else
167 {
168 uint16_t checksum, sum = 0;
169
170 j++;
171 strncpy(entry->folder, current_folder, sizeof(entry->folder) - 1);
172 current_folder[sizeof(entry->folder) - 1] = 0;
173 cur_pos = ftell(f);
174 if (cur_pos < 0) goto tfrr;
175 if (fread_long(f, &next_offset) < 0) goto tfrr; // Offset N+16, 0x4C for the first entry
176 if (next_offset > file_size)
177 {
178 ret = ERR_INVALID_FILE;
179 goto tfrr;
180 }
181 entry->size = next_offset - curr_offset - 4 - 2;
182 if (entry->size > file_size)
183 {
184 ret = ERR_INVALID_FILE;
185 goto tfrr;
186 }
187 entry->data = (uint8_t *)g_malloc0(entry->size);
188 if (entry->data == NULL)
189 {
190 ret = ERR_MALLOC;
191 goto tfrr;
192 }
193
194 if (fseek(f, curr_offset, SEEK_SET)) goto tfrr;
195 if (fread_long(f, NULL) < 0) goto tfrr; // Normally: offset N+22, 0x52 for the first entry
196 if (fread(entry->data, 1, entry->size, f) < entry->size) goto tfrr; // Normally: offset N+26, 0x56 for the first entry
197
198 if (fread_word(f, &checksum) < 0) goto tfrr;
199 if (fseek(f, cur_pos, SEEK_SET)) goto tfrr;
200
201 sum = tifiles_checksum(entry->data, entry->size);
202 if (sum != checksum)
203 {
204 ret = ERR_FILE_CHECKSUM;
205 goto tfrr;
206 }
207 content->checksum += sum; // sum of all checksums but unused
208 }
209 }
210 content->num_entries = j;
211 content->entries = g_realloc(content->entries, content->num_entries * sizeof(VarEntry*));
212 //fread_long(f, &next_offset);
213 //fseek(f, next_offset - 2, SEEK_SET);
214 //fread_word(f, &(content->checksum));
215
216 fclose(f);
217 return 0;
218
219 tfrr: // release on exit
220 tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
221 fclose(f);
222 tfrr2:
223 tifiles_content_delete_regular(content);
224 return ret;
225 }
226
227 /**
228 * ti9x_file_read_backup:
229 * @filename: name of backup file to open.
230 * @content: where to store the file content.
231 *
232 * Load the backup file into a Ti9xBackup structure.
233 *
234 * Structure content must be freed with #tifiles_content_delete_backup when
235 * no longer used. If error occurs, the structure content is released for you.
236 *
237 * Return value: an error code, 0 otherwise.
238 **/
ti9x_file_read_backup(const char * filename,Ti9xBackup * content)239 int ti9x_file_read_backup(const char *filename, Ti9xBackup *content)
240 {
241 FILE *f;
242 long cur_pos = 0;
243 uint32_t file_size;
244 char signature[9];
245 uint16_t sum;
246 int ret = ERR_FILE_IO;
247
248 if (content == NULL)
249 {
250 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
251 return ERR_INVALID_FILE;
252 }
253
254 if (!tifiles_file_is_backup(filename))
255 {
256 ret = ERR_INVALID_FILE;
257 goto tfrb2;
258 }
259
260 f = g_fopen(filename, "rb");
261 if (f == NULL)
262 {
263 tifiles_info( "Unable to open this file: %s", filename);
264 ret = ERR_FILE_OPEN;
265 goto tfrb2;
266 }
267
268 // Get file size, then rewind.
269 if (fseek(f, 0, SEEK_END) < 0) goto tfrb;
270 cur_pos = ftell(f);
271 if (cur_pos < 0) goto tfrb;
272 if (fseek(f, 0, SEEK_SET) < 0) goto tfrb;
273
274 // The TI-68k series' members have at best 4 MB of Flash (TODO: modify this code if this no longer holds).
275 // Backup files larger than that size are highly dubious, files larger than twice that size are insane.
276 if (cur_pos >= (8L << 20))
277 {
278 ret = ERR_INVALID_FILE;
279 goto tfrb;
280 }
281
282 if (fread_8_chars(f, signature) < 0) goto tfrb;
283 content->model = tifiles_signature2calctype(signature);
284 if (content->model == CALC_NONE)
285 {
286 ret = ERR_INVALID_FILE;
287 }
288
289 if (fread_word(f, NULL) < 0) goto tfrb;
290 if (fread_8_chars(f, NULL) < 0) goto tfrb;
291 if (fread_n_chars(f, 40, content->comment) < 0) goto tfrb;
292 if (fread_word(f, NULL) < 0) goto tfrb;
293 if (fread_long(f, NULL) < 0) goto tfrb;
294 if (fread_8_chars(f, content->rom_version) < 0) goto tfrb;
295 if (fread_byte(f, &(content->type)) < 0) goto tfrb;
296 if (fread_byte(f, NULL) < 0) goto tfrb;
297 if (fread_word(f, NULL) < 0) goto tfrb;
298 if (fread_long(f, &file_size) < 0) goto tfrb;
299 if (file_size > (uint32_t)cur_pos)
300 {
301 ret = ERR_INVALID_FILE;
302 goto tfrb;
303 }
304 content->data_length = file_size - 0x52 - 2;
305 if (fread_word(f, NULL) < 0) goto tfrb;
306
307 content->data_part = (uint8_t *)g_malloc0(content->data_length);
308 if (content->data_part == NULL)
309 {
310 ret = ERR_MALLOC;
311 goto tfrb;
312 }
313
314 if (fread(content->data_part, 1, content->data_length, f) < content->data_length) goto tfrb;
315 if (fread_word(f, &(content->checksum)) < 0) goto tfrb;
316
317 sum = tifiles_checksum(content->data_part, content->data_length);
318 #if defined(CHECKSUM_ENABLED)
319 if (sum != content->checksum)
320 {
321 ret = ERR_FILE_CHECKSUM;
322 goto tfrb;
323 }
324 #endif
325
326 fclose(f);
327 return 0;
328
329 tfrb: // release on exit
330 tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
331 fclose(f);
332 tfrb2:
333 tifiles_content_delete_backup(content);
334 return ret;
335 }
336
check_device_type(uint8_t id)337 static int check_device_type(uint8_t id)
338 {
339 static const uint8_t types[] = { 0, DEVICE_TYPE_89, DEVICE_TYPE_92P };
340 int i;
341
342 for (i = 1; i < (int)(sizeof(types)/sizeof(types[0])); i++)
343 {
344 if (types[i] == id)
345 {
346 return i;
347 }
348 }
349
350 return 0;
351 }
352
check_data_type(uint8_t id)353 static int check_data_type(uint8_t id)
354 {
355 static const uint8_t types[] = { 0, TI89_AMS, TI89_APPL, TI89_CERTIF, TI89_LICENSE };
356 int i;
357
358 for (i = 1; i < (int)(sizeof(types)/sizeof(types[0])); i++)
359 {
360 if (types[i] == id)
361 {
362 return i;
363 }
364 }
365
366 return 0;
367 }
368
369 /**
370 * ti9x_file_read_flash:
371 * @filename: name of flash file to open.
372 * @content: where to store the file content.
373 *
374 * Load the flash file into a #FlashContent structure.
375 *
376 * Structure content must be freed with #tifiles_content_delete_flash when
377 * no longer used. If error occurs, the structure content is released for you.
378 *
379 * Return value: an error code, 0 otherwise.
380 **/
ti9x_file_read_flash(const char * filename,Ti9xFlash * head)381 int ti9x_file_read_flash(const char *filename, Ti9xFlash *head)
382 {
383 FILE *f;
384 Ti9xFlash *content = head;
385 long cur_pos = 0;
386 int tib = 0;
387 char signature[9];
388 int ret = ERR_FILE_IO;
389
390 if (head == NULL)
391 {
392 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
393 return ERR_INVALID_FILE;
394 }
395
396 if (!tifiles_file_is_flash(filename) && !tifiles_file_is_tib(filename))
397 {
398 ret = ERR_INVALID_FILE;
399 goto tfrf2;
400 }
401
402 // detect file type (old or new format)
403 tib = tifiles_file_is_tib(filename);
404
405 f = g_fopen(filename, "rb");
406 if (f == NULL)
407 {
408 tifiles_info("Unable to open this file: %s", filename);
409 ret = ERR_FILE_OPEN;
410 goto tfrf2;
411 }
412
413 if (fseek(f, 0, SEEK_END)) goto tfrf;
414 cur_pos = ftell(f);
415 if (cur_pos < 0) goto tfrf;
416 if (fseek(f, 0, SEEK_SET)) goto tfrf;
417
418 // The TI-68k series' members have at best 4 MB of Flash.
419 // TIB files larger than that size are insane.
420 if (cur_pos >= (4L << 20))
421 {
422 ret = ERR_INVALID_FILE;
423 goto tfrf;
424 }
425
426 if (tib)
427 {
428 // tib is an old format but mainly used by developers
429 memset(content, 0, sizeof(Ti9xFlash));
430
431 content->data_length = (uint32_t)cur_pos;
432
433 strncpy(content->name, "basecode", sizeof(content->name) - 1);
434 content->name[sizeof(content->name) - 1] = 0;
435 content->data_type = 0x23; // FLASH os
436
437 content->data_part = (uint8_t *)g_malloc0(content->data_length);
438 if (content->data_part == NULL)
439 {
440 ret = ERR_MALLOC;
441 goto tfrf;
442 }
443
444 if (fread(content->data_part, 1, content->data_length, f) < content->data_length) goto tfrf;
445 switch(content->data_part[8])
446 {
447 case 1: content->device_type = DEVICE_TYPE_92P; break; // TI92+
448 case 3: content->device_type = DEVICE_TYPE_89; break; // TI89
449 // value added by the TI community according to HWID parameter
450 // doesn't have any 'legal' existence.
451 case 8: content->device_type = DEVICE_TYPE_92P; break; // V200PLT
452 case 9: content->device_type = DEVICE_TYPE_89; break; // Titanium
453 }
454
455 content->next = NULL;
456 }
457 else
458 {
459 for (content = head;; content = content->next)
460 {
461 if (fread_8_chars(f, signature) < 0) goto tfrf;
462 content->model = tifiles_file_get_model(filename);
463 if (fread_byte(f, &(content->revision_major)) < 0) goto tfrf;
464 if (fread_byte(f, &(content->revision_minor)) < 0) goto tfrf;
465 if (fread_byte(f, &(content->flags)) < 0) goto tfrf;
466 if (fread_byte(f, &(content->object_type)) < 0) goto tfrf;
467 if (fread_byte(f, &(content->revision_day)) < 0) goto tfrf;
468 if (fread_byte(f, &(content->revision_month)) < 0) goto tfrf;
469 if (fread_word(f, &(content->revision_year)) < 0) goto tfrf;
470 if (fskip(f, 1) < 0) goto tfrf;
471 if (fread_8_chars(f, content->name) < 0) goto tfrf;
472 if (fskip(f, 23) < 0) goto tfrf;
473 if (fread_byte(f, &(content->device_type)) < 0) goto tfrf;
474 if (fread_byte(f, &(content->data_type)) < 0) goto tfrf;
475 if (fskip(f, 23) < 0) goto tfrf;
476 if (fread_byte(f, &(content->hw_id)) < 0) goto tfrf;
477 if (fread_long(f, &(content->data_length)) < 0) goto tfrf;
478
479 if (content->data_type != TI89_LICENSE && !check_device_type(content->device_type))
480 {
481 ret = ERR_INVALID_FILE;
482 goto tfrf;
483 }
484 if (!check_data_type(content->data_type))
485 {
486 ret = ERR_INVALID_FILE;
487 goto tfrf;
488 }
489 // TODO: modify this code if TI ever makes a TI-eZ80 model with more than 4 MB of Flash memory...
490 if (content->data_length > 4U * 1024 * 1024 - 65536U)
491 {
492 // Data length larger than Flash memory size - boot code sector size doesn't look right.
493 ret = ERR_INVALID_FILE;
494 goto tfrf;
495 }
496
497 content->data_part = (uint8_t *)g_malloc0(content->data_length);
498 if (content->data_part == NULL)
499 {
500 ret = ERR_MALLOC;
501 goto tfrf;
502 }
503
504 if (fread(content->data_part, 1, content->data_length, f) < content->data_length) goto tfrf;
505 if ( (content->data_type == TI83p_AMS && content->data_part[0] != 0x80)
506 || (content->data_type == TI83p_APPL && content->data_part[0] != 0x81))
507 {
508 ret = ERR_INVALID_FILE;
509 goto tfrf;
510 }
511 content->next = NULL;
512
513 // check for end of file
514 if (fread_8_chars(f, signature) < 0)
515 {
516 break;
517 }
518 if (strcmp(signature, "**TIFL**") || feof(f))
519 {
520 break;
521 }
522 if (fseek(f, -8, SEEK_CUR)) goto tfrf;
523
524 content->next = (Ti9xFlash *)g_malloc0(sizeof(Ti9xFlash));
525 if (content->next == NULL)
526 {
527 ret = ERR_MALLOC;
528 goto tfrf;
529 }
530 }
531 }
532
533 fclose(f);
534 return 0;
535
536 tfrf: // release on exit
537 tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
538 fclose(f);
539 tfrf2:
540 tifiles_content_delete_flash(content);
541 return ret;
542 }
543
544 /***********/
545 /* Writing */
546 /***********/
547
548 /**
549 * ti9x_file_write_regular:
550 * @filename: name of single/group file where to write or NULL.
551 * @content: the file content to write.
552 * @real_filename: pointer address or NULL. Must be freed if needed when no longer needed.
553 *
554 * Write one (or several) variable(s) into a single (group) file. If filename is set to NULL,
555 * the function build a filename from varname and allocates resulting filename in %real_fname.
556 * %filename and %real_filename can be NULL but not both !
557 *
558 * %real_filename must be freed when no longer used.
559 *
560 * Return value: an error code, 0 otherwise.
561 **/
ti9x_file_write_regular(const char * fname,Ti9xRegular * content,char ** real_fname)562 int ti9x_file_write_regular(const char *fname, Ti9xRegular *content, char **real_fname)
563 {
564 FILE *f;
565 unsigned int i;
566 char *filename = NULL;
567 uint32_t offset = 0x52;
568 int **table;
569 unsigned int num_folders;
570 char default_folder[FLDNAME_MAX];
571 char fldname[FLDNAME_MAX], varname[VARNAME_MAX];
572
573 if (content->entries == NULL)
574 {
575 tifiles_warning("%s: skipping content with NULL content->entries", __FUNCTION__);
576 return 0;
577 }
578
579 if (fname != NULL)
580 {
581 filename = g_strdup(fname);
582 if (filename == NULL)
583 {
584 return ERR_MALLOC;
585 }
586 }
587 else
588 {
589 if (content->entries[0])
590 {
591 filename = tifiles_build_filename(content->model_dst, content->entries[0]);
592 }
593 else
594 {
595 tifiles_warning("%s: asked to build a filename from null content->entries[0], bailing out", __FUNCTION__);
596 if (real_fname != NULL)
597 {
598 *real_fname = NULL;
599 }
600 return 0;
601 }
602 if (real_fname != NULL)
603 {
604 *real_fname = g_strdup(filename);
605 }
606 }
607
608 // build the table of folder & variable entries
609 table = tifiles_create_table_of_entries((FileContent *)content, &num_folders);
610 if (table == NULL)
611 {
612 g_free(filename);
613 return ERR_MALLOC;
614 }
615
616 f = g_fopen(filename, "wb");
617 if (f == NULL)
618 {
619 tifiles_info( "Unable to open this file: %s", filename);
620 tifiles_free_table_of_entries(table);
621 g_free(filename);
622 return ERR_FILE_OPEN;
623 }
624
625 // write header
626 if (fwrite_8_chars(f, tifiles_calctype2signature(content->model)) < 0) goto tfwr;
627 if (fwrite(fsignature, 1, 2, f) < 2) goto tfwr;
628 if (content->num_entries == 1) // folder entry for single var is placed here
629 {
630 strncpy(content->default_folder, content->entries[0]->folder, sizeof(content->default_folder) - 1);
631 content->default_folder[sizeof(content->default_folder) - 1] = 0;
632 }
633 ticonv_varname_to_tifile_sn(content->model, content->default_folder, default_folder, sizeof(default_folder), -1);
634 if (fwrite_8_chars(f, default_folder) < 0) goto tfwr;
635 if (fwrite_n_bytes(f, 40, (uint8_t *)content->comment) < 0) goto tfwr;
636 if (content->num_entries > 1)
637 {
638 if (fwrite_word(f, (uint16_t) (content->num_entries + num_folders)) < 0) goto tfwr;
639 offset += 16 * (content->num_entries + num_folders - 1);
640 }
641 else
642 {
643 if (fwrite_word(f, 1) < 0) goto tfwr;
644 }
645
646 // write table of entries
647 for (i = 0; table[i] != NULL; i++)
648 {
649 VarEntry *fentry;
650 int j, idx = table[i][0];
651 fentry = content->entries[idx];
652
653 if (fentry == NULL)
654 {
655 tifiles_warning("%s: skipping null content entry %d", __FUNCTION__, i);
656 continue;
657 }
658
659 if (content->num_entries > 1) // single var does not have folder entry
660 {
661 if (fwrite_long(f, offset) < 0) goto tfwr;
662 ticonv_varname_to_tifile_sn(content->model, fentry->folder, fldname, sizeof(fldname), -1);
663 if (fwrite_8_chars(f, fldname) < 0) goto tfwr;
664 if (fwrite_byte(f, (uint8_t)tifiles_folder_type(content->model)) < 0) goto tfwr;
665 if (fwrite_byte(f, 0x00) < 0) goto tfwr;
666 for (j = 0; table[i][j] != -1; j++);
667 if (fwrite_word(f, (uint16_t) j) < 0) goto tfwr;
668 }
669
670 for (j = 0; table[i][j] != -1; j++)
671 {
672 int idx2 = table[i][j];
673 VarEntry *entry = content->entries[idx2];
674 uint8_t attr = ATTRB_NONE;
675
676 if (fwrite_long(f, offset) < 0) goto tfwr;
677 ticonv_varname_to_tifile_sn(content->model, entry->name, varname, sizeof(varname), entry->type);
678 if (fwrite_8_chars(f, varname) < 0) goto tfwr;
679 if (fwrite_byte(f, entry->type) < 0) goto tfwr;
680 attr = (entry->attr == ATTRB_ARCHIVED) ? 3 : entry->attr;
681 if (fwrite_byte(f, attr) < 0) goto tfwr;
682 if (fwrite_word(f, 0) < 0) goto tfwr;
683
684 offset += entry->size + 4 + 2;
685 }
686 }
687
688 if (fwrite_long(f, offset) < 0) goto tfwr;
689 if (fwrite_word(f, 0x5aa5) < 0) goto tfwr;
690
691 // write data
692 for (i = 0; table[i] != NULL; i++)
693 {
694 int j;
695
696 for (j = 0; table[i][j] != -1; j++)
697 {
698 int idx = table[i][j];
699 VarEntry *entry = content->entries[idx];
700 uint16_t sum;
701
702 if (fwrite_long(f, 0) < 0) goto tfwr;
703 if (fwrite(entry->data, 1, entry->size, f) < entry->size) goto tfwr;
704 sum = tifiles_checksum(entry->data, entry->size);
705 if (fwrite_word(f, sum) < 0) goto tfwr;
706 }
707 }
708
709 tifiles_free_table_of_entries(table);
710 g_free(filename);
711 fclose(f);
712 return 0;
713
714 tfwr: // release on exit
715 tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
716 tifiles_free_table_of_entries(table);
717 g_free(filename);
718 fclose(f);
719 return ERR_FILE_IO;
720 }
721
722 /**
723 * ti9x_file_write_backup:
724 * @filename: name of backup file where to write.
725 * @content: the file content to write.
726 *
727 * Write content to a backup file.
728 *
729 * Return value: an error code, 0 otherwise.
730 **/
ti9x_file_write_backup(const char * filename,Ti9xBackup * content)731 int ti9x_file_write_backup(const char *filename, Ti9xBackup *content)
732 {
733 FILE *f;
734
735 if (filename == NULL || content == NULL)
736 {
737 tifiles_critical("%s: an argument is NULL", __FUNCTION__);
738 return ERR_INVALID_FILE;
739 }
740
741 f = g_fopen(filename, "wb");
742 if (f == NULL)
743 {
744 tifiles_info("Unable to open this file: %s", filename);
745 return ERR_FILE_OPEN;
746 }
747
748 if (fwrite_8_chars(f, tifiles_calctype2signature(content->model)) < 0) goto tfwb;
749 if (fwrite(fsignature, 1, 2, f) < 2) goto tfwb;
750 if (fwrite_8_chars(f, "") < 0) goto tfwb;
751 if (fwrite_n_bytes(f, 40, (uint8_t *)content->comment) < 0) goto tfwb;
752 if (fwrite_word(f, 1) < 0) goto tfwb;
753 if (fwrite_long(f, 0x52) < 0) goto tfwb;
754 if (fwrite_8_chars(f, content->rom_version) < 0) goto tfwb;
755 if (fwrite_word(f, content->type) < 0) goto tfwb;
756 if (fwrite_word(f, 0) < 0) goto tfwb;
757 if (fwrite_long(f, content->data_length + 0x52 + 2) < 0) goto tfwb;
758 if (fwrite_word(f, 0x5aa5) < 0) goto tfwb;
759 if (fwrite(content->data_part, 1, content->data_length, f) < content->data_length) goto tfwb;
760
761 content->checksum = tifiles_checksum(content->data_part, content->data_length);
762 if (fwrite_word(f, content->checksum) < 0) goto tfwb;
763
764 fclose(f);
765 return 0;
766
767 tfwb: // release on exit
768 tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
769 fclose(f);
770 return ERR_FILE_IO;
771 }
772
773 /**
774 * ti9x_file_write_flash:
775 * @filename: name of flash file where to write.
776 * @content: the file content to write.
777 *
778 * Write content to a flash file (os or app).
779 *
780 * Return value: an error code, 0 otherwise.
781 **/
ti9x_file_write_flash(const char * fname,Ti9xFlash * head,char ** real_fname)782 int ti9x_file_write_flash(const char *fname, Ti9xFlash *head, char **real_fname)
783 {
784 FILE *f;
785 Ti9xFlash *content = head;
786 char *filename;
787
788 if (head == NULL)
789 {
790 tifiles_critical("%s: head is NULL", __FUNCTION__);
791 return ERR_INVALID_FILE;
792 }
793
794 if (fname)
795 {
796 filename = g_strdup(fname);
797 if (filename == NULL)
798 {
799 return ERR_MALLOC;
800 }
801 }
802 else
803 {
804 VarEntry ve;
805
806 for (content = head; content != NULL; content = content->next)
807 {
808 if (content->data_type == TI89_AMS || content->data_type == TI89_APPL)
809 {
810 break;
811 }
812 }
813 if (content == NULL)
814 {
815 tifiles_critical("%s: content is NULL", __FUNCTION__);
816 return ERR_BAD_FILE;
817 }
818
819 strncpy(ve.name, content->name, sizeof(ve.name) - 1);
820 ve.name[sizeof(ve.name) - 1] = 0;
821 ve.type = content->data_type;
822
823 filename = tifiles_build_filename(content->model, &ve);
824 if (real_fname != NULL)
825 {
826 *real_fname = g_strdup(filename);
827 }
828 }
829
830 f = g_fopen(filename, "wb");
831 if (f == NULL)
832 {
833 tifiles_info("Unable to open this file: %s", filename);
834 g_free(filename);
835 return ERR_FILE_OPEN;
836 }
837
838 for (content = head; content != NULL; content = content->next)
839 {
840 if (fwrite_8_chars(f, "**TIFL**") < 0) goto tfwf;
841 if (fwrite_byte(f, content->revision_major) < 0) goto tfwf;
842 if (fwrite_byte(f, content->revision_minor) < 0) goto tfwf;
843 if (fwrite_byte(f, content->flags) < 0) goto tfwf;
844 if (fwrite_byte(f, content->object_type) < 0) goto tfwf;
845 if (fwrite_byte(f, content->revision_day) < 0) goto tfwf;
846 if (fwrite_byte(f, content->revision_month) < 0) goto tfwf;
847 if (fwrite_word(f, content->revision_year) < 0) goto tfwf;
848 if (fwrite_byte(f, (uint8_t) strlen(content->name)) < 0) goto tfwf;
849 if (fwrite_8_chars(f, content->name) < 0) goto tfwf;
850 if (fwrite_n_chars(f, 23, "") < 0) goto tfwf;
851 if (fwrite_byte(f, content->device_type) < 0) goto tfwf;
852 if (fwrite_byte(f, content->data_type) < 0) goto tfwf;
853 if (fwrite_n_chars(f, 23, "") < 0) goto tfwf;
854 if (fwrite_byte(f, content->hw_id) < 0) goto tfwf;
855 if (fwrite_long(f, content->data_length) < 0) goto tfwf;
856 if (fwrite(content->data_part, 1, content->data_length, f) < content->data_length) goto tfwf;
857 }
858
859 g_free(filename);
860 fclose(f);
861 return 0;
862
863 tfwf: // release on exit
864 tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
865 g_free(filename);
866 fclose(f);
867 return ERR_FILE_IO;
868 }
869
870 /**************/
871 /* Displaying */
872 /**************/
873
874 /**
875 * ti9x_content_display_backup:
876 * @content: a Ti9xBackup structure.
877 *
878 * Display fields of a Ti9xBackup structure.
879 *
880 * Return value: an error code, 0 otherwise.
881 **/
ti9x_content_display_backup(Ti9xBackup * content)882 int ti9x_content_display_backup(Ti9xBackup *content)
883 {
884 if (content == NULL)
885 {
886 tifiles_critical("%s(NULL)", __FUNCTION__);
887 return ERR_INVALID_FILE;
888 }
889
890 tifiles_info("BackupContent for TI-9x: %p", content);
891 tifiles_info("Model: %02X (%u)", content->model, content->model);
892 tifiles_info("Signature: %s", tifiles_calctype2signature(content->model));
893 tifiles_info("Comment: %s", content->comment);
894 tifiles_info("ROM version: %s", content->rom_version);
895 tifiles_info("Type: %02X (%s)", content->type, tifiles_vartype2string(content->model, content->type));
896
897 tifiles_info("data_length: %08X (%u)", content->data_length, content->data_length);
898 tifiles_info("data_part: %p", content->data_part);
899
900 tifiles_info("Checksum: %04X (%u)", content->checksum, content->checksum);
901
902 return 0;
903 }
904
905 /**
906 * ti9x_content_display_flash:
907 * @content: a Ti9xFlash structure.
908 *
909 * Display fields of a Ti9xFlash structure.
910 *
911 * Return value: an error code, 0 otherwise.
912 **/
ti9x_content_display_flash(Ti9xFlash * content)913 int ti9x_content_display_flash(Ti9xFlash *content)
914 {
915 Ti9xFlash *ptr;
916
917 for (ptr = content; ptr != NULL; ptr = ptr->next)
918 {
919 tifiles_info("FlashContent for TI-9x: %p", ptr);
920 tifiles_info("Model: %02X (%u)", ptr->model, ptr->model);
921 tifiles_info("Signature: %s", tifiles_calctype2signature(ptr->model));
922 tifiles_info("model_dst: %02X (%u)", ptr->model_dst, ptr->model_dst);
923 tifiles_info("Revision: %u.%u", ptr->revision_major, ptr->revision_minor);
924 tifiles_info("Flags: %02X", ptr->flags);
925 tifiles_info("Object type: %02X", ptr->object_type);
926 tifiles_info("Date: %02X/%02X/%02X%02X", ptr->revision_day, ptr->revision_month, ptr->revision_year & 0xff, (ptr->revision_year & 0xff00) >> 8);
927 tifiles_info("Name: %s", ptr->name);
928 tifiles_info("Device type: %s", ptr->device_type == DEVICE_TYPE_89 ? "ti89" : "ti92+");
929 switch (ptr->data_type)
930 {
931 case 0x23:
932 tifiles_info("Data type: OS data");
933 break;
934 case 0x24:
935 tifiles_info("Data type: APP data");
936 break;
937 case 0x20:
938 case 0x25:
939 tifiles_info("Data type: certificate");
940 break;
941 case 0x3E:
942 tifiles_info("Data type: license");
943 break;
944 default:
945 tifiles_info("Data type: Unknown (send mail to tilp-users@lists.sf.net)");
946 break;
947 }
948 tifiles_info("Hardware ID: %02X (%u)", ptr->hw_id, ptr->hw_id);
949 tifiles_info("Length: %08X (%u)", ptr->data_length, ptr->data_length);
950 tifiles_info("Data part: %p", ptr->data_part);
951 tifiles_info("Next: %p", ptr->next);
952 }
953
954 return 0;
955 }
956
957 /**
958 * ti9x_file_display:
959 * @filename: a TI file.
960 *
961 * Determine file class and display internal content.
962 *
963 * Return value: an error code, 0 otherwise.
964 **/
ti9x_file_display(const char * filename)965 int ti9x_file_display(const char *filename)
966 {
967 Ti9xRegular *content1;
968 Ti9xBackup *content2;
969 Ti9xFlash *content3;
970 int ret;
971
972 // the testing order is important: regular before backup (due to TI89/92+)
973 if (tifiles_file_is_flash(filename) || tifiles_file_is_tib(filename))
974 {
975 content3 = tifiles_content_create_flash(CALC_TI92);
976 ret = ti9x_file_read_flash(filename, content3);
977 if (!ret)
978 {
979 ti9x_content_display_flash(content3);
980 tifiles_content_delete_flash(content3);
981 }
982 }
983 else if (tifiles_file_is_regular(filename))
984 {
985 content1 = tifiles_content_create_regular(CALC_TI92);
986 ret = ti9x_file_read_regular(filename, content1);
987 if (!ret)
988 {
989 tifiles_file_display_regular(content1);
990 tifiles_content_delete_regular(content1);
991 }
992 }
993 else if (tifiles_file_is_backup(filename))
994 {
995 content2 = tifiles_content_create_backup(CALC_TI92);
996 ret = ti9x_file_read_backup(filename, content2);
997 if (!ret)
998 {
999 ti9x_content_display_backup(content2);
1000 tifiles_content_delete_backup(content2);
1001 }
1002 }
1003 else
1004 {
1005 tifiles_info("Unknown file type !");
1006 return ERR_BAD_FILE;
1007 }
1008
1009 return ret;
1010 }
1011
1012 #endif
1013