cake
/
libg1m
Archived
1
0
Fork 0

Corrected CAS management with new information.

This commit is contained in:
Thomas Touhey 2017-03-26 03:14:34 +02:00
parent f99423279c
commit da972cbc73
9 changed files with 205 additions and 143 deletions

View File

@ -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!

View File

@ -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 + <num>) */
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: */

View File

@ -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
}

View File

@ -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 */

View File

@ -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 */
/* ************************************************************************** */

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);