Continued trying to correct G3P, corrected CAS type decoding
This commit is contained in:
parent
6d3504d0cc
commit
57e1a4a112
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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: */
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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 */
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Reference in New Issue