1 /* Hey EMACS -*- linux-c -*- */
2 /* $Id: files9x.c 3524 2007-06-26 13:31:26Z roms $ */
3 
4 /*  libtifiles - file format library, a part of the TiLP project
5  *  Copyright (C) 1999-2005  Romain Lievin
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software Foundation,
19  *  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 /*
23 	TI File Format handling routines
24 	Calcs: TI-NSpire
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 "filesnsp.h"
39 #include "rwfile.h"
40 
41 
42 /***********/
43 /* Reading */
44 /***********/
45 
46 /**
47  * tnsp_file_read_regular:
48  * @filename: name of file to open.
49  * @content: where to store the file content.
50  *
51  * Load the file into a FileContent structure.
52  *
53  * Structure content must be freed with #tifiles_content_delete_regular when
54  * no longer used. If error occurs, the structure content is released for you.
55  *
56  * Return value: an error code, 0 otherwise.
57  **/
tnsp_file_read_regular(const char * filename,FileContent * content)58 int tnsp_file_read_regular(const char *filename, FileContent *content)
59 {
60 	FILE *f;
61 	int ret = ERR_FILE_IO;
62 
63 	if (content == NULL)
64 	{
65 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
66 		return ERR_INVALID_FILE;
67 	}
68 
69 	if (!tifiles_file_is_regular(filename))
70 	{
71 		ret = ERR_INVALID_FILE;
72 		goto tfrr2;
73 	}
74 
75 	f = g_fopen(filename, "rb");
76 	if (f == NULL)
77 	{
78 		tifiles_info( "Unable to open this file: %s", filename);
79 		ret = ERR_FILE_OPEN;
80 		goto tfrr2;
81 	}
82 
83 	content->model = CALC_NSPIRE;
84 	content->model_dst = content->model;
85 
86 	content->entries = g_malloc0((content->num_entries + 1) * sizeof(VarEntry*));
87 
88 	{
89 		long cur_pos;
90 		VarEntry *entry = content->entries[0] = g_malloc0(sizeof(VarEntry));
91 
92 		gchar *basename = g_path_get_basename(filename);
93 		gchar *ext = tifiles_fext_get(basename);
94 
95 		entry->type = tifiles_fext2vartype(content->model, ext);
96 		if (ext && ext[0]) *(ext-1) = '\0';
97 
98 		entry->folder[0] = 0;
99 		strncpy(entry->name, basename, sizeof(entry->name) - 1);
100 		entry->name[sizeof(entry->name) - 1] = 0;
101 		g_free(basename);
102 
103 		entry->attr = ATTRB_NONE;
104 		if (fseek(f, 0, SEEK_END) < 0) goto tfrr;
105 		cur_pos = ftell(f);
106 		if (cur_pos < 0) goto tfrr;
107 		if (fseek(f, 0, SEEK_SET) < 0) goto tfrr;
108 
109 		// The Nspire series' members have at best 128 MB of Flash (TODO: modify this code if this no longer holds).
110 		// Regular files larger than that size are insane.
111 		if (cur_pos >= (128L << 20))
112 		{
113 			ret = ERR_INVALID_FILE;
114 			goto tfrr;
115 		}
116 		entry->size = (uint32_t)cur_pos;
117 
118 		entry->data = (uint8_t *)g_malloc0(entry->size);
119 		if(fread(entry->data, 1, entry->size, f) < entry->size) goto tfrr;
120 	}
121 
122 	content->num_entries++;
123 
124 	fclose(f);
125 	return 0;
126 
127 tfrr:	// release on exit
128 	tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
129 	fclose(f);
130 tfrr2:
131 	tifiles_content_delete_regular(content);
132 	return ret;
133 }
134 
135 /**
136  * tnsp_file_read_flash:
137  * @filename: name of flash file to open.
138  * @content: where to store the file content.
139  *
140  * Load the flash file into a #FlashContent structure.
141  *
142  * Structure content must be freed with #tifiles_content_delete_flash when
143  * no longer used. If error occurs, the structure content is released for you.
144  *
145  * Return value: an error code, 0 otherwise.
146  **/
tnsp_file_read_flash(const char * filename,FlashContent * content)147 int tnsp_file_read_flash(const char *filename, FlashContent *content)
148 {
149 	FILE *f;
150 	int c;
151 	long cur_pos;
152 	uint32_t file_size;
153 	int ret = ERR_FILE_IO;
154 
155 	if (content == NULL)
156 	{
157 		tifiles_critical("%s: an argument is NULL", __FUNCTION__);
158 		return ERR_INVALID_FILE;
159 	}
160 
161 	if (!tifiles_file_is_tno(filename))
162 	{
163 		ret = ERR_INVALID_FILE;
164 		goto tfrf2;
165 	}
166 
167 	f = g_fopen(filename, "rb");
168 	if (f == NULL)
169 	{
170 		tifiles_info("Unable to open this file: %s", filename);
171 		ret = ERR_FILE_OPEN;
172 		goto tfrf2;
173 	}
174 
175 	if (fseek(f, 0, SEEK_END) < 0) goto tfrf;
176 	cur_pos = ftell(f);
177 	if (cur_pos < 0) goto tfrf;
178 	if (fseek(f, 0, SEEK_SET) < 0) goto tfrf;
179 	// The Nspire series' members have at best 128 MB of Flash (TODO: modify this code if this no longer holds).
180 	// Flash files larger than that size are insane.
181 	if (cur_pos >= (128L << 20))
182 	{
183 		ret = ERR_INVALID_FILE;
184 		goto tfrf;
185 	}
186 	file_size = (uint32_t)cur_pos;
187 
188 	content->model = CALC_NSPIRE;
189 
190 	// Skip chars.
191 	c = 0;
192 	while (c != ' ')
193 	{
194 		c = fgetc(f);
195 		if (c == EOF)
196 		{
197 			goto tfrf;
198 		}
199 	}
200 
201 	// Read revision major.
202 	c = fgetc(f);
203 	if (c == EOF)
204 	{
205 		goto tfrf;
206 	}
207 	content->revision_major = c;
208 
209 	// Skip char.
210 	c = fgetc(f);
211 	if (c == EOF)
212 	{
213 		goto tfrf;
214 	}
215 
216 	// Read revision minor.
217 	c = fgetc(f);
218 	if (c == EOF)
219 	{
220 		goto tfrf;
221 	}
222 	content->revision_minor = c;
223 
224 	// Skip chars.
225 	c = fgetc(f);
226 	if (c == EOF)
227 	{
228 		goto tfrf;
229 	}
230 
231 	c = 0;
232 	while (c != ' ')
233 	{
234 		c = fgetc(f);
235 		if (c == EOF)
236 		{
237 			goto tfrf;
238 		}
239 	}
240 	if (fscanf(f, "%i", &(content->data_length)) < 1)
241 	{
242 		goto tfrf;
243 	}
244 	if (content->data_length > file_size)
245 	{
246 		ret = ERR_INVALID_FILE;
247 		goto tfrf;
248 	}
249 	if (fseek(f, 0, SEEK_SET) < 0) goto tfrf;
250 
251 	content->data_part = (uint8_t *)g_malloc0(content->data_length);
252 	if (content->data_part == NULL)
253 	{
254 		fclose(f);
255 		tifiles_content_delete_flash(content);
256 		return ERR_MALLOC;
257 	}
258 
259 	content->next = NULL;
260 	if(fread(content->data_part, 1, content->data_length, f) < content->data_length) goto tfrf;
261 
262 	fclose(f);
263 	return 0;
264 
265 tfrf:	// release on exit
266 	tifiles_critical("%s: error reading / understanding file %s", __FUNCTION__, filename);
267 	fclose(f);
268 tfrf2:
269 	tifiles_content_delete_flash(content);
270 	return ret;
271 }
272 
273 /***********/
274 /* Writing */
275 /***********/
276 
277 /**
278  * tnsp_file_write_regular:
279  * @filename: name of file where to write or NULL.
280  * @content: the file content to write.
281  * @real_filename: pointer address or NULL. Must be freed if needed when no longer needed.
282  *
283  * Write one variable into a single file. If filename is set to NULL,
284  * the function build a filename from varname and allocates resulting filename in %real_fname.
285  * %filename and %real_filename can be NULL but not both !
286  *
287  * %real_filename must be freed when no longer used.
288  *
289  * Return value: an error code, 0 otherwise.
290  **/
tnsp_file_write_regular(const char * fname,FileContent * content,char ** real_fname)291 int tnsp_file_write_regular(const char *fname, FileContent *content, char **real_fname)
292 {
293 	FILE *f;
294 	char *filename = NULL;
295 	VarEntry *entry;
296 
297 	if (content->entries == NULL || content->entries[0] == NULL)
298 	{
299 		tifiles_warning("%s: skipping content with NULL content->entries or content->entries[0]", __FUNCTION__);
300 		return ERR_FILE_IO;
301 	}
302 
303 	if (fname != NULL)
304 	{
305 		filename = g_strdup(fname);
306 		if (filename == NULL)
307 		{
308 			return ERR_MALLOC;
309 		}
310 	}
311 	else
312 	{
313 		filename = tifiles_build_filename(content->model_dst, content->entries[0]);
314 		if (real_fname != NULL)
315 		{
316 			*real_fname = g_strdup(filename);
317 		}
318 	}
319 
320 	f = g_fopen(filename, "wb");
321 	if (f == NULL)
322 	{
323 		tifiles_info( "Unable to open this file: %s", filename);
324 		g_free(filename);
325 		return ERR_FILE_OPEN;
326 	}
327 
328 	entry = content->entries[0];
329 	if(fwrite(entry->data, 1, entry->size, f) < entry->size)
330 	{
331 		goto tfwr;
332 	}
333 
334 	g_free(filename);
335 	fclose(f);
336 	return 0;
337 
338 tfwr:  // release on exit
339 	tifiles_critical("%s: error writing file %s", __FUNCTION__, filename);
340 	g_free(filename);
341 	fclose(f);
342 	return ERR_FILE_IO;
343 }
344 
345 /**************/
346 /* Displaying */
347 /**************/
348 
349 /**
350  * tnsp_content_display_flash:
351  * @content: a FlashContent structure.
352  *
353  * Display fields of a FlashContent structure.
354  *
355  * Return value: an error code, 0 otherwise.
356  **/
tnsp_content_display_flash(FlashContent * content)357 int tnsp_content_display_flash(FlashContent *content)
358 {
359 	FlashContent *ptr = content;
360 
361 	if (content == NULL)
362 	{
363 		tifiles_critical("%s(NULL)", __FUNCTION__);
364 		return ERR_INVALID_FILE;
365 	}
366 
367 	for (ptr = content; ptr != NULL; ptr = ptr->next)
368 	{
369 		tifiles_info("FlashContent for TI-Nspire: %p", ptr);
370 		tifiles_info("Model:           %02X (%u)", ptr->model, ptr->model);
371 		tifiles_info("Signature:       %s", tifiles_calctype2signature(ptr->model));
372 		tifiles_info("model_dst:       %02X (%u)", ptr->model_dst, ptr->model_dst);
373 		tifiles_info("Revision:        %u.%u", ptr->revision_major, ptr->revision_minor);
374 		tifiles_info("Flags:           %02X", ptr->flags);
375 		tifiles_info("Object type:     %02X", ptr->object_type);
376 		tifiles_info("Date:            %02X/%02X/%02X%02X", ptr->revision_day, ptr->revision_month, ptr->revision_year & 0xff, (ptr->revision_year & 0xff00) >> 8);
377 		tifiles_info("Name:            %s", ptr->name);
378 		tifiles_info("Data type:       OS data");
379 		tifiles_info("Length:          %08X (%i)", ptr->data_length, ptr->data_length);
380 		tifiles_info("Data part:       %p", ptr->data_part);
381 		tifiles_info("Next:            %p", ptr->next);
382 	}
383 
384 	return 0;
385 }
386 
387 /**
388  * tnsp_file_display:
389  * @filename: a TI file.
390  *
391  * Determine file class and display internal content.
392  *
393  * Return value: an error code, 0 otherwise.
394  **/
tnsp_file_display(const char * filename)395 int tnsp_file_display(const char *filename)
396 {
397 	FileContent *content1;
398 	FlashContent *content3;
399 	int ret;
400 
401 	if (tifiles_file_is_os(filename))
402 	{
403 		content3 = tifiles_content_create_flash(CALC_NSPIRE);
404 		ret = tnsp_file_read_flash(filename, content3);
405 		if (!ret)
406 		{
407 			tnsp_content_display_flash(content3);
408 			tifiles_content_delete_flash(content3);
409 		}
410 	}
411 	else if (tifiles_file_is_regular(filename))
412 	{
413 		content1 = tifiles_content_create_regular(CALC_NSPIRE);
414 		ret = tnsp_file_read_regular(filename, content1);
415 		if (!ret)
416 		{
417 			tifiles_file_display_regular(content1);
418 			tifiles_content_delete_regular(content1);
419 		}
420 	}
421 	else
422 	{
423 		tifiles_info("Unknown file type !");
424 		return ERR_BAD_FILE;
425 	}
426 
427 	return ret;
428 }
429