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