1 /* $Id$ */
2 
3 /*  libtifiles - file format library, a part of the TiLP project
4  *  Copyright (C) 1999-2005  Romain Lievin
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software Foundation,
18  *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19  */
20 
21 /*
22 	TI File Format handling routines
23 	Calcs: 73/82/83/83+/84+/85/86
24 */
25 
26 /*
27 	Thanks to Adrian Mettler <amettler@hmc.edu> for his patch which fixes
28 	some TI85/86 file issues (padded, not padded).
29 */
30 
31 #include <glib/gstdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 
35 #include <ticonv.h>
36 #include "tifiles.h"
37 #include "error.h"
38 #include "logging.h"
39 #include "typesxx.h"
40 #include "files8x.h"
41 #include "rwfile.h"
42 #include "intelhex.h"
43 
44 #ifndef DISABLE_TI8X
45 
46 /********/
47 /* Misc */
48 /********/
49 
50 static uint8_t fsignature85[3] = { 0x1A, 0x0C, 0x00 };	//TI85
51 static uint8_t fsignature8x[3] = { 0x1A, 0x0A, 0x00 };	//TI73, 82, 83, 86
52 
53 
is_ti8586(CalcModel model)54 static int is_ti8586(CalcModel model)
55 {
56 	return ((model == CALC_TI85) || (model == CALC_TI86));
57 }
58 
is_ti83p(CalcModel model)59 static int is_ti83p(CalcModel model)
60 {
61 	return ((model == CALC_TI83P) || (model == CALC_TI84P) || (model == CALC_TI84P_USB)
62 	        || (model == CALC_TI84PC) || (model == CALC_TI84PC_USB)
63 	        || (model == CALC_TI83PCE_USB) || (model == CALC_TI84PCE_USB) || (model == CALC_TI82A_USB) || (model == CALC_TI84PT_USB));
64 }
65 
compute_backup_sum(BackupContent * content,uint16_t header_size)66 static uint16_t compute_backup_sum(BackupContent* content, uint16_t header_size)
67 {
68 	uint16_t sum= 0;
69 
70 	sum += header_size;
71 	sum += tifiles_checksum((uint8_t *)&(content->data_length1), 2);
72 	sum += content->type;
73 	if (header_size >= 12)
74 	{
75 		sum += content->version;
76 	}
77 	sum += tifiles_checksum((uint8_t *)&(content->data_length2), 2);
78 	sum += tifiles_checksum((uint8_t *)&(content->data_length3), 2);
79 	if (content->model != CALC_TI86)
80 	{
81 		sum += tifiles_checksum((uint8_t *)&(content->mem_address), 2);
82 	}
83 	else
84 	{
85 		sum += tifiles_checksum((uint8_t *)&(content->data_length4), 2);
86 	}
87 
88 	sum += tifiles_checksum((uint8_t *)&(content->data_length1), 2);
89 	sum += tifiles_checksum(content->data_part1, content->data_length1);
90 	sum += tifiles_checksum((uint8_t *)&(content->data_length2), 2);
91 	sum += tifiles_checksum(content->data_part2, content->data_length2);
92 	sum += tifiles_checksum((uint8_t *)&(content->data_length3), 2);
93 	sum += tifiles_checksum(content->data_part3, content->data_length3);
94 	sum += tifiles_checksum((uint8_t *)&(content->data_length4), 2);
95 	sum += tifiles_checksum(content->data_part4, content->data_length4);
96 
97 	return sum;
98 }
99 
100 /***********/
101 /* Reading */
102 /***********/
103 
104 /**
105  * ti8x_file_read_regular:
106  * @filename: name of single/group file to open.
107  * @content: where to store the file content.
108  *
109  * Load the single/group file into a Ti8xRegular structure.
110  *
111  * Structure content must be freed with #tifiles_content_delete_regular when
112  * no longer used. If error occurs, the structure content is released for you.
113  *
114  * Return value: an error code, 0 otherwise.
115  **/
ti8x_file_read_regular(const char * filename,Ti8xRegular * content)116 int ti8x_file_read_regular(const char *filename, Ti8xRegular *content)
117 {
118 	FILE *f;
119 	uint16_t tmp = 0x000B;
120 	long offset = 0;
121 	unsigned int i, j;
122 	int ti83p_flag = 0;
123 	uint8_t name_length = 8;	// ti85/86 only
124 	uint16_t data_size, sum = 0;
125 	long cur_pos;
126 	uint32_t file_size;
127 	char signature[9];
128 	int padded86 = 0;
129 	char varname[VARNAME_MAX];
130 	int ret = ERR_FILE_IO;
131 
132 	if (content == NULL)
133 	{
134 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
135 		return ERR_INVALID_FILE;
136 	}
137 
138 	if (!tifiles_file_is_regular(filename))
139 	{
140 		ret = ERR_INVALID_FILE;
141 		goto tfrr2;
142 	}
143 
144 	f = g_fopen(filename, "rb");
145 	if (f == NULL)
146 	{
147 		tifiles_warning("Unable to open this file: %s", filename);
148 		ret = ERR_FILE_OPEN;
149 		goto tfrr2;
150 	}
151 
152 	// Get file size, then rewind.
153 	if (fseek(f, 0, SEEK_END) < 0) goto tfrr;
154 	cur_pos = ftell(f);
155 	if (cur_pos < 0) goto tfrr;
156 	if (fseek(f, 0, SEEK_SET) < 0) goto tfrr;
157 
158 	// The TI-Z80 and TI-eZ80 series' members have at best 4 MB of Flash (TODO: modify this code if this no longer holds).
159 	// Regular / group files larger than that size are highly dubious, files larger than twice that size are insane.
160 	if (cur_pos >= (8L << 20))
161 	{
162 		ret = ERR_INVALID_FILE;
163 		goto tfrr;
164 	}
165 	file_size = (uint32_t)cur_pos;
166 
167 	if (fread_8_chars(f, signature) < 0) goto tfrr; // Offset 0
168 	content->model = tifiles_signature2calctype(signature);
169 	if (content->model == CALC_NONE)
170 	{
171 		ret = ERR_INVALID_FILE;
172 		goto tfrr;
173 	}
174 	if (content->model_dst == CALC_NONE)
175 	{
176 		content->model_dst = content->model;
177 	}
178 	if (fskip(f, 3) < 0) goto tfrr; // Offset 0x8
179 	if (fread_n_chars(f, 42, content->comment) < 0) goto tfrr; // Offset 0xB
180 	if (fread_word(f, &data_size) < 0) goto tfrr; // Offset 0x35
181 	if ((uint32_t)data_size > file_size)
182 	{
183 		ret = ERR_INVALID_FILE;
184 		goto tfrr;
185 	}
186 
187 	// search for the number of entries by parsing the whole file
188 	offset = ftell(f); // Offset 0x37
189 	if (offset == -1L) goto tfrr;
190 
191 	for (i = 0;; i++)
192 	{
193 		long current_offset = ftell(f);
194 		/* We are done finding entries once we reach the end of the data segment
195 		 * as defined in the header.  This works better than magic numbers, as
196 		 * as there exist files in the wild with incorrect magic numbers that
197 		 * transmit correctly with TI's software and with this code.
198 		 *   Adrian Mettler
199 		 */
200 		if (current_offset == -1L) goto tfrr;
201 		if (current_offset >= offset + data_size)
202 		{
203 			break;
204 		}
205 
206 		if (fread_word(f, &tmp) < 0) goto tfrr; // Offset N, 0x37 for the first entry
207 		if (tmp == 0x0D)
208 		{
209 			ti83p_flag = !0;		// true TI83+ file (2 extra bytes)
210 		}
211 
212 		if (content->model == CALC_TI85)
213 		{
214 			// length &  name with no padding
215 			if (fskip(f, 3) < 0) goto tfrr; // Offset N+2, 0x39 for the first entry
216 			if (fread_byte(f, &name_length) < 0) goto tfrr; // Offset N+5, 0x3C for the first entry
217 			if (name_length > 8)
218 			{
219 				ret = ERR_INVALID_FILE;
220 				goto tfrr;
221 			}
222 			if (fskip(f, name_length) < 0) goto tfrr; // Offset N+6+name_length, 0x3D for the first entry
223 		}
224 		else if (content->model == CALC_TI86)
225 		{
226 			/* name may follow one of four conventions: padded with SPC bytes
227 			 * (most correct, generated by TI's software), padded with NULL bytes,
228 			 * unpadded (like TI85) or partially padded (garbaged).
229 			 * TI's software accepts all four, so we should too.
230 			 */
231 			padded86 = tmp < 0x0C ? 0 : !0;	// TI-85 style file
232 
233 			if (fskip(f, 3) < 0) goto tfrr; // Offset N+2, 0x39 for the first entry
234 			if (fread_byte(f, &name_length) < 0) goto tfrr; // Offset N+5, 0x3C for the first entry
235 			if (name_length > 8)
236 			{
237 				ret = ERR_INVALID_FILE;
238 				goto tfrr;
239 			}
240 			if (fskip(f, name_length) < 0) goto tfrr; // Offset N+6+name_length, 0x3D for the first entry
241 
242 			if (padded86)
243 			{
244 				if (fskip(f, 8 - name_length) < 0) goto tfrr;
245 			}
246 		}
247 		else if (ti83p_flag)
248 		{
249 			if (fskip(f, 13) < 0) goto tfrr; // Offset N+2, 0x39 for the first entry.
250 		}
251 		else
252 		{
253 			if (fskip(f, 11) < 0) goto tfrr; // Offset N+2, 0x39 for the first entry.
254 		}
255 		if (fread_word(f, &tmp) < 0) goto tfrr; // Offset depends on model.
256 		if (fskip(f, tmp) < 0) goto tfrr;
257 	}
258 
259 	if (fseek(f, offset, SEEK_SET) < 0) goto tfrr; // Offset 0x37
260 
261 	content->num_entries = i;
262 	content->entries = g_malloc0((content->num_entries + 1) * sizeof(VarEntry*));
263 	if (content->entries == NULL)
264 	{
265 		ret = ERR_MALLOC;
266 		goto tfrr;
267 	}
268 
269 	for (i = 0; i < content->num_entries; i++)
270 	{
271 		VarEntry *entry = content->entries[i] = g_malloc0(sizeof(VarEntry));
272 		uint16_t packet_length, entry_size;
273 
274 		if (fread_word(f, &packet_length) < 0) goto tfrr; // Offset 0x37
275 		if ((uint32_t)packet_length > file_size)
276 		{
277 			ret = ERR_INVALID_FILE;
278 			goto tfrr;
279 		}
280 		if (fread_word(f, &entry_size) < 0) goto tfrr; // Offset 0x39
281 		if ((uint32_t)entry_size > file_size)
282 		{
283 			ret = ERR_INVALID_FILE;
284 			goto tfrr;
285 		}
286 		entry->size = entry_size;
287 		if (fread_byte(f, &(entry->type)) < 0) goto tfrr; // Offset 0x3B
288 		if (is_ti8586(content->model))
289 		{
290 			if (fread_byte(f, &name_length) < 0) goto tfrr; // Offset 0x3C
291 		}
292 		if (fread_n_chars(f, name_length, varname) < 0) goto tfrr; // Offset up to 0x44
293 		ticonv_varname_from_tifile_sn(content->model_dst, varname, entry->name, sizeof(entry->name), entry->type);
294 		if ((content->model == CALC_TI86) && padded86)
295 		{
296 			for (j = 0; j < 8U - name_length; j++)
297 			{
298 				sum += fgetc(f);
299 			}
300 		}
301 		if (ti83p_flag)
302 		{
303 			uint16_t attribute;
304 			if (fread_word(f, &attribute) < 0) goto tfrr; // Offset 0x44
305 			// Handle both the files created by TI-Connect and the files created by
306 			// some broken versions of libtifiles.
307 			if (attribute == 0x80)
308 			{
309 				entry->attr = ATTRB_ARCHIVED;
310 				entry->version = 0;
311 			}
312 			else
313 			{
314 				entry->attr = ((attribute & 0x8000) ? ATTRB_ARCHIVED : ATTRB_NONE);
315 				entry->version = attribute & 0xff;
316 			}
317 
318 			// Handle broken 84+CSE Pic files created by older versions of libtifiles.
319 			if (entry->type == TI84p_PIC && entry->size == 0x55bb && entry->version == 0)
320 			{
321 				entry->version = 10;
322 			}
323 
324 			sum += MSB(attribute);
325 			sum += LSB(attribute);
326 		}
327 		if (fread_word(f, NULL) < 0) goto tfrr;
328 
329 		entry->data = (uint8_t *) g_malloc0(entry->size);
330 		if (entry->data == NULL)
331 		{
332 			ret = ERR_MALLOC;
333 			goto tfrr;
334 		}
335 
336 		if (fread(entry->data, 1, entry->size, f) < entry->size) goto tfrr;
337 
338 		sum += packet_length;
339 		sum += tifiles_checksum((uint8_t *)&(entry->size), 2);
340 		sum += entry->type;
341 		if (is_ti8586(content->model))
342 		{
343 			sum += strlen(entry->name);
344 		}
345 		sum += tifiles_checksum((uint8_t *)varname, name_length);
346 		sum += 0; // see above (file may be padded with garbage)
347 		sum += tifiles_checksum((uint8_t *)&(entry->size), 2);
348 		sum += tifiles_checksum(entry->data, entry->size);
349 	}
350 
351 	if (fread_word(f, &(content->checksum)) < 0) goto tfrr;
352 #if defined(CHECKSUM_ENABLED)
353 	if (sum != content->checksum)
354 	{
355 		ret = ERR_FILE_CHECKSUM;
356 		goto tfrr;
357 	}
358 #endif
359 
360 	fclose(f);
361 	return 0;
362 
363 tfrr:	// release on exit
364 	tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
365 	fclose(f);
366 tfrr2:
367 	tifiles_content_delete_regular(content);
368 	return ret;
369 }
370 
371 /**
372  * ti8x_file_read_backup:
373  * @filename: name of backup file to open.
374  * @content: where to store the file content.
375  *
376  * Load the backup file into a Ti8xBackup structure.
377  *
378  * Structure content must be freed with #tifiles_content_delete_backup when
379  * no longer used. If error occurs, the structure content is released for you.
380  *
381  * Return value: an error code, 0 otherwise.
382  **/
ti8x_file_read_backup(const char * filename,Ti8xBackup * content)383 int ti8x_file_read_backup(const char *filename, Ti8xBackup *content)
384 {
385 	FILE *f;
386 	long cur_pos = 0;
387 	char signature[9];
388 	uint16_t sum;
389 	uint16_t file_size, header_size;
390 	uint8_t extra_header[3] = { 0, 0, 0 };
391 	int ret = ERR_FILE_IO;
392 
393 	if (content == NULL)
394 	{
395 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
396 		return ERR_INVALID_FILE;
397 	}
398 
399 	if (!tifiles_file_is_backup(filename))
400 	{
401 		ret = ERR_INVALID_FILE;
402 		goto tfrb2;
403 	}
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 tfrb2;
411 	}
412 
413 	// Get file size, then rewind.
414 	if (fseek(f, 0, SEEK_END) < 0) goto tfrb;
415 	cur_pos = ftell(f);
416 	if (cur_pos < 0) goto tfrb;
417 	if (fseek(f, 0, SEEK_SET) < 0) goto tfrb;
418 
419 	// The TI-Z80 backup format cannot handle more than 65535 bytes.
420 	if (cur_pos >= 65536L)
421 	{
422 		ret = ERR_INVALID_FILE;
423 		goto tfrb;
424 	}
425 	file_size = (uint16_t)cur_pos;
426 
427 	if (fread_8_chars(f, signature) < 0) goto tfrb;
428 	content->model = tifiles_signature2calctype(signature);
429 	if (content->model == CALC_NONE)
430 	{
431 		ret = ERR_INVALID_FILE;
432 		goto tfrb;
433 	}
434 	if (fskip(f, 3) < 0) goto tfrb;
435 	if (fread_n_chars(f, 42, content->comment) < 0) goto tfrb;
436 	if (fread_word(f, NULL) < 0) goto tfrb;
437 
438 	if (fread_word(f, &header_size) < 0) goto tfrb;
439 	if (header_size < 9 || header_size > 12)
440 	{
441 		ret = ERR_INVALID_FILE;
442 		goto tfrb;
443 	}
444 
445 	if (fread_word(f, &(content->data_length1)) < 0) goto tfrb;
446 	if (content->data_length1 > file_size)
447 	{
448 		ret = ERR_INVALID_FILE;
449 		goto tfrb;
450 	}
451 	if (fread_byte(f, &(content->type)) < 0) goto tfrb;
452 	if (fread_word(f, &(content->data_length2)) < 0) goto tfrb;
453 	if (content->data_length2 > file_size)
454 	{
455 		ret = ERR_INVALID_FILE;
456 		goto tfrb;
457 	}
458 	if (fread_word(f, &(content->data_length3)) < 0) goto tfrb;
459 	if (content->data_length3 > file_size)
460 	{
461 		ret = ERR_INVALID_FILE;
462 		goto tfrb;
463 	}
464 	content->data_length4 = 0;
465 	if (content->model != CALC_TI86)
466 	{
467 		if (fread_word(f, &(content->mem_address)) < 0) goto tfrb;
468 	}
469 	else
470 	{
471 		if (fread_word(f, &(content->data_length4)) < 0) goto tfrb;
472 	}
473 	if (content->data_length4 > file_size)
474 	{
475 		ret = ERR_INVALID_FILE;
476 		goto tfrb;
477 	}
478 	if (header_size > 9)
479 	{
480 		if (fread(extra_header, 1, header_size - 9, f) < header_size - 9) goto tfrb;
481 	}
482 	content->version = extra_header[2];
483 
484 	if (fread_word(f, NULL) < 0) goto tfrb;
485 	content->data_part1 = (uint8_t *)g_malloc0(content->data_length1);
486 	if (content->data_part1 == NULL)
487 	{
488 		ret = ERR_MALLOC;
489 		goto tfrb;
490 	}
491 	if (fread(content->data_part1, 1, content->data_length1, f) < content->data_length1) goto tfrb;
492 
493 	if (fread_word(f, NULL) < 0) goto tfrb;
494 	content->data_part2 = (uint8_t *)g_malloc0(content->data_length2);
495 	if (content->data_part2 == NULL)
496 	{
497 		ret = ERR_MALLOC;
498 		goto tfrb;
499 	}
500 	if (fread(content->data_part2, 1, content->data_length2, f) < content->data_length2) goto tfrb;
501 
502 	if (content->data_length3)	// can be 0000 on TI86
503 	{
504 		if (fread_word(f, NULL) < 0) goto tfrb;
505 		content->data_part3 = (uint8_t *)g_malloc0(content->data_length3);
506 		if (content->data_part3 == NULL)
507 		{
508 			ret = ERR_MALLOC;
509 			goto tfrb;
510 		}
511 		if (fread(content->data_part3, 1, content->data_length3, f) < content->data_length3) goto tfrb;
512 	}
513 
514 	if (content->model == CALC_TI86)
515 	{
516 		if (fread_word(f, NULL) < 0) goto tfrb;
517 		content->data_part4 = (uint8_t *)g_malloc0(content->data_length4);
518 		if (content->data_part4 == NULL)
519 		{
520 			ret = ERR_MALLOC;
521 			goto tfrb;
522 		}
523 		if (fread(content->data_part4, 1, content->data_length4, f) < content->data_length4) goto tfrb;
524 	}
525 	else
526 	{
527 		content->data_length4 = 0;
528 		content->data_part4 = NULL;
529 	}
530 
531 	if (fread_word(f, &(content->checksum)) < 0) goto tfrb;
532 	sum = compute_backup_sum(content, header_size);
533 #if defined(CHECKSUM_ENABLED)
534 	if (sum != content->checksum)
535 	{
536 		ret = ERR_FILE_CHECKSUM;
537 		goto tfrb;
538 	}
539 #endif
540 
541 	fclose(f);
542 	return 0;
543 
544 tfrb:	// release on exit
545 	tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
546 	fclose(f);
547 tfrb2:
548 	tifiles_content_delete_backup(content);
549 	return ret;
550 }
551 
check_device_type(uint8_t id)552 static int check_device_type(uint8_t id)
553 {
554 	static const uint8_t types[] = { 0, DEVICE_TYPE_73, DEVICE_TYPE_83P };
555 	int i;
556 
557 	for (i = 1; i < (int)(sizeof(types)/sizeof(types[0])); i++)
558 	{
559 		if (types[i] == id)
560 		{
561 			return i;
562 		}
563 	}
564 
565 	return 0;
566 }
567 
check_data_type(uint8_t id)568 static int check_data_type(uint8_t id)
569 {
570 	static const uint8_t types[] = { 0, TI83p_AMS, TI83p_APPL, TI83p_CERT, TI83p_LICENSE };
571 	int i;
572 
573 	for (i = 1; i < (int)(sizeof(types)/sizeof(types[0])); i++)
574 	{
575 		if (types[i] == id)
576 		{
577 			return i;
578 		}
579 	}
580 
581 	return 0;
582 }
583 
get_native_app_name(const FlashContent * content,char * buffer,size_t buffer_size)584 static int get_native_app_name(const FlashContent *content, char *buffer, size_t buffer_size)
585 {
586 	const uint8_t *data, *name;
587 	uint16_t app_type;
588 	uint32_t size, n;
589 
590 	if (content->num_pages > 0)
591 	{
592 		data = content->pages[0]->data;
593 		size = content->pages[0]->size;
594 	}
595 	else
596 	{
597 		data = content->data_part;
598 		size = content->data_length;
599 	}
600 
601 	if (size >= 6 && (data[0] & 0xf0) == 0x80 && data[1] == 0x0f)
602 	{
603 		app_type = (uint16_t)(data[0]) << 8;
604 		if (!tifiles_cert_field_find(data + 6, size - 6, app_type + 0x40, &name, &n))
605 		{
606 			if (n >= buffer_size)
607 			{
608 				n = buffer_size - 1;
609 			}
610 			memcpy(buffer, name, n);
611 			buffer[n] = 0;
612 			return 1;
613 		}
614 	}
615 
616 	return 0;
617 }
618 
619 /**
620  * ti8x_file_read_flash:
621  * @filename: name of flash file to open.
622  * @content: where to store the file content.
623  *
624  * Load the flash file into a #FlashContent structure.
625  *
626  * Structure content must be freed with #tifiles_content_delete_flash when
627  * no longer used. If error occurs, the structure content is released for you.
628  *
629  * Return value: an error code, 0 otherwise.
630  **/
ti8x_file_read_flash(const char * filename,Ti8xFlash * head)631 int ti8x_file_read_flash(const char *filename, Ti8xFlash *head)
632 {
633 	FILE *f;
634 	Ti8xFlash *content = head;
635 	int i;
636 	char signature[9];
637 	char varname[9];
638 	int ret = ERR_FILE_IO;
639 
640 	if (head == NULL)
641 	{
642 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
643 		return ERR_INVALID_FILE;
644 	}
645 
646 	if (!tifiles_file_is_flash(filename))
647 	{
648 		ret = ERR_INVALID_FILE;
649 		goto tfrf2;
650 	}
651 
652 	f = g_fopen(filename, "rb");
653 	if (f == NULL)
654 	{
655 		tifiles_info("Unable to open this file: %s", filename);
656 		ret = ERR_FILE_OPEN;
657 		goto tfrf2;
658 	}
659 
660 	for (content = head;; content = content->next)
661 	{
662 		if (fread_8_chars(f, signature) < 0) goto tfrf;
663 		content->model = tifiles_file_get_model(filename);
664 		if (fread_byte(f, &(content->revision_major)) < 0) goto tfrf;
665 		if (fread_byte(f, &(content->revision_minor)) < 0) goto tfrf;
666 		if (fread_byte(f, &(content->flags)) < 0) goto tfrf;
667 		if (fread_byte(f, &(content->object_type)) < 0) goto tfrf;
668 		if (fread_byte(f, &(content->revision_day)) < 0) goto tfrf;
669 		if (fread_byte(f, &(content->revision_month)) < 0) goto tfrf;
670 		if (fread_word(f, &(content->revision_year)) < 0) goto tfrf;
671 		if (fskip(f, 1) < 0) goto tfrf;
672 		if (fread_8_chars(f, varname) < 0) goto tfrf;
673 		if (fskip(f, 23) < 0) goto tfrf;
674 		if (fread_byte(f, &(content->device_type)) < 0) goto tfrf;
675 		if (fread_byte(f, &(content->data_type)) < 0) goto tfrf;
676 		if (fskip(f, 23) < 0) goto tfrf;
677 		if (fread_byte(f, &(content->hw_id)) < 0) goto tfrf;
678 		if (fread_long(f, &content->data_length) < 0) goto tfrf;
679 
680 		if (!check_device_type(content->device_type))
681 		{
682 			ret = ERR_INVALID_FILE;
683 			goto tfrf;
684 		}
685 		if (!check_data_type(content->data_type))
686 		{
687 			ret = ERR_INVALID_FILE;
688 			goto tfrf;
689 		}
690 		// TODO: modify this code if TI ever makes a TI-eZ80 model with more than 4 MB of Flash memory...
691 		if (content->data_length > 4U * 1024 * 1024 - 16384)
692 		{
693 			// Data length larger than Flash memory size - boot code sector size doesn't look right.
694 			ret = ERR_INVALID_FILE;
695 			goto tfrf;
696 		}
697 
698 		if (content->data_type == TI83p_CERT || content->data_type == TI83p_LICENSE)
699 		{
700 			// get data like TI9X
701 			content->data_part = (uint8_t *)g_malloc0(content->data_length + 256);
702 			if (content->data_part == NULL)
703 			{
704 				ret = ERR_MALLOC;
705 				goto tfrf;
706 			}
707 
708 			memset(content->data_part, 0xff, content->data_length + 256);
709 			if (fread(content->data_part, 1, content->data_length, f) < content->data_length) goto tfrf;
710 
711 			content->next = NULL;
712 		}
713 		else if (content->data_type == TI83p_AMS || content->data_type == TI83p_APPL)
714 		{
715 			if (content->hw_id == 0)
716 			{
717 				// This looks like a TI-Z80-style OS / FlashApp, let's try to parse Intel Hex format.
718 
719 				// reset/initialize block reader
720 				hex_block_read(NULL, NULL, NULL, NULL, NULL, NULL);
721 				content->pages = NULL;
722 
723 				// TODO: modify this code if TI ever makes a TI-Z80 model with more than 256 Flash pages...
724 				content->pages = g_malloc0((256+1) * sizeof(Ti8xFlashPage *));
725 				if (content->pages == NULL)
726 				{
727 					ret = ERR_MALLOC;
728 					goto tfrf;
729 				}
730 
731 				// read FLASH pages
732 				content->data_length = 0;
733 				for (i = 0, ret = 0; !ret; i++)
734 				{
735 					uint16_t size;
736 					uint16_t addr;
737 					uint16_t page;
738 					uint8_t flag = 0x80;
739 					uint8_t data[FLASH_PAGE_SIZE];
740 					FlashPage* fp = content->pages[i] = g_malloc0(sizeof(FlashPage));
741 
742 					ret = hex_block_read(f, &size, &addr, &flag, data, &page);
743 
744 					fp->data = (uint8_t *) g_malloc0(FLASH_PAGE_SIZE);
745 					memset(fp->data, 0xff, FLASH_PAGE_SIZE);
746 					if (fp->data == NULL)
747 					{
748 						ret = ERR_MALLOC;
749 						goto tfrf;
750 					}
751 
752 					fp->addr = addr;
753 					fp->page = page;
754 					fp->flag = flag;
755 					fp->size = size;
756 					memcpy(fp->data, data, size);
757 
758 					content->data_length += size;
759 				}
760 				content->num_pages = i;
761 				content->next = NULL;
762 			}
763 			else
764 			{
765 				// This looks like a TI-68k-style OS / FlashApp, containing flat data.
766 
767 				content->data_part = (uint8_t *)g_malloc0(content->data_length);
768 				if (content->data_part == NULL)
769 				{
770 					ret = ERR_MALLOC;
771 					goto tfrf;
772 				}
773 
774 				if (fread(content->data_part, 1, content->data_length, f) < content->data_length) goto tfrf;
775 				if (   (content->data_type == TI83p_AMS && content->data_part[0] != 0x80)
776 				    || (content->data_type == TI83p_APPL && content->data_part[0] != 0x81))
777 				{
778 					ret = ERR_INVALID_FILE;
779 					goto tfrf;
780 				}
781 				content->next = NULL;
782 			}
783 		}
784 
785 		if (content->data_type == TI83p_APPL)
786 		{
787 			// Determine the app name from the internal header if possible.
788 			if (!get_native_app_name(content, varname, sizeof(varname)))
789 			{
790 				tifiles_warning("unable to determine app name from header");
791 			}
792 		}
793 
794 		if (content->model_dst == CALC_NONE)
795 		{
796 			content->model_dst = content->model;
797 		}
798 		ticonv_varname_from_tifile_sn(content->model_dst, varname, content->name, sizeof(content->name), content->data_type);
799 
800 		// check for end of file
801 		if (fread_8_chars(f, signature) < 0)
802 		{
803 			break;
804 		}
805 		if (strcmp(signature, "**TIFL**") || feof(f))
806 		{
807 			break;
808 		}
809 		if (fseek(f, -8, SEEK_CUR)) goto tfrf;
810 
811 		content->next = (Ti8xFlash *)g_malloc0(sizeof(Ti8xFlash));
812 		if (content->next == NULL)
813 		{
814 			ret = ERR_MALLOC;
815 			goto tfrf;
816 		}
817 	}
818 
819 	fclose(f);
820 	return 0;
821 
822 tfrf:	// release on exit
823 	tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
824 	fclose(f);
825 tfrf2:
826 	tifiles_content_delete_flash(content);
827 	return ret;
828 }
829 
830 /***********/
831 /* Writing */
832 /***********/
833 
834 /**
835  * ti8x_file_write_regular:
836  * @filename: name of single/group file where to write or NULL.
837  * @content: the file content to write.
838  * @real_filename: pointer address or NULL. Must be freed if needed when no longer needed.
839  *
840  * Write one (or several) variable(s) into a single (group) file. If filename is set to NULL,
841  * the function build a filename from varname and allocates resulting filename in %real_fname.
842  * %filename and %real_filename can be NULL but not both !
843  *
844  * %real_filename must be freed when no longer used.
845  *
846  * Return value: an error code, 0 otherwise.
847  **/
ti8x_file_write_regular(const char * fname,Ti8xRegular * content,char ** real_fname)848 int ti8x_file_write_regular(const char *fname, Ti8xRegular *content, char **real_fname)
849 {
850 	FILE *f;
851 	unsigned int i;
852 	uint16_t sum = 0;
853 	char *filename = NULL;
854 	uint32_t data_length;
855 	uint16_t packet_length = 0x0B;
856 	uint8_t name_length = 8;
857 	uint16_t attr;
858 
859 	if (content->entries == NULL)
860 	{
861 		tifiles_warning("%s: skipping content with NULL content->entries", __FUNCTION__);
862 		return 0;
863 	}
864 
865 	// Compute data length now, so as to be able to bail out immediately if we know the data won't fit.
866 	for (i = 0, data_length = 0; i < content->num_entries; i++)
867 	{
868 		VarEntry *entry = content->entries[i];
869 		if (entry == NULL)
870 		{
871 			tifiles_warning("%s: skipping null content entry %d", __FUNCTION__, i);
872 			continue;
873 		}
874 
875 		if (content->model == CALC_TI82 || content->model == CALC_TI73)
876 		{
877 			data_length += entry->size + 15;
878 		}
879 		else if (content->model == CALC_TI83)
880 		{
881 			data_length += entry->size + 15;
882 		}
883 		else if (content->model == CALC_TI85)
884 		{
885 			data_length += entry->size + 8 + strlen(entry->name);
886 		}
887 		else if (content->model == CALC_TI86)
888 		{
889 			data_length += entry->size + 16;
890 		}
891 		else if (is_ti83p(content->model))
892 		{
893 			data_length += entry->size + 17;
894 		}
895 	}
896 	if (data_length > 65535)
897 	{
898 		return ERR_GROUP_SIZE;
899 	}
900 
901 	if (fname != NULL)
902 	{
903 		filename = g_strdup(fname);
904 		if (filename == NULL)
905 		{
906 			return ERR_MALLOC;
907 		}
908 	}
909 	else
910 	{
911 		if (content->entries[0])
912 		{
913 			filename = tifiles_build_filename(content->model_dst, content->entries[0]);
914 		}
915 		else
916 		{
917 			tifiles_warning("%s: asked to build a filename from null content->entries[0], bailing out", __FUNCTION__);
918 			if (real_fname != NULL)
919 			{
920 				*real_fname = NULL;
921 			}
922 			return 0;
923 		}
924 		if (real_fname != NULL)
925 		{
926 			*real_fname = g_strdup(filename);
927 		}
928 	}
929 
930 	f = g_fopen(filename, "wb");
931 	if (f == NULL)
932 	{
933 		tifiles_info( "Unable to open this file: %s", filename);
934 		g_free(filename);
935 		return ERR_FILE_OPEN;
936 	}
937 
938 	// write header
939 	if (fwrite_8_chars(f, tifiles_calctype2signature(content->model)) < 0) goto tfwr;
940 	if (fwrite(content->model == CALC_TI85 ? fsignature85 : fsignature8x, 1, 3, f) < 3) goto tfwr;
941 	if (fwrite_n_bytes(f, 42, (uint8_t *)content->comment) < 0) goto tfwr;
942 
943 	if (fwrite_word(f, (uint16_t) data_length) < 0) goto tfwr;
944 
945 	// write data section
946 	for (i = 0, sum = 0; i < content->num_entries; i++)
947 	{
948 		VarEntry *entry = content->entries[i];
949 		char varname[VARNAME_MAX];
950 
951 		if (content->model == CALC_TI85)
952 		{
953 			packet_length = 4 + strlen(entry->name);	//offset to data length
954 		}
955 		else if (content->model == CALC_TI86)
956 		{
957 			packet_length = 0x0C;
958 		}
959 		else if (is_ti83p(content->model))
960 		{
961 			packet_length = 0x0D;
962 		}
963 		else
964 		{
965 			packet_length = 0x0B;
966  		}
967 
968 		if (fwrite_word(f, packet_length) < 0) goto tfwr;
969 		if (fwrite_word(f, (uint16_t)entry->size) < 0) goto tfwr;
970 		if (fwrite_byte(f, entry->type) < 0) goto tfwr;
971 		memset(varname, 0, sizeof(varname));
972 		ticonv_varname_to_tifile_sn(content->model_dst, entry->name, varname, sizeof(varname), entry->type);
973 		if (is_ti8586(content->model))
974 		{
975 			name_length = strlen(varname);
976 			if (fwrite_byte(f, (uint8_t)name_length) < 0) goto tfwr;
977 			if (content->model == CALC_TI85)
978 			{
979 				if (fwrite_n_chars(f, name_length, varname) < 0) goto tfwr;
980 			}
981 			else
982 			{
983 				if (fwrite_n_chars2(f, 8, varname) < 0) goto tfwr; // space padded
984 			}
985 		}
986 		else
987 		{
988 			if (fwrite_n_chars(f, 8, varname) < 0) goto tfwr;
989 		}
990 		if (is_ti83p(content->model))
991 		{
992 			attr = (uint16_t)((entry->attr == ATTRB_ARCHIVED) ? 0x8000 : 0x00) + entry->version;
993 			if (fwrite_word(f, attr) < 0) goto tfwr;
994 			sum += MSB(attr);
995 			sum += LSB(attr);
996 		}
997 		if (fwrite_word(f, (uint16_t)entry->size) < 0) goto tfwr;
998 		if (fwrite(entry->data, 1, entry->size, f) < entry->size) goto tfwr;
999 
1000 		sum += packet_length;
1001 		sum += MSB(entry->size);
1002 		sum += LSB(entry->size);
1003 		sum += entry->type;
1004 		if (is_ti8586(content->model))
1005 		{
1006 			sum += strlen(entry->name);
1007 		}
1008 		sum += tifiles_checksum((uint8_t *)varname, name_length);
1009 		if (content->model == CALC_TI86)
1010 		{
1011 			sum += (8 - name_length) * ' ';
1012 		}
1013 		sum += MSB(entry->size);
1014 		sum += LSB(entry->size);
1015 		sum += tifiles_checksum(entry->data, entry->size);
1016 	}
1017 
1018 	//checksum is the sum of all bytes in the data section
1019 	content->checksum = sum;
1020 	if (fwrite_word(f, content->checksum) < 0) goto tfwr;
1021 
1022 	g_free(filename);
1023 	fclose(f);
1024 	return 0;
1025 
1026 tfwr:	// release on exit
1027 	tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
1028 	g_free(filename);
1029 	fclose(f);
1030 	return ERR_FILE_IO;
1031 }
1032 
1033 /**
1034  * ti8x_file_write_backup:
1035  * @filename: name of backup file where to write.
1036  * @content: the file content to write.
1037  *
1038  * Write content to a backup file.
1039  *
1040  * Return value: an error code, 0 otherwise.
1041  **/
ti8x_file_write_backup(const char * filename,Ti8xBackup * content)1042 int ti8x_file_write_backup(const char *filename, Ti8xBackup *content)
1043 {
1044 	FILE *f;
1045 	uint16_t header_size;
1046 	uint32_t data_length;
1047 
1048 	if (filename == NULL || content == NULL)
1049 	{
1050 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
1051 		return ERR_INVALID_FILE;
1052 	}
1053 
1054 	data_length = content->data_length1 + content->data_length2 + content->data_length3 + 17;
1055 	if (content->model == CALC_TI86)
1056 	{
1057 		data_length += content->data_length4;
1058 	}
1059 
1060 	if (data_length > 65535)
1061 	{
1062 		return ERR_GROUP_SIZE;
1063 	}
1064 
1065 	f = g_fopen(filename, "wb");
1066 	if (f == NULL)
1067 	{
1068 		tifiles_info( "Unable to open this file: %s", filename);
1069 		return ERR_FILE_OPEN;
1070 	}
1071 	// write header
1072 	if (fwrite_8_chars(f, tifiles_calctype2signature(content->model)) < 0) goto tfwb;
1073 	if (fwrite(content->model == CALC_TI85 ? fsignature85 : fsignature8x, 1, 3, f) < 3) goto tfwb;
1074 	if (fwrite_n_bytes(f, 42, (uint8_t *)content->comment) < 0) goto tfwb;
1075 	if (fwrite_word(f, data_length) < 0) goto tfwb;
1076 
1077 	// Use the old-style header for versions 0 to 5 (84+ OS 2.48 and
1078 	// earlier), for compatibility with older versions of libtifiles
1079 	// that assume the header is always 9 bytes.  Backups from newer
1080 	// OSes (2.53 and later) are incompatible in both directions, so a
1081 	// new format is needed for version 6 and above.
1082 	if ((content->version & ~0x20) <= 0x05)
1083 	{
1084 		header_size = 9;
1085 	}
1086 	else
1087 	{
1088 		header_size = 12;
1089 	}
1090 
1091 	// write backup header
1092 	if (fwrite_word(f, header_size) < 0) goto tfwb;
1093 	if (fwrite_word(f, content->data_length1) < 0) goto tfwb;
1094 	if (fwrite_byte(f, content->type) < 0) goto tfwb;
1095 	if (fwrite_word(f, content->data_length2) < 0) goto tfwb;
1096 	if (fwrite_word(f, content->data_length3) < 0) goto tfwb;
1097 	if (content->model != CALC_TI86)
1098 	{
1099 		if (fwrite_word(f, content->mem_address) < 0) goto tfwb;
1100 	}
1101 	else
1102 	{
1103 		if (fwrite_word(f, content->data_length4) < 0) goto tfwb;
1104 	}
1105 	if (header_size == 12)
1106 	{
1107 		if (fwrite_word(f, 0) < 0) goto tfwb;
1108 		if (fwrite_byte(f, content->version) < 0) goto tfwb;
1109 	}
1110 
1111 	// write data num_entries
1112 	if (fwrite_word(f, content->data_length1) < 0) goto tfwb;
1113 	if (fwrite(content->data_part1, 1, content->data_length1, f) < content->data_length1) goto tfwb;
1114 	if (fwrite_word(f, content->data_length2) < 0) goto tfwb;
1115 	if (fwrite(content->data_part2, 1, content->data_length2, f) < content->data_length2) goto tfwb;
1116 	if (content->data_length3)	// TI86: can be NULL
1117 	{
1118 		if (fwrite_word(f, content->data_length3) < 0) goto tfwb;
1119 	}
1120 	if (fwrite(content->data_part3, 1, content->data_length3, f) < content->data_length3) goto tfwb;
1121 	if (content->model == CALC_TI86)
1122 	{
1123 		if (fwrite_word(f, content->data_length4) < 0) goto tfwb;
1124 		if (fwrite(content->data_part4, 1, content->data_length4, f) < content->data_length4) goto tfwb;
1125 	}
1126 
1127 	// checksum = sum of all bytes in bachup headers and data num_entries
1128 	content->checksum = compute_backup_sum(content, header_size);
1129 	if (fwrite_word(f, content->checksum) < 0) goto tfwb;
1130 
1131 	fclose(f);
1132 	return 0;
1133 
1134 tfwb:	// release on exit
1135 	tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
1136 	fclose(f);
1137 	return ERR_FILE_IO;
1138 }
1139 
1140 /**
1141  * ti8x_file_write_flash:
1142  * @filename: name of flash file where to write.
1143  * @content: the file content to write.
1144  * @real_filename: pointer address or NULL. Must be freed if needed when no longer needed.
1145  *
1146  * Write content to a flash file (os or app).
1147  *
1148  * Return value: an error code, 0 otherwise.
1149  **/
ti8x_file_write_flash(const char * fname,Ti8xFlash * head,char ** real_fname)1150 int ti8x_file_write_flash(const char *fname, Ti8xFlash *head, char **real_fname)
1151 {
1152 	FILE *f;
1153 	Ti8xFlash *content = head;
1154 	unsigned int i;
1155 	int bytes_written = 0;
1156 	long pos;
1157 	char *filename;
1158 	char varname[VARNAME_MAX];
1159 
1160 	if (head == NULL)
1161 	{
1162 		tifiles_critical("%s: head is NULL", __FUNCTION__);
1163 		return ERR_INVALID_FILE;
1164 	}
1165 
1166 	if (fname)
1167 	{
1168 		filename = g_strdup(fname);
1169 		if (filename == NULL)
1170 		{
1171 			return ERR_MALLOC;
1172 		}
1173 	}
1174 	else
1175 	{
1176 		VarEntry ve;
1177 
1178 		for (content = head; content != NULL; content = content->next)
1179 		{
1180 			if (content->data_type == TI83p_AMS || content->data_type == TI83p_APPL)
1181 			{
1182 				break;
1183 			}
1184 		}
1185 		if (content == NULL)
1186 		{
1187 			tifiles_critical("%s: content is NULL", __FUNCTION__);
1188 			return ERR_BAD_FILE;
1189 		}
1190 
1191 		strncpy(ve.name, content->name, sizeof(ve.name) - 1);
1192 		ve.name[sizeof(ve.name) - 1] = 0;
1193 		ve.type = content->data_type;
1194 
1195 		filename = tifiles_build_filename(content->model_dst, &ve);
1196 		if (real_fname != NULL)
1197 		{
1198 			*real_fname = g_strdup(filename);
1199 		}
1200 	}
1201 
1202 	f = g_fopen(filename, "wb");
1203 	if (f == NULL)
1204 	{
1205 		tifiles_info("Unable to open this file: %s", filename);
1206 		g_free(filename);
1207 		return ERR_FILE_OPEN;
1208 	}
1209 
1210 	for (content = head; content != NULL; content = content->next)
1211 	{
1212 		// header
1213 		if (fwrite_8_chars(f, "**TIFL**") < 0) goto tfwf;
1214 		if (fwrite_byte(f, content->revision_major) < 0) goto tfwf;
1215 		if (fwrite_byte(f, content->revision_minor) < 0) goto tfwf;
1216 		if (fwrite_byte(f, content->flags) < 0) goto tfwf;
1217 		if (fwrite_byte(f, content->object_type) < 0) goto tfwf;
1218 		if (fwrite_byte(f, content->revision_day) < 0) goto tfwf;
1219 		if (fwrite_byte(f, content->revision_month) < 0) goto tfwf;
1220 		if (fwrite_word(f, content->revision_year) < 0) goto tfwf;
1221 
1222 		memset(varname, 0, sizeof(varname));
1223 		ticonv_varname_to_tifile_sn(content->model_dst, content->name, varname, sizeof(varname), content->data_type);
1224 		if (content->data_type == TI83p_APPL)
1225 		{
1226 			// Determine the app name from the internal header if possible.
1227 			get_native_app_name(content, varname, sizeof(varname));
1228 		}
1229 		if (fwrite_byte(f, (uint8_t) strlen(varname)) < 0) goto tfwf;
1230 		if (fwrite_8_chars(f, varname) < 0) goto tfwf;
1231 		for (i = 0; i < 23; i++)
1232 		{
1233 			if (fwrite_byte(f, 0) < 0) goto tfwf;
1234 		}
1235 		if (fwrite_byte(f, content->device_type) < 0) goto tfwf;
1236 		if (fwrite_byte(f, content->data_type) < 0) goto tfwf;
1237 		for (i = 0; i < 23; i++)
1238 		{
1239 			if (fwrite_byte(f, 0) < 0) goto tfwf;
1240 		}
1241 		if (fwrite_byte(f, content->hw_id) < 0) goto tfwf;
1242 		pos = ftell(f);
1243 		if (pos == -1L) goto tfwf;
1244 		if (fwrite_long(f, content->data_length) < 0) goto tfwf;
1245 
1246 		// data
1247 		if (content->data_type == TI83p_CERT || content->data_type == TI83p_LICENSE)
1248 		{
1249 			if (fwrite(content->data_part, 1, content->data_length, f) < content->data_length) goto tfwf;
1250 		}
1251 		else if (content->data_type == TI83p_AMS || content->data_type == TI83p_APPL)
1252 		{
1253 			// write
1254 			if (content->hw_id == 0)
1255 			{
1256 				// TI-Z80-style OS / FlashApp, let's write Intel Hex format.
1257 				for (i = 0; content->pages != NULL && i < content->num_pages; i++)
1258 				{
1259 					uint32_t app_length, page_length;
1260 					int extra_bytes = 0;
1261 
1262 					page_length = content->pages[i]->size;
1263 
1264 					if (   content->data_type == TI83p_APPL && i == content->num_pages - 1
1265 					    && content->pages[0]->data[0] == 0x80 && content->pages[0]->data[1] == 0x0f)
1266 					{
1267 						/* Flash app signing programs will usually add some
1268 						   padding to the end of the app, and some programs
1269 						   seem to expect that padding to be there.
1270 
1271 						   The following is designed to mimic the behavior
1272 						   of these programs, padding to 96 bytes beyond the
1273 						   end of the application proper. */
1274 
1275 						/* get actual app length */
1276 						app_length = 6 + (  (((uint32_t)(content->pages[0]->data[2])) << 24)
1277 						                  | (((uint32_t)(content->pages[0]->data[3])) << 16)
1278 						                  | (((uint32_t)(content->pages[0]->data[4])) <<  8)
1279 						                  | ((uint32_t)(content->pages[0]->data[5])));
1280 
1281 						/* remove any existing padding */
1282 						while (page_length > 0 && content->pages[i]->data[page_length - 1] == 0xff)
1283 						{
1284 							page_length--;
1285 						}
1286 
1287 						extra_bytes = app_length + 96 - i * 0x4000 - page_length;
1288 
1289 						/* don't add padding beyond the end of the page */
1290 						if (page_length + extra_bytes >= 0x3fff)
1291 						{
1292 							extra_bytes = 0x3fff - page_length;
1293 						}
1294 
1295 						if (extra_bytes < 0)
1296 						{
1297 							extra_bytes = 0;
1298 						}
1299 						else if (extra_bytes > 96)
1300 						{
1301 							extra_bytes = 96;
1302 						}
1303 					}
1304 
1305 					bytes_written += hex_block_write(f,
1306 					  page_length, content->pages[i]->addr,
1307 					  content->pages[i]->flag, content->pages[i]->data,
1308 					  content->pages[i]->page, extra_bytes);
1309 				}
1310 
1311 				// final block
1312 				bytes_written += hex_block_write(f, 0, 0, 0, NULL, 0, 0);
1313 				if (fseek(f, -bytes_written - 4, SEEK_CUR)) goto tfwf;
1314 				if (fwrite_long(f, bytes_written) < 0) goto tfwf;
1315 				if (fseek(f, SEEK_END, 0L) ) goto tfwf;
1316 			}
1317 			else
1318 			{
1319 				// This looks like a TI-68k-style OS / FlashApp, containing flat data.
1320 				if (fwrite(content->data_part, 1, content->data_length, f) < content->data_length) goto tfwf;
1321 			}
1322 		}
1323 	}
1324 
1325 	g_free(filename);
1326 	fclose(f);
1327 	return 0;
1328 
1329 tfwf:	// release on exit
1330 	tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
1331 	g_free(filename);
1332 	fclose(f);
1333 	return ERR_FILE_IO;
1334 }
1335 
1336 
1337 /**************/
1338 /* Displaying */
1339 /**************/
1340 
1341 /**
1342  * ti8x_content_display_backup:
1343  * @content: a Ti8xBackup structure.
1344  *
1345  * Display fields of a Ti8xBackup structure.
1346  *
1347  * Return value: an error code, 0 otherwise.
1348  **/
ti8x_content_display_backup(Ti8xBackup * content)1349 int ti8x_content_display_backup(Ti8xBackup *content)
1350 {
1351 	if (content == NULL)
1352 	{
1353 		tifiles_critical("%s(NULL)", __FUNCTION__);
1354 		return ERR_INVALID_FILE;
1355 	}
1356 
1357 	tifiles_info("BackupContent for TI-8x: %p", content);
1358 	tifiles_info("Model:          %02X (%u)", content->model, content->model);
1359 	tifiles_info("Signature:      %s", tifiles_calctype2signature(content->model));
1360 	tifiles_info("Comment:        %s", content->comment);
1361 	tifiles_info("Type:           %02X (%s)", content->type, tifiles_vartype2string(content->model, content->type));
1362 	tifiles_info("Version:        %02X (%u)", content->version, content->version);
1363 	tifiles_info("Mem address:    %04X (%u)", content->mem_address, content->mem_address);
1364 
1365 	tifiles_info("data_length1:   %04X (%u)", content->data_length1, content->data_length1);
1366 	tifiles_info("data_part1:     %p", content->data_part1);
1367 	tifiles_info("data_length2:   %04X (%u)", content->data_length2, content->data_length2);
1368 	tifiles_info("data_part2:     %p", content->data_part2);
1369 	tifiles_info("data_length3:   %04X (%u)", content->data_length3, content->data_length3);
1370 	tifiles_info("data_part3:     %p", content->data_part3);
1371 
1372 	if (content->model == CALC_TI86)
1373 	{
1374 		tifiles_info("data_length4:   %04X (%u)", content->data_length4, content->data_length4);
1375 		tifiles_info("data_part4:     %p", content->data_part4);
1376 	}
1377 
1378 	tifiles_info("Checksum:       %04X (%u)", content->checksum, content->checksum);
1379 
1380 	return 0;
1381 }
1382 
1383 /**
1384  * ti8x_content_display_flash:
1385  * @content: a Ti8xFlash structure.
1386  *
1387  * Display fields of a Ti8xFlash structure.
1388  *
1389  * Return value: an error code, 0 otherwise.
1390  **/
ti8x_content_display_flash(Ti8xFlash * content)1391 int ti8x_content_display_flash(Ti8xFlash *content)
1392 {
1393 	Ti8xFlash *ptr;
1394 
1395 	for (ptr = content; ptr != NULL; ptr = ptr->next)
1396 	{
1397 		tifiles_info("FlashContent for TI-8x: %p", ptr);
1398 		tifiles_info("Model:           %02X (%u)", ptr->model, ptr->model);
1399 		tifiles_info("Signature:       %s", tifiles_calctype2signature(ptr->model));
1400 		tifiles_info("model_dst:       %02X (%u)", ptr->model_dst, ptr->model_dst);
1401 		tifiles_info("Revision:        %u.%u", ptr->revision_major, ptr->revision_minor);
1402 		tifiles_info("Flags:           %02X", ptr->flags);
1403 		tifiles_info("Object type:     %02X", ptr->object_type);
1404 		tifiles_info("Date:            %02X/%02X/%02X%02X", ptr->revision_day, ptr->revision_month, ptr->revision_year & 0xff, (ptr->revision_year & 0xff00) >> 8);
1405 		tifiles_info("Name:            %s", ptr->name);
1406 		tifiles_info("Device type:     %s", ptr->device_type == DEVICE_TYPE_83P ? "ti83+" : "ti73");
1407 		switch (ptr->data_type)
1408 		{
1409 			case 0x23:
1410 				tifiles_info("Data type:       OS data");
1411 				break;
1412 			case 0x24:
1413 				tifiles_info("Data type:       APP data");
1414 				break;
1415 			case 0x20:
1416 			case 0x25:
1417 				tifiles_info("Data type:       certificate");
1418 				break;
1419 			case 0x3E:
1420 				tifiles_info("Data type:       license");
1421 				break;
1422 			default:
1423 				tifiles_info("Data type:       Unknown (send mail to tilp-users@lists.sf.net)");
1424 				break;
1425 		}
1426 		tifiles_info("Hardware ID:     %02X (%u)", ptr->hw_id, ptr->hw_id);
1427 		tifiles_info("Length:          %08X (%u)", ptr->data_length, ptr->data_length);
1428 		tifiles_info("Data:            %p", ptr->data_part);
1429 		tifiles_info("Number of pages: %i", ptr->num_pages);
1430 		tifiles_info("Pages:           %p", ptr->pages);
1431 		tifiles_info("Next:            %p", ptr->next);
1432 	}
1433 
1434 	return 0;
1435 }
1436 
1437 /**
1438  * ti8x_file_display:
1439  * @filename: a TI file.
1440  *
1441  * Determine file class and display internal content.
1442  *
1443  * Return value: an error code, 0 otherwise.
1444  **/
ti8x_file_display(const char * filename)1445 int ti8x_file_display(const char *filename)
1446 {
1447 	Ti8xRegular *content1;
1448 	Ti8xBackup *content2;
1449 	Ti8xFlash *content3;
1450 	int ret;
1451 
1452 	if (tifiles_file_is_flash(filename))
1453 	{
1454 		content3 = tifiles_content_create_flash(CALC_TI83P);
1455 		ret = ti8x_file_read_flash(filename, content3);
1456 		if (!ret)
1457 		{
1458 			ti8x_content_display_flash(content3);
1459 			tifiles_content_delete_flash(content3);
1460 		}
1461 	}
1462 	else if (tifiles_file_is_regular(filename))
1463 	{
1464 		content1 = tifiles_content_create_regular(CALC_NONE);
1465 		ret = ti8x_file_read_regular(filename, content1);
1466 		if (!ret)
1467 		{
1468 			tifiles_file_display_regular(content1);
1469 			tifiles_content_delete_regular(content1);
1470 		}
1471 	}
1472 	else if (tifiles_file_is_backup(filename))
1473 	{
1474 		content2 = tifiles_content_create_backup(CALC_NONE);
1475 		ret = ti8x_file_read_backup(filename, content2);
1476 		if (!ret)
1477 		{
1478 			ti8x_content_display_backup(content2);
1479 			tifiles_content_delete_backup(content2);
1480 		}
1481 	}
1482 	else
1483 	{
1484 		tifiles_info("Unknown file type !");
1485 		return ERR_BAD_FILE;
1486 	}
1487 
1488 	return ret;
1489 }
1490 
1491 #endif
1492