cake
/
libg1m
Archived
1
0
Fork 0

Continued trying to correct G3P, corrected CAS type decoding

This commit is contained in:
Thomas Touhey 2017-03-21 16:32:08 +01:00
parent 6d3504d0cc
commit 57e1a4a112
8 changed files with 133 additions and 86 deletions

View File

@ -77,6 +77,14 @@
# include <libg1m/format/casemul.h>
/* ************************************************************************** */
/* The G1S format */
/* ************************************************************************** */
/* This format is basically a raw dump of the storage memory from the fx-9860G.
* It won't work with fx-CP or fx-CG calculators, as the storage memory
* has changed since. */
# include <libg1m/format/storage.h>
/* ************************************************************************** */
/* The G1M/STD format */
/* ************************************************************************** */
/* Since around 2004/2005, CASIO has adopted a single "superformat"; we call it

View File

@ -24,16 +24,18 @@
/* This format is used by CASIO with its calculator for storing things
* like add-ins (compiled programs), MCS (main memory saves), and others.
* In fact, there is no name for the general format, only names for the
* "subformats" based on this one.
*
* It all starts with a header, called Standard Header by Simon Lothar.
* "subformats" based on this one (which are mainly made of their extensions,
* such as G1M or G3P). */
/* ************************************************************************** */
/* Standard Header */
/* ************************************************************************** */
/* It all starts with a header, called Standard Header by Simon Lothar.
* This Standard Header contains the total filesize, the G1M type (add-in,
* MCS, e-acts), some data that will be useful for the MCS type, and some
* magic and control bytes.
*
* For some reason, this StandardHeader is INVERTED on every file it's on,
* you will have to apply a NOT operation on its bytes for it to make sense.
*
* Keep in mind that, everywhere in the header, multi-bytes integers
* are BIG ENDIAN.
*
@ -81,9 +83,11 @@ struct standard_header {
* in `include/libg1m/formatutils.h`.
*
* After the Standard Header is read and the type is read, we have parts,
* each with their own subheaders and their own subtilities.
*
* Some Prizm/Classpad-related formats (language, fkeys, add-ins) use a
* each with their own subheaders and their own subtilities. */
/* ************************************************************************** */
/* Standard Subheader */
/* ************************************************************************** */
/* Some Prizm/Classpad-related formats (language, fkeys, add-ins) use a
* common subheader followed by a platform-specific subheader (which is the
* same size of Prizm and Classpad, but doesn't seem to have the same
* field organization). Here is the common subheader structure: */
@ -198,9 +202,11 @@ struct _classpad_subheader {
/* Also, if the platform is the Prizm, there is a footer at the end of the
* file, which is only made of a 32-bit checksum that should be equal to
* the subheader checksum.
*
* Picture formats (C2P, G3P) have a common picture standard subheader, which
* the subheader checksum. */
/* ************************************************************************** */
/* Standard Picture Header */
/* ************************************************************************** */
/* Picture formats (C2P, G3P) have a common picture standard subheader, which
* is the following: */
struct standard_picheader {
@ -235,15 +241,16 @@ struct standard_picheader {
uint8_t _unknown2[0x7C];
};
/* Then we have the specific header -- see `libg1m/format/std/picture.h`.
*
* Where do you want to go next? Pick your poison. */
/* Then we have the specific header -- see `libg1m/format/std/picture.h`. */
/* ************************************************************************** */
/* Flavors */
/* ************************************************************************** */
/* Where do you want to go next? Pick your poison. */
# pragma pack()
# include <libg1m/format/std/addin.h>
# include <libg1m/format/std/eact.h>
# include <libg1m/format/std/mcs.h>
# include <libg1m/format/std/storage.h>
# include <libg1m/format/std/picture.h>
# include <libg1m/format/std/lang.h>
# include <libg1m/format/std/fkey.h>

View File

@ -52,7 +52,7 @@ struct g1l_subheader {
/* ************************************************************************** */
/* G3L - Language files for Prizm */
/* ************************************************************************** */
/* G3L and G3N start with the StandardHeader and the Standard sub-Header,
/* G3L and G3N start with the StandardHeader and the Standard Subheader,
* then the Prizm-specific subheader. After this, both the G3L and G3N
* have this language header: */

View File

@ -23,7 +23,8 @@
/* MCS is the main filesystem on CASIO calculators. They contain settings,
* programs, lists, pictures, captures, matrixes, and other data CASIO
* calculators own.
* calculators own. This format is common to fx and fx-CG MCS archives.
*
* Keep in mind that all numbers in the MCS format are BCD-encoded.
* Because of some precision issue (0.3), we'll deliver them to the user
* almost as is.

View File

@ -72,10 +72,10 @@ struct g3p_subheader {
/* The generator ID is a 16-bit ID giving information about the picture
* generator. The known IDs are the following: */
# define g3p_id_casio 0x3C1B
# define g3p_id_unknown0 0x388D
# define g3p_id_unknown1 0x789C
# define g3p_id_unknown2 0x3E93
# define g3p_id_casio0 0x3C1B /* one footer */
# define g3p_id_casio1 0x3E93 /* three footers */
# define g3p_id_capture 0x388D /* no footer */
# define g3p_id_converter 0x789C /* one footer */
/* Then we have the deflated image data, which is `data_size - 6` sized
* (see the `data_size` comment above for more explanation).

View File

@ -1,5 +1,5 @@
/* *****************************************************************************
* libg1m/format/std/storage.h -- the storage file format description.
* libg1m/format/storage.h -- the storage file format description.
* Copyright (C) 2017 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
*
* This file is part of libg1m.
@ -16,8 +16,8 @@
* You should have received a copy of the GNU Lesser General Public License
* along with libg1m; if not, see <http://www.gnu.org/licenses/>.
* ************************************************************************** */
#ifndef LIBG1M_FORMAT_STD_STORAGE_H
# define LIBG1M_FORMAT_STD_STORAGE_H
#ifndef LIBG1M_FORMAT_STORAGE_H
# define LIBG1M_FORMAT_STORAGE_H
# include <stdint.h>
# pragma pack(1)
@ -25,12 +25,11 @@
* It corresponds exactly to the on-calc storage memory structure.
*
* According to Simon Lothar, the files contain zeroes up to 0x00270000
* (including the StandardHeader; this is the location of the SMEM in real
* fx-9860 calculators), then it has the same structure than the one
* used by the calculator.
* (this is the mapped location of the SMEM in real fx-9860 calculators),
* then it has the same structure than the one used by the calculator.
*
* It looks like these files are not managed by the calculator (not directly);
* however, they are managed by FA-124. */
* These files are not managed by the calculator directly; however, they
* are managed by FA-124. */
/* ************************************************************************** */
/* Directory list */
/* ************************************************************************** */
@ -43,10 +42,9 @@ struct storage_entry {
/* the type number - see below */
uint16_t type;
/* the number of the entry
* if this field is 0x00, just ignore the entry.
* we don't know why entries with this field set to 0x00 exist...
* (probably backups?) */
/* the number of the entry; if this field is 0x00, just ignore the entry.
* we don't know why entries with this field set to 0x00 exist...
* (probably backups?) */
uint16_t id;
/* the raw subheader */
@ -70,7 +68,7 @@ struct storage_entry {
* This is also why optimizing is not done straight away (to save the flash
* a little).
*
* The list can have to 0x800 entries, and occupies the first sector.
* The list can contain up to 0x800 entries, and occupies the first sector.
* Simon Lothar says the list can expand to the second sector (which is used
* only if there is no space in the index sector or the other data sectors).
* Entries with the 0xFFFF type should not be read.
@ -143,14 +141,14 @@ struct storage_file {
/* File fragments */
/* ************************************************************************** */
/* After each file entry comes the corresponding fragments entries.
* Fragments are in fact links to the sectors, with some more info.
* Fragments are in fact links to the sectors, with some more information.
*
* Their special nibble have the same meaning that for files... but are not
* always accurate, so only check on files!
*
* The first fragment corresponding to a file contain this file's type,
* probably detected when the file is transferred. Here are the
* known values: */
* probably detected when the file is transferred/created/whatever. Here are
* the known values: */
# define g1m_storage_filetype_regular 0x01
# define g1m_storage_filetype_unknown g1m_storage_filetype_regular
@ -182,4 +180,4 @@ struct storage_fragment {
};
# pragma pack()
#endif /* LIBG1M_FORMAT_STD_STORAGE_H */
#endif /* LIBG1M_FORMAT_STORAGE_H */

View File

@ -58,50 +58,48 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer,
int err; *h = NULL;
uint8_t *defbuf = NULL, *infbuf = NULL;
/* start a checksum */
uint32_t check = g1m_checksum32((void*)std,
sizeof(struct standard_header), 0);
check = g1m_checksum32((void*)pic,
sizeof(struct standard_picheader), check);
/* read the image header */
DREAD(ihd, g3p_subheader)
check = g1m_checksum32(&ihd, sizeof(struct g3p_subheader), check);
unsigned int width = be16toh(ihd.width), height = be16toh(ihd.height);
int color_depth = be16toh(ihd.color_depth);
/* log info */
log_info("Width: %upx, height: %upx", width, height);
log_info("Pixel depth: %s",
(color_depth == g3p_color_4bit) ? "4-bit" : "16-bit");
log_info("Generator ID: 0x%04X", be16toh(ihd.generator_id));
g1m_pictureformat_t picfmt; uint16_t rawcol = be16toh(ihd.color_depth);
const char *coldesc; switch (rawcol) {
case g3p_color_4bit: picfmt = g1m_pictureformat_4bit_code;
coldesc = "4-bit"; break;
case g3p_color_16bit: picfmt = g1m_pictureformat_16bit;
coldesc = "16-bit"; break;
default: log_error("Unknown picture format: 0x%04" PRIX16, rawcol);
err = g1m_error_magic; goto fail;
}
log_info("Picture format: %s", coldesc);
int gen = be16toh(ihd.generator_id);
log_info("Generator ID: 0x%04X", gen);
log_info("-");
/* read deflated image */
size_t deflated_image_size = be32toh(ihd.data_size) - 6;
log_info("Reading %" PRIuSIZE "B of deflated data", deflated_image_size);
size_t deflated_size = be32toh(ihd.data_size) - 6; /* FIXME: dangerous */
log_info("Reading %" PRIuSIZE "B of deflated data", deflated_size);
err = g1m_error_alloc;
defbuf = malloc(deflated_image_size);
if (!defbuf) goto fail;
READ(defbuf, deflated_image_size)
if (!(defbuf = malloc(deflated_size))) goto fail;
READ(defbuf, deflated_size)
/* unobfuscate if required */
if (std->obfuscated0) {
log_info("Is obfuscated, let's deobfuscate!");
g3p_deobfuscate(defbuf, deflated_image_size);
g3p_deobfuscate(defbuf, deflated_size);
}
/* make the destination buffer */
size_t rawsize = color_depth == g3p_color_4bit
size_t rawsize = picfmt == g1m_pictureformat_4bit_code
? g1m_picturesize_4bit_code(width, height)
: g1m_picturesize_16bit(width, height);
err = g1m_error_alloc;
infbuf = malloc(rawsize);
if (!infbuf) goto fail;
if (!(infbuf = malloc(rawsize))) goto fail;
/* uncompress */
uLongf inflated_size = 0L; // rawsize?
int z_err = uncompress(infbuf, &inflated_size, defbuf, deflated_image_size);
uLong inflated_size = (uLong)rawsize;
int z_err = uncompress(infbuf, &inflated_size,
defbuf, (uLong)deflated_size);
if (z_err) {
log_fatal("Zlib error: error #%d", z_err);
err = g1m_error_magic;
@ -111,7 +109,9 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer,
/* check the checksum */
uint32_t checksum; READ(&checksum, sizeof(uint32_t))
uLong adl = adler32(0, defbuf, deflated_image_size);
if (std->obfuscated0) g3p_deobfuscate((void*)&checksum, sizeof(uint32_t));
checksum = be32toh(checksum);
uLong adl = adler32(0, infbuf, inflated_size);
if (adl != (uLong)checksum) {
log_fatal("Incorrect Adler32 checksum!");
log_fatal("Expected 0x%08" PRIX32 ", got 0x%08lX",
@ -126,8 +126,7 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer,
g1m_t *handle = *h;
/* then store it */
g1m_decode_picture(handle->pixels, ihd.color_depth == g3p_color_4bit
? g1m_pictureformat_4bit_code : g1m_pictureformat_16bit,
g1m_decode_picture(handle->pixels, picfmt,
infbuf, handle->width, handle->height);
free(infbuf); infbuf = NULL;

View File

@ -21,36 +21,47 @@
/* ************************************************************************** */
/* Local types */
/* ************************************************************************** */
/* Flags */
#define mult 0x0001
#define noarg 0x0000
#define arg 0x0002
#define arg_is_num 0x0000
/* Correspondance type */
struct type_corresp {
/* identification */
const char *datatype;
unsigned int flags;
/* libg1m MCS file type, flags */
g1m_mcstype_t type;
unsigned int flags;
g1m_pictureformat_t picformat;
};
/* ************************************************************************** */
/* Correspondances */
/* ************************************************************************** */
/* All correspondances found by Tom Wheeley and Tom Lynn. Remarks:
* - Correspondances with a NULL data type means the data type isn't to be
* read. */
/* All correspondances found by Tom Wheeley, Tom Lynn (creators of CaS),
* and Göran Weinholt (creator of Cafix). */
#define UNIMPLEMENTED(S) {S, 0, 0, 0}
#define TTERM {NULL, 0, 0, 0}
#define BASIC(S, T) {S, noarg, T, 0}
#define CAPT(S, PT) {S, noarg, g1m_mcstype_capture, PT}
#define UNIMPLEMENTED(S) {S, noarg, 0, 0}
#define TTERM {NULL, noarg, 0, 0}
static struct type_corresp cas_groups[] = {
/* implemented */
{"LT", g1m_mcstype_list, 0, 0},
{"MT", g1m_mcstype_matrix, 0, 0},
{"P1", g1m_mcstype_program, 0, 0},
{"PG", g1m_mcstype_program, 0, 0},
{"PZ", g1m_mcstype_program, g1m_mcsflag_multiple, 0},
{"VM", g1m_mcstype_variable, 0, 0},
{"DC", g1m_mcstype_capture, 0, g1m_pictureformat_4bit_color},
{"DD", g1m_mcstype_capture, 0, g1m_pictureformat_4bit_mono},
/* basic things */
BASIC("LT", g1m_mcstype_list),
BASIC("MT", g1m_mcstype_matrix),
BASIC("PG", g1m_mcstype_program),
BASIC("VM", g1m_mcstype_variable),
/* programs */
{"P", arg | arg_is_num, g1m_mcstype_program, 0},
{"PZ", mult | noarg, g1m_mcstype_program, 0},
/* captures */
CAPT("DC", g1m_pictureformat_4bit_color),
CAPT("DD", g1m_pictureformat_4bit_mono),
/* not implemented yet */
UNIMPLEMENTED("AA"), // dynamic graph functions
@ -89,12 +100,30 @@ static struct type_corresp cas_groups[] = {
/* ************************************************************************** */
/* Main functions */
/* ************************************************************************** */
/**
* get_number:
* Get number from string.
*
* Adapted from `type/mcs.c`. If you need letter ID decoding or other
* stuff, go and see there.
*
* @arg s the string.
* @arg num pointer to the num to fill.
* @return if there was an error (0 if ok).
*/
static int get_number(const char *s, int *num)
{
if (!(*num = atoi(s))) return (1);
return (0);
}
/**
* g1m_maketype_cas:
* Get the libg1m MCS type from raw CAS identification data.
*
* @arg head the head to fill.
* @arg datatype the data type string.
* @arg datatype the data type string (2 bytes long).
* @return the error (if any).
*/
@ -104,12 +133,16 @@ int g1m_maketype_cas(g1m_mcshead_t *head, const char *datatype)
memset(head, 0, sizeof(g1m_mcshead_t));
head->flags |= g1m_mcsinfo_cas;
memcpy(head->_datatype, datatype, 2);
head->_datatype[2] = 0;
/* look for correspondance */
struct type_corresp *c = cas_groups - 1;
while ((++c)->datatype) {
if (c->datatype && strncmp(datatype, c->datatype, 4))
struct type_corresp *c = cas_groups - 1; int id = 0;
while ((++c)->type) {
size_t pl = strlen(c->datatype);
/* check if pattern is there */
if (strncmp(c->datatype, datatype, pl))
continue;
if ((c->flags & arg) && get_number(&head->name[pl], &id))
continue;
break;
}
@ -117,12 +150,13 @@ int g1m_maketype_cas(g1m_mcshead_t *head, const char *datatype)
/* fill in info and return */
head->type = c->type;
head->id = id;
head->_picformat = c->picformat;
head->flags |= c->flags;
if (c->flags & mult) head->flags |= g1m_mcsflag_multiple;
return (0);
notfound:
log_info("Type with '%.4s' data string was not implemented or not "
log_info("Type with '%.2s' data string was not implemented or not "
"recognized.", datatype);
head->type = 0;
return (1);