aboutsummaryrefslogtreecommitdiff
path: root/lib/file/decode/std.c
blob: e696db2b3bfd9eff7c18f3cee4c04aa59a61f353 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
/* ****************************************************************************
 * file/decode/std.c -- decode a "standard" CASIO file.
 * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
 *
 * This file is part of libcasio.
 * libcasio is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3.0 of the License,
 * or (at your option) any later version.
 *
 * libcasio is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with libcasio; if not, see <http://www.gnu.org/licenses/>.
 * ************************************************************************* */
#include "decode.h"
#include "../corresp/standard.h"
#define FUNC(NAME) casio_decode_std_##NAME

/* ************************************************************************* */
/*  Getting the decoding function                                            */
/* ************************************************************************* */
/* Correspondance type */
typedef int decode_func ();
struct decode_corresp {
	unsigned int platform;
	unsigned int type;

	/* result */
	decode_func *decode;
};

/* The correspondances */
CASIO_LOCAL struct decode_corresp decode_functions[] = {
	/* add-ins */
	{casio_filefor_fx, casio_filetype_addin,     FUNC(addin)},
	{casio_filefor_cp, casio_filetype_addin,     FUNC(cp_addin)},
	{casio_filefor_cg, casio_filetype_addin,     FUNC(cg_addin)},
	/* mcs */
	{casio_filefor_fx, casio_filetype_mcs,       FUNC(mcs)},
	{casio_filefor_cg, casio_filetype_mcs,       FUNC(mcs)},
	/* language files */
	{casio_filefor_fx, casio_filetype_lang,      FUNC(lang)},
	{casio_filefor_cg, casio_filetype_lang,      FUNC(cg_lang)},
	/* function keys file */
	{casio_filefor_fx, casio_filetype_fkey,      FUNC(fkey)},
	{casio_filefor_cg, casio_filetype_fkey,      FUNC(cg_fkey)},
	/* e-activities */
/*	{casio_filefor_fx, casio_filetype_eact,      FUNC(eact)}, */
	/* pictures */
	{casio_filefor_cg, casio_filetype_pict,      FUNC(g3p)},
	{casio_filefor_cp, casio_filetype_pict,      FUNC(c2p)},

	{0, 0, NULL}
};

/**
 *	find_decode_function:
 *	Find the parsing function.
 *
 *	@arg	platform	the platform.
 *	@arg	type		the type.
 *	@arg	rd			pointer to the decode function.
 *	@return				the error code (0 if ok).
 */

CASIO_LOCAL int find_decode_function(casio_filefor_t platform,
	casio_filetype_t type, decode_func **rd)
{
	struct decode_corresp *c;

	/* get the function */
	for (c = decode_functions; c->decode; c++) {
		if (c->type != type)
			continue;
		if (c->platform != platform)
			continue;

		break;
	}
	if (!c->decode) {
		msg((ll_fatal, "No parsing function was found for this type."));
		return (casio_error_magic);
	}

	/* set the vars */
	*rd = c->decode;
	return (0);
}
/* ************************************************************************* */
/*  Main standard header decoding function                                   */
/* ************************************************************************* */
/**
 *	casio_decode_std:
 *	Decode a file with standard header.
 *
 *	@arg	h				the handle to create.
 *	@arg	path			the path.
 *	@arg	buffer			the buffer to read from.
 *	@arg	std				the standard header.
 *	@arg	expected_types	the expected types.
 *	@return					the error code (0 if ok).
 */

