From da972cbc73a0fd2ca3e7a966b493c4af05708977 Mon Sep 17 00:00:00 2001 From: "Thomas \"Cakeisalie5\" Touhey" Date: Sun, 26 Mar 2017 03:14:34 +0200 Subject: [PATCH] Corrected CAS management with new information. --- AUTHORS.md | 1 + include/libg1m/format/cas.h | 102 ++++++++++++++++++++++---- include/libg1m/formatutils.h | 4 +- include/libg1m/mcs.h | 26 +++---- src/decode/cas.c | 65 ++++++++++------- src/manage/mcs.c | 2 +- src/type/cas/app.c | 138 +++++++++++++++-------------------- src/type/cas/datatype.c | 8 +- src/type/mcs.c | 2 +- 9 files changed, 205 insertions(+), 143 deletions(-) diff --git a/AUTHORS.md b/AUTHORS.md index 0633086..af95d20 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -19,4 +19,5 @@ Thanks to: - Tom Wheeley for making CaS a free software; - Him and Tom Lynn for documenting the legacy protocol (9700); - Zezombye (from Planète Casio) for its research on some MCS file formats; +- David Quaranta for having made and shared Flash100's source code; - Other people at **Planète Casio** for their support! diff --git a/include/libg1m/format/cas.h b/include/libg1m/format/cas.h index bc3e1ef..5f1673c 100644 --- a/include/libg1m/format/cas.h +++ b/include/libg1m/format/cas.h @@ -72,24 +72,40 @@ struct cas40 { /* The specific bytes are different according to the datatype, but its * length is fixed. */ /* ************************************************************************** */ -/* CAS50 header */ +/* CASDYN header */ /* ************************************************************************** */ -/* The CAS50 (50 bytes long) header appeared later. +/* The CASDYN (dynamic size) header appeared later. * It is known in CaS as the protocol the CFX-9850G uses. * - * This format was previously named `caspro` in libg1m, before finally - * using the CAS50 name, which was more appropriate. - * Notice that most headers the Graph 100 (~2002) still used CAS40. + * This header format was previously named 'caspro' in libg1m, then CAS50 when + * I thought the header was always 50 bytes long, then CASDYN as soon as I + * realized this application system was there on headers of different sizes. * - * The main differences between CAS40 and CAS50 are: - * - CAS50 has a more `struct`-ural approach (no specific subheader); - * - CAS50 include the app you're interacting with. - * - * The header format is the following: */ + * I started understanding that this byte between the app and data type I + * thought was useless was in fact not when I saw in Flash100 sources that it + * was always 0x31 ('1' in ASCII), which are 40 bytes long. From there and other + * pieces of documentation, here are the extension types I could find: */ -struct cas50 { +# define casdyn_ext_9850 0x00 /* 50 bytes long */ +# define casdyn_ext_end 0xFF /* 46 bytes long, used with the END packet + * described by Casetta */ +# define casdyn_ext_g100 0x31 /* 40 bytes long */ + +/* Here are the common fields to all packets: */ + +struct casdyn { + /* app */ + uint8_t app[3]; + + /* type of extension - see the `casdyn_ext_*` macros above */ + uint8_t ext; +}; + +/* The format the CFX-9850G uses is 50 bytes long, so we'll name it CAS50. + * Here is the header format after the CASDYN header is read: */ + +struct _cas50 { /* types */ - uint8_t type[4]; uint8_t data[2]; /* data length */ @@ -108,9 +124,65 @@ struct cas50 { uint8_t checksum; }; -/* As you can guess, the two header formats are, in theory, incompatible. - * In practice, anyhow, they more or less are: libg1m reads the first 7 bytes - * from the header, and tries to identify a CAS50 header; if it doesn't manage, +/* The format the G100 (AlgebraFX) uses is 40 bytes long, but as CAS40 already + * exists, we'll name this format CAS100 (which is funny because the Graph 100 + * has a CAS, Computer Algebra System). + * + * Information from here is mainly decoded from Flash100 sources. + * Anyway, here is the CAS100 header after the CASDYN header is read: */ + +struct _cas100 { + /* drive? "INF", "FR0" */ + uint8_t drive[3]; + + /* driver number, in ASCII (0x30 + ) */ + uint8_t id; + + /* ExportDrive: 0x00000400, size? */ + uint32_t _size; + + /* ExportDrive: 128, type? */ + uint32_t _type; + + /* ExportDrive: 0x00020000, other size? */ + uint32_t _othersize; + + /* 0xFFs */ + uint8_t _unknown[18]; + + /* checksum */ + uint8_t checksum; +}; + +/* However, the CAS100 header has a variant, with the 'MDL' application: it + * looks like system info. The information here is obtained from Flash100's + * source code, once again. */ + +struct _cas100_info { + /* board identifier? "ZX945" */ + uint8_t board[5]; + uint8_t _delim0; /* 0xFF */ + + /* serial settings? "038400N1.00" + * excepted the ".00", this would mean 38400 bauds, no parity, 1 stop bit */ + uint8_t settings[11]; + + /* values? */ + uint32_t _val1; /* 0xF00 */ + uint32_t _val2; /* 0x400 */ + uint32_t _val3; /* 0x100 */ + + /* hex value with prefix... what? */ + uint8_t hex[4]; /* "0x07", litteraly */ + uint8_t _delim1; /* 0xFF */ + + /* checksum */ + uint8_t checksum; +}; + +/* As you can guess, CAS40 and CASDYN are, in theory, incompatible. + * In practice, anyhow, they more or less are: libg1m reads the first 4 bytes + * from the header, and tries to identify a CASDYN header; if it doesn't manage, * it falls back on CAS40. * * Here are the content formats for the two header types: */ diff --git a/include/libg1m/formatutils.h b/include/libg1m/formatutils.h index bb56ba0..62667a7 100644 --- a/include/libg1m/formatutils.h +++ b/include/libg1m/formatutils.h @@ -61,8 +61,8 @@ extern int g1m_maketype_cas(g1m_mcshead_t *head, const char *datatype); /* get caspro type data */ -extern int g1m_maketype_caspro(g1m_mcshead_t *head, - const char *maintype, const char *datatype); +extern int g1m_maketype_casapp(g1m_mcshead_t *head, + int ext, const char *app); # ifdef __cplusplus } diff --git a/include/libg1m/mcs.h b/include/libg1m/mcs.h index 4838411..ce06dce 100644 --- a/include/libg1m/mcs.h +++ b/include/libg1m/mcs.h @@ -61,6 +61,17 @@ typedef g1m_mcsfile_type_t g1m_mcstype_t; # define g1m_mcstype_variable g1m_mcstype_var # define g1m_mcstype_variables g1m_mcstype_var +/* MCS file platform */ +typedef unsigned int g1m_mcsinfo_t; +# define g1m_mcsinfo_none 0x0000 +# define g1m_mcsinfo_mcs 0x0001 +# define g1m_mcsinfo_cas40 0x0002 +# define g1m_mcsinfo_cas g1m_mcsinfo_cas40 +# define g1m_mcsinfo_cas50 0x0004 +# define g1m_mcsinfo_caspro g1m_mcsinfo_cas50 +# define g1m_mcsinfo_cas100 0x0008 +# define g1m_mcsinfo_graph100 g1m_mcsinfo_cas100 + /* Macros to check if the type uses the ID, and to interact with it */ # define g1m_mcshead_uses_id(H) (((H)->type & (\ g1m_mcstype_list | g1m_mcstype_mat | g1m_mcstype_vct | \ @@ -87,17 +98,6 @@ typedef struct g1m_mcs_cell_s { # define g1m_mcsflag_multiple 0x4000 /* is a group */ # define g1m_mcsflag_request 0x2000 /* is a request */ -/* mcs file platform -- what type of raw information is there - * e.g. `g1m_mcsinfo(&head) == g1m_mcsinfo_mcs` */ -# define g1m_mcsinfo(H) ((H)->flags & g1m_mcsmask_info) -# define g1m_mcsmask_info 0x0F00 -# define g1m_mcsinfo_none 0x0000 -# define g1m_mcsinfo_mcs 0x0100 -# define g1m_mcsinfo_cas40 0x0200 -# define g1m_mcsinfo_cas g1m_mcsinfo_cas40 -# define g1m_mcsinfo_cas50 0x0400 -# define g1m_mcsinfo_caspro g1m_mcsinfo_cas50 - /* mcs file content flags */ # define g1m_mcsflag_complex 0x0001 /* is a complex variable */ @@ -105,6 +105,7 @@ typedef struct g1m_mcs_cell_s { typedef struct g1m_mcshead_s { /* file main information */ g1m_mcstype_t type; + g1m_mcsinfo_t info; char name[13]; int id; unsigned int flags; @@ -118,8 +119,7 @@ typedef struct g1m_mcshead_s { unsigned char _group[17], _dirname[9]; /* cas-related raw data */ - unsigned char _appname[4]; - unsigned char _datatype[3]; + unsigned char _appname[4], _datatype[3]; g1m_pictureformat_t _picformat; /* the program password */ diff --git a/src/decode/cas.c b/src/decode/cas.c index e3f0575..a51f796 100644 --- a/src/decode/cas.c +++ b/src/decode/cas.c @@ -79,46 +79,49 @@ static void *lookup_cas_decode(g1m_mcstype_t type, int heads) /* Head decoding functions */ /* ************************************************************************** */ /** - * decode_caspro_head: + * decode_cas50: * Decode a CASIOLINK Protocol header. * * @arg head the head to fill. * @arg hd the header. + * @arg csum the current checksum. * @return if there was an error, or not. */ -static int decode_caspro_head(g1m_mcshead_t *head, struct cas50 *hd) +static int decode_cas50(g1m_mcshead_t *head, g1m_buffer_t *buffer, uint8_t csum) { - /* log the raw header */ - log_info("Raw CAS50 (CASPRO) header:"); - logm_info(hd, sizeof(struct cas50)); + /* read the raw header */ + DREAD(hd, _cas50) + log_info("Raw CAS50 (CASPRO) header content (app: '%.3s'):", + head->_appname); + logm_info(&hd, sizeof(struct _cas50)); /* check the checksum */ - uint8_t csum = ~g1m_checksum8(hd, sizeof(struct cas50) - 1) + 1; - if (csum != hd->checksum) + csum += g1m_checksum8(&hd, sizeof(struct _cas50) - 1); + if (~csum + 1 != hd.checksum) return (g1m_error_checksum); /* copy the basic information */ - head->size = be16toh(hd->height) - 2 /* checksum, colon */; - char *end = memchr(hd->name, 0xFF, 8); - size_t len = end ? (size_t)(end - (char*)hd->name) : 8; - memcpy(head->name, hd->name, len); head->name[len] = 0; + head->size = be16toh(hd.height) - 2 /* checksum, colon */; + char *end = memchr(hd.name, 0xFF, 8); + size_t len = end ? (size_t)(end - (char*)hd.name) : 8; + memcpy(head->name, hd.name, len); head->name[len] = 0; /* read specific data */ switch (head->type) { case g1m_mcstype_program:; /* copy password */ head->flags |= g1m_mcsflag_unfinished; - end = memchr(hd->aux, 0xFF, 8); - len = end ? (size_t)(end - (char*)hd->aux) : 8; - memcpy(head->password, hd->aux, len); head->password[len] = 0; + end = memchr(hd.aux, 0xFF, 8); + len = end ? (size_t)(end - (char*)hd.aux) : 8; + memcpy(head->password, hd.aux, len); head->password[len] = 0; log_info("Is a program of %" PRIuFAST32 " bytes", head->size); break; case g1m_mcstype_variable: case g1m_mcstype_matrix: case g1m_mcstype_list: - head->height = be16toh(hd->height) & 0xFF; - head->width = be16toh(hd->width) & 0xFF; + head->height = be16toh(hd.height) & 0xFF; + head->width = be16toh(hd.width) & 0xFF; head->count = head->height; if (head->width && head->height) head->flags |= g1m_mcsflag_unfinished; @@ -144,26 +147,35 @@ int g1m_decode_casfile_head(g1m_mcshead_t *head, g1m_buffer_t *buffer) if (!head) return (-1); memset(head, 0, sizeof(g1m_mcshead_t)); - /* read beginning of the header, try to guess a newer header */ - uint8_t buf[49]; READ(buf, 7) - struct cas50 *phd = (void*)buf; - if (!g1m_maketype_caspro(head, (char*)phd->type, (char*)phd->data)) { - READ(&buf[7], 42) - return (decode_caspro_head(head, phd)); + /* read beginning of the header, check if is an extended header */ + uint8_t buf[39]; READ(buf, 4) + uint8_t csum = g1m_checksum8(buf, 4); + struct casdyn *dhd = (void*)buf; + if (!g1m_maketype_casapp(head, dhd->ext, (char*)dhd->app)) + switch (head->info) { + case g1m_mcsinfo_cas50: return (decode_cas50(head, buffer, csum)); + //case g1m_mcsinfo_cas100: return (decode_cas100(head, buffer)); + default: + log_error("Platform 0x%04X isn't implemented yet.", head->info); + return (g1m_error_op); } - /* check the data type */ - READ(&buf[7], 32) - struct cas40 *hd = (void*)buf; + /* is a CAS40 head, read it. */ + READ(&buf[4], 35) struct cas40 *hd = (void*)buf; + csum += g1m_checksum8(&buf[4], 34); log_info("Raw CAS40 (CAS) header:"); logm_info(hd, sizeof(struct cas40)); if (g1m_maketype_cas(head, (char*)hd->data)) return (g1m_error_unrecognized); + if (~csum + 1 != hd->checksum) + return (g1m_error_checksum); /* fill the handle */ + memset(head, 0, sizeof(struct cas40)); + head->info = g1m_mcsinfo_cas40; memcpy(head->name, hd->filename, 12); head->name[12] = 0; - /* type specific */ + /* type specific things */ if (head->type == g1m_mcstype_program) { struct cas_spe_program *spe = (void*)hd->misc; head->size = be16toh(spe->length); @@ -173,7 +185,6 @@ int g1m_decode_casfile_head(g1m_mcshead_t *head, g1m_buffer_t *buffer) /* no error! */ return (0); } - /* ************************************************************************** */ /* Part decoding functions */ /* ************************************************************************** */ diff --git a/src/manage/mcs.c b/src/manage/mcs.c index fef55a5..291c0f4 100644 --- a/src/manage/mcs.c +++ b/src/manage/mcs.c @@ -57,7 +57,7 @@ int g1m_mcs_insert(g1m_handle_t *handle, g1m_mcsfile_t **tofile, continue; } else if (strcmp(file->head.name, head->name)) continue; - } else switch (g1m_mcsinfo(head)) { + } else switch (head->info) { case g1m_mcsinfo_mcs: if (strncmp((char*)head->_group, (char*)file->head._group, 16)) continue; diff --git a/src/type/cas/app.c b/src/type/cas/app.c index e912e3f..7a2ba53 100644 --- a/src/type/cas/app.c +++ b/src/type/cas/app.c @@ -22,18 +22,21 @@ /* Local types */ /* ************************************************************************** */ /* Correspondance type */ -#define flg_type 0x0001 /* if not there, the type is defined by the datatype */ -#define flg_flags 0x0002 /* the following value is the flag to OR. */ struct app_corresp { /* identification */ const char *name; - /* values */ - unsigned int flags; /* the internal flags */ - unsigned long int value; /* meaning of this depends on the flags */ - const char **datatypes; /* datatypes the app is used with */ + /* things: nothing yet */ }; +/* Extension */ +struct ext_corresp { + /* identification */ + int value; + + /* app correspondances */ + struct app_corresp *apps; +}; /* ************************************************************************** */ /* Correspondances */ /* ************************************************************************** */ @@ -41,104 +44,81 @@ struct app_corresp { * - Correspondances with a NULL data type means the data type isn't to be * read. */ -#define BASIC(S, ...) {S, 0, 0, (const char*[]){__VA_ARGS__, NULL}} -#define FLAGS(S, F) {S, flg_flags, F, NULL} -#define TYPE(S, T) {S, flg_type, T, NULL} -static struct app_corresp apps[] = { - /* advanced apps */ - FLAGS("REQ", g1m_mcsflag_request), /* request! */ - TYPE("END", g1m_mcstype_end), /* end packet */ +#define ETERM {NULL} +static struct ext_corresp apps[] = { + /* CFX-9850G headers (very first headers with this extension system?) */ + {casdyn_ext_9850, (struct app_corresp[]){ + {"TXT"}, /* editor */ + {"VAL"}, /* RUN? */ + {"IMG"}, /* screen shooter? */ - /* basic apps */ - BASIC("TXT", "PG"), /* editor */ - BASIC("VAL", "VM"), /* RUN? */ - BASIC("IMG", "PC"), /* screen shooter? */ + ETERM + }}, + + /* some sort of format only used with the END packet, I suppose? */ + {casdyn_ext_end, (struct app_corresp[]){ + {"END"}, /* end packet... used in the same period than 9850G packets? */ + + ETERM + }}, + + /* Graph 100 (Algebra FX) headers */ + {casdyn_ext_g100, (struct app_corresp[]){ + {"ADN"}, /* ? */ + {"MCS"}, /* ? */ + {"MDL"}, /* model info? */ + {"END"}, /* end, again? */ + + ETERM + }}, /* sentinel */ - {NULL, 0, 0, NULL} + {0, NULL} }; /* ************************************************************************** */ /* Main functions */ /* ************************************************************************** */ /** - * g1m_maketype_caspro: - * Get the CAS application from raw CAS50 identification data. + * g1m_maketype_casapp: + * Get the CAS application from raw CASDYN identification data. * * @arg head the head to fill. - * @arg app the app string. - * @arg datatype the data type string. + * @arg ext the extension value. + * @arg app the application name. * @return the error (if any). */ -int g1m_maketype_caspro(g1m_mcshead_t *head, - const char *app, const char *datatype) +int g1m_maketype_casapp(g1m_mcshead_t *head, + int ext, const char *app) { - /* look for the app */ + /* look for the extension */ + struct ext_corresp *e; + for (e = apps; e->apps; e++) { + if (e->value == ext) + break; + } if (!e->apps) + goto notfound; + + /* look for the application */ struct app_corresp *a; - for (a = apps; a->name; a++) { + for (a = e->apps; a->name; a++) { if (!strncmp(a->name, app, 3)) break; - } - if (!a->name) goto notfound; - - /* check if we need the datatype */ - if (a->flags & flg_type) { - memset(head, 0, sizeof(g1m_mcshead_t)); - head->type = a->value; - memcpy(head->_appname, app, 3); head->_appname[3] = 0; - } else { - int err = g1m_maketype_cas(head, datatype); - if (err) return (err); - } + } if (!a->name) goto notfound; /* copy raw information */ - head->flags = (head->flags & ~g1m_mcsmask_info) | g1m_mcsinfo_caspro; - memcpy(head->_appname, app, 3); head->_appname[3] = 0; - if (a->flags & flg_flags) - head->flags |= (unsigned int)a->value; + memset(head, 0, sizeof(g1m_mcshead_t)); + head->info = g1m_mcsinfo_graph100; /* FIXME */ + strncpy((char*)head->_appname, app, 3); + head->_appname[3] = 0; /* fill in info and return */ return (0); notfound: - log_info("Type with '%.3s' app name and '%.2s' data string was not" - "implemented or recognized.", app, datatype); + log_error("Type with 0x%02X extension and '%.3s' app name was not " + "implemented or recognized", ext, app); head->type = 0; return (1); } - -/** - * g1m_correct_cashead_app: - * Get the app string from data. - * - * @arg head the head to correct. - * @return if everything went alright. - */ - -int g1m_correct_cashead_app(g1m_mcshead_t *head) -{ - /* find the corresponding app correspondance */ - struct app_corresp *a; - for (a = apps; a->name; a++) { - if (a->flags & flg_type) { - if (a->value == head->type) - goto found; - } else if (a->flags & flg_flags) { - if ((head->flags & a->value) == a->value) - goto found; - } else /* if (!a->flags) */ { - const char **ds = a->datatypes - 1; - while (*++ds) if (!memcmp(*ds, head->_datatype, 2)) - goto found; - } - } - - /* app was not found. */ - return (g1m_error_unknown); - - /* app was found! */ -found: - memcpy(head->_appname, a->name, 4); - return (0); -} diff --git a/src/type/cas/datatype.c b/src/type/cas/datatype.c index b748ccc..c98e191 100644 --- a/src/type/cas/datatype.c +++ b/src/type/cas/datatype.c @@ -121,7 +121,7 @@ static int get_number(const char *s, int *num) /** * g1m_maketype_cas: - * Get the libg1m MCS type from raw CAS40/CAS50 data type identification data. + * Get the libg1m MCS type from the raw CAS40/CAS50 data type. * * @arg head the head to fill. * @arg datatype the data type string (2 bytes long). @@ -131,9 +131,8 @@ static int get_number(const char *s, int *num) int g1m_maketype_cas(g1m_mcshead_t *head, const char *datatype) { /* copy information */ - 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; int id = 0; @@ -188,7 +187,6 @@ int g1m_correct_casfile_head(g1m_mcshead_t *head) found: memcpy(head->_datatype, c->datatype, 2); /* TODO: things with IDs, etc */ - if (g1m_mcsinfo(head) == g1m_mcsinfo_cas50) - return (g1m_correct_cashead_app(head)); + /* TODO: add app? */ return (0); } diff --git a/src/type/mcs.c b/src/type/mcs.c index 7941d12..f38d68a 100644 --- a/src/type/mcs.c +++ b/src/type/mcs.c @@ -314,7 +314,7 @@ int g1m_maketype_mcs(g1m_mcshead_t *head, /* copy raw information */ memset(head, 0, sizeof(g1m_mcshead_t)); - head->flags |= g1m_mcsinfo_mcs; + head->info = g1m_mcsinfo_mcs; memcpy(head->name, fname, 8); head->name[8] = 0; memcpy(head->_group, gname, 16);