diff --git a/include/libg1m/format.h b/include/libg1m/format.h index ef70ade..d04fa30 100644 --- a/include/libg1m/format.h +++ b/include/libg1m/format.h @@ -77,6 +77,14 @@ # include /* ************************************************************************** */ +/* 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 +/* ************************************************************************** */ /* The G1M/STD format */ /* ************************************************************************** */ /* Since around 2004/2005, CASIO has adopted a single "superformat"; we call it diff --git a/include/libg1m/format/std.h b/include/libg1m/format/std.h index 004bbc7..0df24f2 100644 --- a/include/libg1m/format/std.h +++ b/include/libg1m/format/std.h @@ -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 # include # include -# include # include # include # include diff --git a/include/libg1m/format/std/lang.h b/include/libg1m/format/std/lang.h index 8c5fdd6..261a48d 100644 --- a/include/libg1m/format/std/lang.h +++ b/include/libg1m/format/std/lang.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: */ diff --git a/include/libg1m/format/std/mcs.h b/include/libg1m/format/std/mcs.h index ba5787d..6f83431 100644 --- a/include/libg1m/format/std/mcs.h +++ b/include/libg1m/format/std/mcs.h @@ -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. diff --git a/include/libg1m/format/std/picture.h b/include/libg1m/format/std/picture.h index e21b8f0..97f524b 100644 --- a/include/libg1m/format/std/picture.h +++ b/include/libg1m/format/std/picture.h @@ -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). diff --git a/include/libg1m/format/std/storage.h b/include/libg1m/format/storage.h similarity index 89% rename from include/libg1m/format/std/storage.h rename to include/libg1m/format/storage.h index 0991c18..0de9d9c 100644 --- a/include/libg1m/format/std/storage.h +++ b/include/libg1m/format/storage.h @@ -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 * * 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 . * ************************************************************************** */ -#ifndef LIBG1M_FORMAT_STD_STORAGE_H -# define LIBG1M_FORMAT_STD_STORAGE_H +#ifndef LIBG1M_FORMAT_STORAGE_H +# define LIBG1M_FORMAT_STORAGE_H # include # 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 */ diff --git a/src/decode/std/picture.c b/src/decode/std/picture.c index 3d5cc2d..e46c4d7 100644 --- a/src/decode/std/picture.c +++ b/src/decode/std/picture.c @@ -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; diff --git a/src/type/cas.c b/src/type/cas.c index 137e740..44d42c9 100644 --- a/src/type/cas.c +++ b/src/type/cas.c @@ -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);