int CASIO_EXPORT casio_decode_std(casio_file_t **h, const char *path,
	casio_stream_t *buffer, casio_standard_header_t *std,
	casio_filetype_t expected_types)
{
	int err;
	casio_filetype_t type; casio_filefor_t platform;
	decode_func *rd; unsigned int mflags;
	casio_uint32_t check = casio_checksum32(std,
		sizeof(casio_standard_header_t), 0);

	/* reverse the standard header */
	{
		unsigned char *u = (unsigned char*)std;
		size_t i;

		for (i = 0; i < sizeof(casio_standard_header_t); i++)
			u[i] = ~u[i];
	}

	/* print header */
	msg((ll_info, "Raw inverted standard header is:"));
	mem((ll_info, std, sizeof(casio_standard_header_t)));
	std->casio_standard_header_filesize =
		be32toh(std->casio_standard_header_filesize);
	std->casio_standard_header_number =
		be16toh(std->casio_standard_header_number);
	msg((ll_info, "Standard Header filesize is 0x%08" CASIO_PRIX32 "o",
		std->casio_standard_header_filesize));


	/* get the type */
	if (casio_maketype_std(path, std, &mflags, &platform, &type))
		return (casio_error_magic);

	/* check control bytes */
	if (mflags & casio_stdflag_check1
	 && std->casio_standard_header_control
	  != ((std->casio_standard_header_filesize + 0x41) & 0xff)) {
		msg((ll_info, "First control byte isn't right."));
		return (casio_error_magic);
	} else if (mflags & casio_stdflag_check2
	 && std->casio_standard_header_control2
	  != ((std->casio_standard_header_filesize + 0xb8) & 0xff)) {
		msg((ll_info, "Second control byte isn't right."));
		return (casio_error_magic);
	}

	/* check if there is a standard subheader */
	if (mflags & casio_stdflag_sub) {
		casio_standard_subheader_t hd;
		casio_stream_t *csum_stream;

		msg((ll_info, "Has a Standard Subheader!"));
		DREAD(hd)
		if (casio_maketype_sub(&hd, &mflags, &type, &platform))
			return (casio_error_magic);

		/* TODO: controls */
		/* find the decode function */
		if (find_decode_function(platform, type, &rd))
			return (casio_error_magic);
		if (expected_types && !(type & expected_types))
			return (casio_error_wrong);

		/* read and decode for specific platforms */
		check = casio_checksum32(&hd.casio_standard_subheader_filetype,
			sizeof(casio_standard_header_t) - 4, check);
		if (platform == casio_filefor_cp) {
			casio_standard_classpad_subheader_t shd;

			DREAD(shd)
			check = casio_checksum32(&shd,
				sizeof(casio_standard_classpad_subheader_t), check);

			/* make the checksum stream */
			msg((ll_info, "Opening the checksum stream"));
			err = casio_open_csum32(&csum_stream, buffer, &check);
			if (err) return (err);

			/* decode the file content */
			msg((ll_info, "Decoding the file using the specific function"));
			err = (*rd)(h, csum_stream, std, &hd, &shd);
			casio_close(csum_stream);
			if (err) return (err);
		} else if (platform == casio_filefor_cg) {
			casio_standard_prizm_subheader_t shd;
			casio_uint32_t endcheck;

			DREAD(shd)
			check = casio_checksum32(&shd,
				sizeof(casio_standard_prizm_subheader_t), check);

			/* make the checksum stream */
			msg((ll_info, "Opening the checksum stream"));
			err = casio_open_csum32(&csum_stream, buffer, &check);
			if (err) return (err);

			/* decode the file content */
			msg((ll_info, "Decoding the file using the specific function"));
			err = (*rd)(h, csum_stream, std, &hd, &shd);
			casio_close(csum_stream);
			if (err) return (err);

			/* read the footer */
			GDREAD(endcheck)
			err = casio_error_csum;
			if (be32toh(endcheck)
			 != be32toh(hd.casio_standard_subheader_checksum))
				goto fail;
		}

		/* check the sum */
		err = casio_error_csum;
		if (check != be32toh(hd.casio_standard_subheader_checksum)) {
			msg((ll_error, "Checksum mismatch: got 0x%08" CASIO_PRIX32
				", expected 0x%08" CASIO_PRIX32,
				check, be32toh(hd.casio_standard_subheader_checksum)));
			goto fail;
		}

		/* no error */
		return (0);
	}

	/* check if there is a standard picture header */
	if (mflags & casio_stdflag_pic) {
		casio_standard_picheader_t hd;

		msg((ll_info, "Has a Standard Picture Header!"));
		DREAD(hd)

		/* correct the type; TODO: hardcode less */
		type = casio_filetype_picture;
		if (!memcmp(hd.casio_standard_picheader_cp, "CC", 2))
			platform = casio_filefor_cp;
		else if (!memcmp(hd.casio_standard_picheader_cp, "CP", 2))
			platform = casio_filefor_cg;
		else {
			msg((ll_error, "Unknown magic sequence: '%.2s'",
				hd.casio_standard_picheader_cp));
			return (casio_error_magic);
		}

		/* find the decode function */
		if (find_decode_function(platform, type, &rd))
			return (casio_error_magic);

		/* check the obfuscation bytes; imitates syscall 0x0C12
		 * http://bible.planet-casio.com/simlo/chm/v20/fxCG20_Bfile.htm */
		if (std->casio_standard_header_obfuscated0 ==
		  std->casio_standard_header_subtype[0]
		+ ((std->casio_standard_header_filesize & 0xFF00) >> 8)
		+  (std->casio_standard_header_filesize & 0xFF)) {
			std->casio_standard_header_obfuscated0 = 0;
			std->casio_standard_header_obfuscated1 = 0;
		} else {
			std->casio_standard_header_obfuscated0 = 1;
			std->casio_standard_header_obfuscated1 = 1;
		}

		/* call it */
		err = (*rd)(h, buffer, std, &hd);
		if (err) return (err);

		/* no error! */
		return (0);
	}

	/* find the decode function */
	if (find_decode_function(platform, type, &rd))
		return (casio_error_magic);
	if (expected_types && !(type & expected_types))
		return (casio_error_wrong);

	/* log some data */
	msg((ll_info, "Standard Header filesize is %" CASIO_PRIu32 "o.",
		std->casio_standard_header_filesize));
	msg((ll_info, "Standard Header num is %" CASIO_PRIu16 ".",
		std->casio_standard_header_number));

	/* subdecode. */
	return ((*rd)(h, buffer, std));
fail:
	casio_free_file(*h); *h = NULL;
	return (err);
}