297 lines
8.8 KiB
C
297 lines
8.8 KiB
C
/* ****************************************************************************
|
|
* 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);
|
|
}
|