Reorganized into modules
This commit is contained in:
parent
56c9c0dc82
commit
0a98f5b682
35
Makefile
35
Makefile
|
@ -77,33 +77,37 @@ reinstall: uninstall install
|
|||
# Make the library.
|
||||
all-lib: $(CHECKCFG) lib$(NAME).so
|
||||
|
||||
# Make the objects directory.
|
||||
$(OBJDIR):
|
||||
# Make a module object directory.
|
||||
$(MODULES:%=$(OBJDIR)/%):
|
||||
$(call bcmd,mkdir,$@,$(MD) $@)
|
||||
|
||||
# Make an object out of a source file.
|
||||
$(OBJDIR)/%.o: $(SRCDIR)/%.c | $(OBJDIR)
|
||||
$(call bcmd,cc,$@,$(CC) -c -o $@ $< $(CFLAGS))
|
||||
# Make a module object out of a module source file.
|
||||
define make-moduleobj-rule
|
||||
$(OBJDIR)/$1/%.o: $(SRCDIR)/$1/%.c | $(OBJDIR)/$1
|
||||
$(call bcmd,cc,$$@,$(CC) -c -o $$@ $$< $(CFLAGS))
|
||||
endef
|
||||
$(foreach mod,$(MODULES), \
|
||||
$(eval $(call make-moduleobj-rule,$(mod))))
|
||||
|
||||
# Make the (shared) library.
|
||||
lib$(NAME).so: $(SRC:%=$(OBJDIR)/%.o)
|
||||
# Make the library (shared).
|
||||
lib$(NAME).so: $(foreach m,$(MODULES),$(SRC_$(m):%=$(OBJDIR)/$(m)/%.o))
|
||||
$(call bcmd,ld,$@,$(LD) -o $@ $^ $(LDFLAGS))
|
||||
|
||||
# Remove the objects directory.
|
||||
mostlyclean-lib:
|
||||
$(call rmsg,Removing the objects directory.)
|
||||
$(call rmsg,Removing object directory.)
|
||||
$(call rcmd,$(OBJDIR),$(RM) -r $(OBJDIR))
|
||||
mclean-lib: mostlyclean-lib
|
||||
|
||||
# Clean and remove the built library
|
||||
# Clean and remove the built library.
|
||||
clean-lib: mclean-lib
|
||||
$(call rmsg,Removing the built library.)
|
||||
$(call rmsg,Removing the library.)
|
||||
$(call rcmd,lib$(NAME).so,$(RM) lib$(NAME).so)
|
||||
|
||||
# Remake the library
|
||||
# Remake the library.
|
||||
re-lib: clean-lib all-lib
|
||||
|
||||
# Install the library and development files.
|
||||
# Install the library, development files and udev rule.
|
||||
install-lib: $(CHECKCFG) lib$(NAME).so
|
||||
$(call imsg,Installing the library.)
|
||||
$(call qcmd,$(INST) -m 755 -d "$(ILIBDIR)")
|
||||
|
@ -122,10 +126,11 @@ install-lib: $(CHECKCFG) lib$(NAME).so
|
|||
$(call qcmd,$(INST) -m 755 -d $(patsubst %,"$(IINCDIR)/%",\
|
||||
$(sort $(dir $(INCPUB))))))
|
||||
$(if $(INSTALL_DEVEL),\
|
||||
$(foreach i,$(INCPUB),$(call icmd,$(i).h,\
|
||||
$(INST) -m 644 $(INCDIR)/$(i).h "$(IINCDIR)/$(i).h"$(\n))))
|
||||
$(foreach i,$(INCPUB),\
|
||||
$(call icmd,$(i).h,\
|
||||
$(INST) -m 644 $(INCDIR)/$(i).h "$(IINCDIR)/$(i).h"$(\n))))
|
||||
|
||||
# Uninstall the library and development files. (EXPERIMENTAL)
|
||||
# Uninstall the library, development files and udev rule. (experimental)
|
||||
uninstall-lib: $(CHECKCFG)
|
||||
$(call rmsg,Uninstalling the library.)
|
||||
$(call rcmd,lib$(NAME).a)
|
||||
|
|
|
@ -80,9 +80,19 @@
|
|||
GZIP := gzip -f
|
||||
|
||||
#******************************************************************************#
|
||||
# Look for sources #
|
||||
# Look for modules and modules sources #
|
||||
#******************************************************************************#
|
||||
SRC := $(basename $(shell find $(SRCDIR) -name "*.c" -printf "%P\n"))
|
||||
# Look for modules first
|
||||
MODULES := $(notdir $(shell find $(SRCDIR) -mindepth 1 -maxdepth 1 \
|
||||
-type d | sort))
|
||||
|
||||
# Then look for their content
|
||||
define get-module-source
|
||||
SRC_$1 := $(basename $(shell find $(SRCDIR)/$1 \
|
||||
-maxdepth 1 -mindepth 1 -type f -name "*.[cs]" -printf "%P\n" | sort))
|
||||
endef
|
||||
$(foreach mod,$(MODULES), \
|
||||
$(eval $(call get-module-source,$(mod))))
|
||||
|
||||
#******************************************************************************#
|
||||
# Look for public headers (not internals.h or internals/**/*.h #
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/format/main.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:15:46 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_FORMAT_H
|
||||
# define LIBG1M_FORMAT_H
|
||||
# include <stdint.h>
|
||||
# pragma pack(1)
|
||||
|
||||
/* Welcome on this new episode of Monster Circus! Today we'll present to you
|
||||
* something that you might not be able to forget: the G1M format.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Here's a complete list of the declinations and corresponding IDs: */
|
||||
|
||||
enum g1m_type {
|
||||
/* fx-CG add-in */
|
||||
g1m_type_addin_cg = 0x2c,
|
||||
|
||||
/* mcs (main memory) save */
|
||||
g1m_type_mcs = 0x31,
|
||||
|
||||
/* e-activity (document) */
|
||||
g1m_type_eact = 0x49,
|
||||
|
||||
/* g1r equivalent (no idea why it isn't 0x31 '-') */
|
||||
g1m_type_g2r = 0x62,
|
||||
|
||||
/* g3p (fx-CGxx picture) */
|
||||
g1m_type_g3p = 0x7d,
|
||||
|
||||
/* add-in (compiled program) */
|
||||
g1m_type_addin = 0xf3,
|
||||
};
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Keep in mind that, everywhere in the header, multi-bytes integers
|
||||
* are BIG ENDIAN.
|
||||
*
|
||||
* The LSB is the Least Significant Byte: once adapted to the host endianness,
|
||||
* it can simply be obtained bitwise-AND-ing with 0xff. */
|
||||
|
||||
struct standard_header {
|
||||
/* the file identifier, should be "USBPower". */
|
||||
uint8_t main_id[8];
|
||||
|
||||
/* our filetype! */
|
||||
uint8_t type;
|
||||
|
||||
/* magic numbers, not always the same:
|
||||
* - for g1m_type_addin_cg: {0x00, 0x01, 0x00, 0x01, 0x00}
|
||||
* - otherwise: {0x00, 0x10, 0x00, 0x10, 0x00} */
|
||||
uint8_t magic[5];
|
||||
|
||||
/* first control byte: filesize LSB + 0x41 */
|
||||
uint8_t control;
|
||||
|
||||
/* said to be 0x01, but doesn't need to */
|
||||
uint8_t align_one;
|
||||
|
||||
/* total filesize */
|
||||
uint32_t filesize;
|
||||
|
||||
/* second control byte: filesize LSB + 0xb8 */
|
||||
uint8_t control2;
|
||||
|
||||
/* 4-bytes alignment */
|
||||
uint8_t align[9];
|
||||
|
||||
/* number of objects contained (useful for MCS filetype) */
|
||||
uint16_t number;
|
||||
};
|
||||
|
||||
# pragma pack()
|
||||
|
||||
/* After the Standard Header is read and the G1M type is read, we have parts,
|
||||
* each with their own subheaders and their own subtilities.
|
||||
*
|
||||
* Where do you want to start? */
|
||||
|
||||
# include <libg1m/format/addin.h>
|
||||
# include <libg1m/format/addin_cg.h>
|
||||
# include <libg1m/format/eact.h>
|
||||
# include <libg1m/format/mcs.h>
|
||||
# include <libg1m/format/picture_cg.h>
|
||||
#endif /* LIBG1M_FORMAT_H */
|
|
@ -0,0 +1,51 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/format/addin.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:15:46 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_FORMAT_ADDIN_H
|
||||
# define LIBG1M_FORMAT_ADDIN_H
|
||||
# include <stdint.h>
|
||||
# pragma pack(1)
|
||||
|
||||
/* Add-ins are compiled programs. They only have one G1M part,
|
||||
* These add-ins usually have the `g1a` extension.
|
||||
*
|
||||
* There is a declination of this format for fx-CG calculators:
|
||||
* See `libg1m/format/addin_cg.h`.
|
||||
*
|
||||
* with one subheader, which is the following: */
|
||||
|
||||
struct g1a_subheader {
|
||||
/* the internal name, of format "@APPNAME".
|
||||
* useful for add-ins calling themselves... I guess? */
|
||||
uint8_t internal_name[8];
|
||||
|
||||
/* the number of estrips (I don't know yet) */
|
||||
uint8_t estrips_count;
|
||||
|
||||
/* the add-in version, of format "01.23.4567"
|
||||
* the "01.23" will be displayed in SYSTEM > VERSION */
|
||||
uint8_t version[10];
|
||||
|
||||
/* the add-in creation type, of format "YYYY.MMDD.HHMM" */
|
||||
uint8_t creation_date[14];
|
||||
|
||||
/* 30x17 pixel menu icon bitmap */
|
||||
uint8_t icon[68];
|
||||
|
||||
/* program title */
|
||||
uint8_t title[8];
|
||||
|
||||
/* and the filesize of the part! */
|
||||
uint32_t filesize;
|
||||
};
|
||||
|
||||
/* Then the G1A file will just contain the add-in code and stop. */
|
||||
|
||||
# pragma pack()
|
||||
#endif /* LIBG1M_FORMAT_ADDIN_H */
|
|
@ -0,0 +1,102 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/format/addin_cg.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:15:46 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_FORMAT_ADDIN_CG_H
|
||||
# define LIBG1M_FORMAT_ADDIN_CG_H
|
||||
# include <stdint.h>
|
||||
# pragma pack(1)
|
||||
|
||||
/* If you don't know what an add-in is, see `libg1m/format/addin.h`.
|
||||
* This is the declination of the `G1A` format for fx-CG calculators.
|
||||
*
|
||||
* fx-CG add-ins also have one part only.
|
||||
* This is the header: */
|
||||
|
||||
struct g3a_subheader {
|
||||
/* byte checksum: 0x0000 to 0x001F (standard header)
|
||||
* and 0x0024 to EOF (everything after this checksum), minus 4. */
|
||||
uint32_t checksum;
|
||||
|
||||
/* magic sequence: {0x01, 0x01} */
|
||||
uint8_t magic[2];
|
||||
|
||||
/* undocumented */
|
||||
uint8_t undocumented[8];
|
||||
|
||||
/* control: filesize - 0x7000 - 4 */
|
||||
uint32_t control;
|
||||
|
||||
/* undocumented */
|
||||
uint8_t undocumented2[14];
|
||||
|
||||
/* title (zero terminated) */
|
||||
uint8_t title[16];
|
||||
|
||||
/* undocumented */
|
||||
uint8_t undocumented3[12];
|
||||
|
||||
/* filesize */
|
||||
uint32_t filesize;
|
||||
|
||||
/* internal name (preceded by @, filled up with zeros */
|
||||
uint8_t intname[11];
|
||||
|
||||
/* language labels */
|
||||
uint8_t language_1_label[24];
|
||||
uint8_t language_2_label[24];
|
||||
uint8_t language_3_label[24];
|
||||
uint8_t language_4_label[24];
|
||||
uint8_t language_5_label[24];
|
||||
uint8_t language_6_label[24];
|
||||
uint8_t language_7_label[24];
|
||||
uint8_t language_8_label[24];
|
||||
|
||||
/* eAct strip flag (0x00: cannot be used, 0x01: can be used) */
|
||||
uint8_t eact_strip_flag;
|
||||
|
||||
/* some null bytes */
|
||||
uint8_t zeroes[4];
|
||||
|
||||
/* version: "01.00.0000" then zeroes */
|
||||
uint8_t version[12];
|
||||
|
||||
/* timestamp: "2012.0903.1652" */
|
||||
uint8_t stamp[14];
|
||||
|
||||
/* null bytes, again */
|
||||
uint8_t zeroes2[38];
|
||||
|
||||
/* eAct strip labels */
|
||||
uint8_t eact_strip_label_1[36];
|
||||
uint8_t eact_strip_label_2[36];
|
||||
uint8_t eact_strip_label_3[36];
|
||||
uint8_t eact_strip_label_4[36];
|
||||
uint8_t eact_strip_label_5[36];
|
||||
uint8_t eact_strip_label_6[36];
|
||||
uint8_t eact_strip_label_7[36];
|
||||
uint8_t eact_strip_label_8[36];
|
||||
|
||||
/* eAct icon */
|
||||
uint8_t icon[0x300];
|
||||
|
||||
/* unused */
|
||||
uint8_t unused[0x92c];
|
||||
|
||||
/* G3A filename (seriously?!) */
|
||||
uint8_t g3a_filename[0x144];
|
||||
|
||||
/* selected and unselected icon image */
|
||||
uint8_t selected_icon_image[0x3000];
|
||||
uint8_t unselected_icon_image[0x3000];
|
||||
};
|
||||
|
||||
/* Then the G3A file will contain the add-in code and stop. */
|
||||
|
||||
# pragma pack()
|
||||
#endif
|
|
@ -0,0 +1,125 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/format/eact.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:15:46 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_FORMAT_EACT_H
|
||||
# define LIBG1M_FORMAT_EACT_H
|
||||
# include <stdint.h>
|
||||
|
||||
/* E-Activities are the format CASIO uses for in-calc documents.
|
||||
* It is the funniest subformat the the libg1m can parse.
|
||||
*
|
||||
* There is only one part for e-activities.
|
||||
* The overall header has two zones:
|
||||
* - the special header, that keeps the global info;
|
||||
* - the setup area, that keeps one part of the calculator setup.
|
||||
*
|
||||
* There are several types for E-Activies, identified by the extensions
|
||||
* CASIO give them.
|
||||
* The E-Act version in the header is 0x10100 for g1e, 0x10200 for g2e,
|
||||
* and 0x10400 for g3e. */
|
||||
|
||||
# define EACT_G1E 0x10100
|
||||
# define EACT_G2E 0x10200
|
||||
# define EACT_G3E 0x10400
|
||||
|
||||
/* The differences between these formats are the following:
|
||||
* - On g1e, the OS version is always 0x5061636B, where on g2e/g3e,
|
||||
* the OS version is the version of the OS on which the file was written.
|
||||
* - On g1e/g2e, the setup directory is 0x10-bytes long, where on g3e,
|
||||
* the setup directory is 0x2C-bytes long.
|
||||
*
|
||||
* So the main header is: */
|
||||
|
||||
struct eact_header {
|
||||
/* the filesize */
|
||||
uint32_t filesize;
|
||||
|
||||
/* the size of the setup area */
|
||||
uint32_t setup_area_size;
|
||||
|
||||
/* the E-Act version */
|
||||
uint32_t eact_version;
|
||||
|
||||
/* The OS version - only with G2E/G3E */
|
||||
uint32_t os_version;
|
||||
};
|
||||
|
||||
/* And now, the funniest part: the content.
|
||||
* So a content has this header: */
|
||||
|
||||
struct eact_contentheader {
|
||||
/* content type: @EACT, @RUNMAT, ... */
|
||||
uint8_t type[8];
|
||||
|
||||
/* align for element name */
|
||||
uint8_t align[8];
|
||||
|
||||
/* element name: "EACT1", "TEXT1", ... */
|
||||
uint8_t name[16];
|
||||
|
||||
/* align for subheader */
|
||||
uint8_t align2[4];
|
||||
};
|
||||
|
||||
/* There are several types of contents.
|
||||
* The most basic one is also called "@EACT".
|
||||
* It has this content subheader: */
|
||||
|
||||
struct eact_eactheader {
|
||||
/* line count */
|
||||
uint32_t line_count;
|
||||
};
|
||||
|
||||
/* And after, lines come. First of all, there is a line descriptor table,
|
||||
* that contain the line type and the line offset from the subheader begin.
|
||||
* A line type can have these types: */
|
||||
|
||||
enum eact_linetype {
|
||||
/* contains a calculation */
|
||||
eact_ltype_calc = 0x03,
|
||||
|
||||
/* contains a calculation result (right after the calculation line) */
|
||||
eact_ltype_calc_result = 0x04,
|
||||
|
||||
/* a subcontent, with content header and everything */
|
||||
eact_ltype_content = 0x06,
|
||||
|
||||
/* standard heading - the title surrounded by '=' */
|
||||
eact_ltype_stdheading = 0x07,
|
||||
|
||||
/* a picture - the content is the path to the picture
|
||||
* ex: '\\fls0\Pict\Pict01.g3p' (With CASIO's encoding on 16-bits) */
|
||||
eact_ltype_picture = 0x08,
|
||||
|
||||
/* Some pure text */
|
||||
eact_ltype_text = 0x81,
|
||||
|
||||
/* Text mixed up with functions -- described by SimLo, untested here */
|
||||
eact_ltype_code = 0x82
|
||||
};
|
||||
|
||||
/* And here's the line descriptor structure: */
|
||||
|
||||
struct line_descriptor {
|
||||
/* the entry type */
|
||||
uint8_t entry_type;
|
||||
|
||||
/* the entry offset */
|
||||
uint32_t entry_offset :24;
|
||||
};
|
||||
|
||||
/* And there is only one content after the main header, and it's a content
|
||||
* of EACT type. But hey, why the heck have we defined several types and stuff?
|
||||
* Well, that's the funniest thing of E-Activities:
|
||||
*
|
||||
* they are RECURSIVE. [drama alert]
|
||||
*
|
||||
* Which means in each node, there can be a content to parse. */
|
||||
|
||||
#endif /* LIBG1M_FORMAT_EACT_H */
|
|
@ -0,0 +1,181 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/format/mcs.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:15:46 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_FORMAT_MCS_H
|
||||
# define LIBG1M_FORMAT_MCS_H
|
||||
# include <stdint.h>
|
||||
# pragma pack(1)
|
||||
|
||||
/* MCS is the main filesystem on CASIO calculators. They contain settings,
|
||||
* programs, lists, pictures, captures, matrixes, and other data CASIO
|
||||
* calculators own.
|
||||
*
|
||||
* First of all, here's the BCD structure used pretty much everywhere: */
|
||||
|
||||
struct bcd {
|
||||
/* the BCD value */
|
||||
uint8_t BCDval[9];
|
||||
|
||||
/* and some 4-bytes alignment stuff */
|
||||
uint8_t align[3];
|
||||
};
|
||||
|
||||
/* So now we are not restricted to only one part. Remember the `number` field
|
||||
* of the Standard Header? Well, it's the number of parts there is.
|
||||
* And each part have a header, so let's describe it here.
|
||||
*/
|
||||
|
||||
struct mcs_subheader {
|
||||
/* main ID ("PROGRAM", zero padded) */
|
||||
uint8_t intname[16];
|
||||
|
||||
/* subitem count: haha, read what follows! */
|
||||
uint32_t subcount;
|
||||
};
|
||||
|
||||
/* Because yes, an part of an MCS G1M file has subparts!
|
||||
* Each one of them represent a single file in the MCS.
|
||||
* According to their type, they are in different directories: */
|
||||
|
||||
enum mcs_dirtype {
|
||||
mcs_dtype_program = 0x1,
|
||||
mcs_dtype_list = 0x5,
|
||||
mcs_dtype_mat = 0x6,
|
||||
mcs_dtype_picture = 0x7,
|
||||
mcs_dtype_capture = 0xa,
|
||||
mcs_dtype_spreadsheet = 0xfe,
|
||||
};
|
||||
|
||||
/* And they also have a directory name, which is less precise. Why?
|
||||
* We don't know! That's the main thing with reverse engineering.
|
||||
*
|
||||
* For basic programs, it is: */
|
||||
# define DIRNAME_PROGRAM "system"
|
||||
/* For most alpha-mem elements (list, matrixes, pictures): */
|
||||
# define DIRNAME_ALPHAMEM "main"
|
||||
/* And for captures: */
|
||||
# define DIRNAME_CAPTURE "@REV2"
|
||||
|
||||
/* Now all of this is said, each part has this header: */
|
||||
|
||||
struct mcs_partheader {
|
||||
/* the directory name (zero-padded).*/
|
||||
uint8_t dirname[8];
|
||||
|
||||
/* the file name (item name) */
|
||||
uint8_t filename[8];
|
||||
|
||||
/* the directory type. */
|
||||
uint8_t dirtype;
|
||||
|
||||
/* the size of the part data, without the part header. */
|
||||
uint32_t datalength;
|
||||
|
||||
/* some alignment */
|
||||
uint8_t align[3];
|
||||
};
|
||||
|
||||
/* Beneath that, we have specific subsubheaders for specific types.
|
||||
* Here's what it is for programs: */
|
||||
|
||||
struct mcs_programheader {
|
||||
/* the program password. not encrypted, anything */
|
||||
uint8_t password[8];
|
||||
|
||||
/* and some alignment. */
|
||||
uint8_t align[2];
|
||||
};
|
||||
|
||||
/* And, well, spreadsheets are more complicated than that.
|
||||
* There is a header, a column directory and a column definition table.
|
||||
*
|
||||
* The header mainly contains the column count: */
|
||||
|
||||
struct mcs_spreadsheetheader {
|
||||
/* magic number? */
|
||||
uint8_t magic;
|
||||
|
||||
/* column count (max: 26) */
|
||||
uint32_t column_count : 24;
|
||||
|
||||
/* alignment or magic? {0, 0, 0, 0} */
|
||||
uint8_t align[4];
|
||||
|
||||
/* number of column definitions */
|
||||
uint32_t defs_size;
|
||||
|
||||
/* alignment or magic II? {0, 0, 0, 0} */
|
||||
uint8_t align2[4];
|
||||
};
|
||||
|
||||
/* The column definition table is the main definition. It contains the row
|
||||
* directory, which is a 80-bytes long bitfield indicating if cells are empty
|
||||
* or not, and the list of non-empty cells.
|
||||
*
|
||||
* Each cell is a BCD structure.
|
||||
*
|
||||
* The column directory is just a list of 4-bytes sizes of each column,
|
||||
* counting the row directory and the cells. It's easier to naviguate to
|
||||
* a column quicker.
|
||||
*
|
||||
* Now, what about captures? Well, their header is the following: */
|
||||
|
||||
struct mcs_captureheader {
|
||||
/* the width (0x80) */
|
||||
uint16_t width;
|
||||
|
||||
/* the height (0x40) */
|
||||
uint16_t height;
|
||||
};
|
||||
|
||||
/* Then the image follows (0x400 for a 0x80*0x40px image).
|
||||
* A picture is the same that an capture, but without the header and containing
|
||||
* two 128x64 images (0x800 bytes).
|
||||
*
|
||||
* Lists! (had no idea how to introduce them)
|
||||
* After their header, they have `element_count` BCD list corresponding
|
||||
* to the numbers real part.
|
||||
* If at least one of the elements have an imaginary part, there is another
|
||||
* list right behind of `element_count` BCDs, with all of the imaginary
|
||||
* parts.
|
||||
*
|
||||
* Here's their header: */
|
||||
|
||||
struct mcs_listheader {
|
||||
/* probably the title */
|
||||
uint8_t title[8];
|
||||
|
||||
/* the elements count */
|
||||
uint16_t element_count;
|
||||
|
||||
/* undocumented (always 0?) */
|
||||
uint8_t undocumented[5];
|
||||
|
||||
/* undocumented (either 0x00 or 0x4F) */
|
||||
uint8_t undocumented2;
|
||||
};
|
||||
|
||||
/* Now, let's see matrixes! */
|
||||
|
||||
struct mcs_matheader {
|
||||
/* undocumented */
|
||||
uint8_t undocumented[8];
|
||||
|
||||
/* height */
|
||||
uint16_t height;
|
||||
|
||||
/* width */
|
||||
uint16_t width;
|
||||
|
||||
/* re-undocumented */
|
||||
uint8_t undocumented2[4];
|
||||
};
|
||||
|
||||
# pragma pack()
|
||||
#endif /* LIBG1M_FORMAT_MCS_H */
|
|
@ -0,0 +1,70 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/format/picture_cg.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:15:46 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_FORMAT_PICTURE_CG_H
|
||||
# define LIBG1M_FORMAT_PICTURE_CG_H
|
||||
# include <stdint.h>
|
||||
|
||||
/* These are pictures for fx-CG. They only have one part.
|
||||
*
|
||||
* Some color depth things: */
|
||||
|
||||
enum g3p_colorsize {
|
||||
g3p_color_4bit = 0x03,
|
||||
g3p_color_16bit = 0x10
|
||||
};
|
||||
|
||||
/* The subheader is: */
|
||||
|
||||
struct g3p_subheader {
|
||||
/* some magic sequence: "CP0100Ly755"
|
||||
* "CP" is check by a syscall */
|
||||
uint8_t magic[11];
|
||||
|
||||
/* unused */
|
||||
uint8_t unused[11];
|
||||
|
||||
/* plenty of unused space, or undocumented, for some reason */
|
||||
uint8_t undocumented[130];
|
||||
|
||||
/* magic sequence? "0100" */
|
||||
uint8_t magic2[4];
|
||||
|
||||
/* control sequence? <length of packed data + 4> */
|
||||
uint32_t control;
|
||||
|
||||
/* undocumented, again */
|
||||
uint16_t undocumented2;
|
||||
|
||||
/* dimensions */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
|
||||
/* color depth - see `g3p_colorsize` */
|
||||
uint16_t color_depth;
|
||||
|
||||
/* undocumented, the third coming */
|
||||
uint16_t undocumented3;
|
||||
|
||||
/* length of the deflated image */
|
||||
uint32_t dimage_length;
|
||||
};
|
||||
|
||||
/* And after the image: */
|
||||
|
||||
struct g3p_footer {
|
||||
/* magic sequence: "0100" */
|
||||
uint8_t magic[4];
|
||||
|
||||
/* some unused bytes? */
|
||||
uint8_t unused[0x88];
|
||||
};
|
||||
|
||||
# pragma pack()
|
||||
#endif /* LIBG1M_FORMAT_PICTURE_CG_H */
|
|
@ -10,8 +10,46 @@
|
|||
#ifndef LIBG1M_INTERNALS_H
|
||||
# define LIBG1M_INTERNALS_H
|
||||
# include <libg1m.h>
|
||||
# include <libg1m/format.h>
|
||||
# include <libg1m/internals/log.h>
|
||||
# include <libg1m/internals/format.h>
|
||||
# include <string.h>
|
||||
# include <endian.h>
|
||||
# ifndef min
|
||||
# define min(A, B) ((A) < (B) ? (A) : (B))
|
||||
# endif
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Macros and functions for parsing */
|
||||
/* ************************************************************************** */
|
||||
/* read with EOF check */
|
||||
#define READ(TO, SZ) { \
|
||||
size_t READ_size = fread(TO, 1, SZ, stream); \
|
||||
if (READ_size < (SZ)) { \
|
||||
log_info("READING failed: read %zu/%zu bytes, %zu missing.", \
|
||||
READ_size, SZ, (SZ) - READ_size); \
|
||||
return (g1m_error_eof); \
|
||||
}}
|
||||
/* read with EOF check, declare var before */
|
||||
#define DREAD(NAM, STRUCT) \
|
||||
struct STRUCT NAM; \
|
||||
READ(&NAM, sizeof(struct STRUCT))
|
||||
/* skip */
|
||||
#define SKIP(SZ) \
|
||||
if (g1m_parse_skip(stream, SZ, NULL)) return (g1m_error_eof);
|
||||
|
||||
/* skipping utility */
|
||||
int g1m_parse_skip(FILE *stream, size_t size, uint_fast32_t *checksum);
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Specific parsing functions */
|
||||
/* ************************************************************************** */
|
||||
int g1m_parse_g3p(g1m_t *handle, FILE *stream,
|
||||
struct standard_header *std);
|
||||
int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num);
|
||||
int g1m_parse_eact(g1m_t * handle, FILE *stream);
|
||||
int g1m_parse_addin(g1m_t *handle, FILE *stream);
|
||||
int g1m_parse_addin_cg(g1m_t *handle, FILE *stream,
|
||||
struct standard_header *std);
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Main handle type */
|
||||
|
|
|
@ -1,543 +0,0 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* libg1m/internals/format.h |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/10/30 17:29:42 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_INTERNALS_FORMAT_H
|
||||
# define LIBG1M_INTERNALS_FORMAT_H
|
||||
# include <stdint.h>
|
||||
# pragma pack(1)
|
||||
|
||||
/* Welcome on this new episode of Monster Circus! Today we'll present to you
|
||||
* something that you might not be able to forget: the G1M format.
|
||||
*
|
||||
* This format is used by CASIO with its calculator for storing things
|
||||
* like add-ins (compiled programs), MCS (main memory saves), and others.
|
||||
* Here's a complete list of the declinations and corresponding IDs: */
|
||||
|
||||
enum g1m_type {
|
||||
/* fx-CG add-in */
|
||||
g1m_type_addin_cg = 0x2c,
|
||||
|
||||
/* mcs (main memory) save */
|
||||
g1m_type_mcs = 0x31,
|
||||
|
||||
/* e-activity (document) */
|
||||
g1m_type_eact = 0x49,
|
||||
|
||||
/* g1r equivalent (no idea why it isn't 0x31 '-') */
|
||||
g1m_type_g2r = 0x62,
|
||||
|
||||
/* g3p (fx-CGxx picture) */
|
||||
g1m_type_g3p = 0x7d,
|
||||
|
||||
/* add-in (compiled program) */
|
||||
g1m_type_addin = 0xf3,
|
||||
};
|
||||
|
||||
/* 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.
|
||||
*
|
||||
* Keep in mind that multi-byte integers are Big Endian for all of the headers.
|
||||
* The LSB is the Least Significant Byte: once adapted to the host endianness,
|
||||
* it can simply be obtained bitwise-AND-ing with 0xff. */
|
||||
|
||||
struct standard_header {
|
||||
/* the file identifier, should be "USBPower". */
|
||||
uint8_t main_id[8];
|
||||
|
||||
/* our filetype! */
|
||||
uint8_t type;
|
||||
|
||||
/* magic numbers, not always the same:
|
||||
* - for g1m_type_addin_cg: {0x00, 0x01, 0x00, 0x01, 0x00}
|
||||
* - otherwise: {0x00, 0x10, 0x00, 0x10, 0x00} */
|
||||
uint8_t magic[5];
|
||||
|
||||
/* first control byte: filesize LSB + 0x41 */
|
||||
uint8_t control;
|
||||
|
||||
/* said to be 0x01, but doesn't need to */
|
||||
uint8_t align_one;
|
||||
|
||||
/* total filesize */
|
||||
uint32_t filesize;
|
||||
|
||||
/* second control byte: filesize LSB + 0xb8 */
|
||||
uint8_t control2;
|
||||
|
||||
/* 4-bytes alignment */
|
||||
uint8_t align[9];
|
||||
|
||||
/* number of objects contained (useful for MCS filetype) */
|
||||
uint16_t number;
|
||||
};
|
||||
|
||||
/* After the Standard Header is read and the G1M type is read, we have parts,
|
||||
* each with their own subheaders.
|
||||
* -----------------------------------------------------------------------------
|
||||
* "Normal" Add-ins (g1a)
|
||||
* -----------------------------------------------------------------------------
|
||||
* Add-ins are compiled programs. They only have one part with one subheader,
|
||||
* which is the following: */
|
||||
|
||||
struct g1a_subheader {
|
||||
/* the internal name, of format "@APPNAME".
|
||||
* useful for add-ins calling themselves... I guess? */
|
||||
uint8_t internal_name[8];
|
||||
|
||||
/* the number of estrips (I don't know yet) */
|
||||
uint8_t estrips_count;
|
||||
|
||||
/* the add-in version, of format "01.23.4567"
|
||||
* the "01.23" will be displayed in SYSTEM > VERSION */
|
||||
uint8_t version[10];
|
||||
|
||||
/* the add-in creation type, of format "YYYY.MMDD.HHMM" */
|
||||
uint8_t creation_date[14];
|
||||
|
||||
/* 30x17 pixel menu icon bitmap */
|
||||
uint8_t icon[68];
|
||||
|
||||
/* program title */
|
||||
uint8_t title[8];
|
||||
|
||||
/* and the filesize of the part! */
|
||||
uint32_t filesize;
|
||||
};
|
||||
|
||||
/* Then the G1A file will just contain the add-in code and stop.
|
||||
* -----------------------------------------------------------------------------
|
||||
* fx-CG Add-ins (g3a)
|
||||
* -----------------------------------------------------------------------------
|
||||
* fx-CG add-ins also have one part only. */
|
||||
|
||||
struct g3a_subheader {
|
||||
/* byte checksum: 0x0000 to 0x001F (standard header)
|
||||
* and 0x0024 to EOF (everything after this checksum), minus 4. */
|
||||
uint32_t checksum;
|
||||
|
||||
/* magic sequence: {0x01, 0x01} */
|
||||
uint8_t magic[2];
|
||||
|
||||
/* undocumented */
|
||||
uint8_t undocumented[8];
|
||||
|
||||
/* control: filesize - 0x7000 - 4 */
|
||||
uint32_t control;
|
||||
|
||||
/* undocumented */
|
||||
uint8_t undocumented2[14];
|
||||
|
||||
/* title (zero terminated) */
|
||||
uint8_t title[16];
|
||||
|
||||
/* undocumented */
|
||||
uint8_t undocumented3[12];
|
||||
|
||||
/* filesize */
|
||||
uint32_t filesize;
|
||||
|
||||
/* internal name (preceded by @, filled up with zeros */
|
||||
uint8_t intname[11];
|
||||
|
||||
/* language labels */
|
||||
uint8_t language_1_label[24];
|
||||
uint8_t language_2_label[24];
|
||||
uint8_t language_3_label[24];
|
||||
uint8_t language_4_label[24];
|
||||
uint8_t language_5_label[24];
|
||||
uint8_t language_6_label[24];
|
||||
uint8_t language_7_label[24];
|
||||
uint8_t language_8_label[24];
|
||||
|
||||
/* eAct strip flag (0x00: cannot be used, 0x01: can be used) */
|
||||
uint8_t eact_strip_flag;
|
||||
|
||||
/* some null bytes */
|
||||
uint8_t zeroes[4];
|
||||
|
||||
/* version: "01.00.0000" then zeroes */
|
||||
uint8_t version[12];
|
||||
|
||||
/* timestamp: "2012.0903.1652" */
|
||||
uint8_t stamp[14];
|
||||
|
||||
/* null bytes, again */
|
||||
uint8_t zeroes2[38];
|
||||
|
||||
/* eAct strip labels */
|
||||
uint8_t eact_strip_label_1[36];
|
||||
uint8_t eact_strip_label_2[36];
|
||||
uint8_t eact_strip_label_3[36];
|
||||
uint8_t eact_strip_label_4[36];
|
||||
uint8_t eact_strip_label_5[36];
|
||||
uint8_t eact_strip_label_6[36];
|
||||
uint8_t eact_strip_label_7[36];
|
||||
uint8_t eact_strip_label_8[36];
|
||||
|
||||
/* eAct icon */
|
||||
uint8_t icon[0x300];
|
||||
|
||||
/* unused */
|
||||
uint8_t unused[0x92c];
|
||||
|
||||
/* G3A filename (seriously?!) */
|
||||
uint8_t g3a_filename[0x144];
|
||||
|
||||
/* selected and unselected icon image */
|
||||
uint8_t selected_icon_image[0x3000];
|
||||
uint8_t unselected_icon_image[0x3000];
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* MCS saves (g1m, g1r)
|
||||
* -----------------------------------------------------------------------------
|
||||
* MCS is the main filesystem on CASIO calculators. They contain settings,
|
||||
* programs, lists, pictures, captures, matrixes, and other data CASIO
|
||||
* calculators own.
|
||||
*
|
||||
* First of all, here's the BCD structure used pretty much everywhere: */
|
||||
|
||||
struct bcd {
|
||||
/* the BCD value */
|
||||
uint8_t BCDval[9];
|
||||
|
||||
/* and some 4-bytes alignment stuff */
|
||||
uint8_t align[3];
|
||||
};
|
||||
|
||||
/* So now we are not restricted to only one part. Remember the `number` field
|
||||
* of the Standard Header? Well, it's the number of parts there is.
|
||||
* And each part have a header, so let's describe it here.
|
||||
*/
|
||||
|
||||
struct mcs_subheader {
|
||||
/* main ID ("PROGRAM", zero padded) */
|
||||
uint8_t intname[16];
|
||||
|
||||
/* subitem count: haha, read what follows! */
|
||||
uint32_t subcount;
|
||||
};
|
||||
|
||||
/* Because yes, an part of an MCS G1M file has subparts!
|
||||
* Each one of them represent a single file in the MCS.
|
||||
* According to their type, they are in different directories: */
|
||||
|
||||
enum mcs_dirtype {
|
||||
mcs_dtype_program = 0x1,
|
||||
mcs_dtype_list = 0x5,
|
||||
mcs_dtype_mat = 0x6,
|
||||
mcs_dtype_picture = 0x7,
|
||||
mcs_dtype_capture = 0xa,
|
||||
mcs_dtype_spreadsheet = 0xfe,
|
||||
};
|
||||
|
||||
/* And they also have a directory name, which is less precise. Why?
|
||||
* We don't know! That's the main thing with reverse engineering.
|
||||
*
|
||||
* For basic programs, it is: */
|
||||
# define DIRNAME_PROGRAM "system"
|
||||
/* For most alpha-mem elements (list, matrixes, pictures): */
|
||||
# define DIRNAME_ALPHAMEM "main"
|
||||
/* And for captures: */
|
||||
# define DIRNAME_CAPTURE "@REV2"
|
||||
|
||||
/* Now all of this is said, each part has this header: */
|
||||
|
||||
struct mcs_partheader {
|
||||
/* the directory name (zero-padded).*/
|
||||
uint8_t dirname[8];
|
||||
|
||||
/* the file name (item name) */
|
||||
uint8_t filename[8];
|
||||
|
||||
/* the directory type. */
|
||||
uint8_t dirtype;
|
||||
|
||||
/* the size of the part data, without the part header. */
|
||||
uint32_t datalength;
|
||||
|
||||
/* some alignment */
|
||||
uint8_t align[3];
|
||||
};
|
||||
|
||||
/* Beneath that, we have specific subsubheaders for specific types.
|
||||
* Here's what it is for programs: */
|
||||
|
||||
struct mcs_programheader {
|
||||
/* the program password. not encrypted, anything */
|
||||
uint8_t password[8];
|
||||
|
||||
/* and some alignment. */
|
||||
uint8_t align[2];
|
||||
};
|
||||
|
||||
/* And, well, spreadsheets are more complicated than that.
|
||||
* There is a header, a column directory and a column definition table.
|
||||
*
|
||||
* The header mainly contains the column count: */
|
||||
|
||||
struct mcs_spreadsheetheader {
|
||||
/* magic number? */
|
||||
uint8_t magic;
|
||||
|
||||
/* column count (max: 26) */
|
||||
uint32_t column_count : 24;
|
||||
|
||||
/* alignment or magic? {0, 0, 0, 0} */
|
||||
uint8_t align[4];
|
||||
|
||||
/* number of column definitions */
|
||||
uint32_t defs_size;
|
||||
|
||||
/* alignment or magic II? {0, 0, 0, 0} */
|
||||
uint8_t align2[4];
|
||||
};
|
||||
|
||||
/* The column definition table is the main definition. It contains the row
|
||||
* directory, which is a 80-bytes long bitfield indicating if cells are empty
|
||||
* or not, and the list of non-empty cells.
|
||||
*
|
||||
* Each cell is a BCD structure.
|
||||
*
|
||||
* The column directory is just a list of 4-bytes sizes of each column,
|
||||
* counting the row directory and the cells. It's easier to naviguate to
|
||||
* a column quicker.
|
||||
*
|
||||
* Now, what about captures? Well, their header is the following: */
|
||||
|
||||
struct mcs_captureheader {
|
||||
/* the width (0x80) */
|
||||
uint16_t width;
|
||||
|
||||
/* the height (0x40) */
|
||||
uint16_t height;
|
||||
};
|
||||
|
||||
/* Then the image follows (0x400 for a 0x80*0x40px image).
|
||||
* A picture is the same that an capture, but without the header and containing
|
||||
* two 128x64 images (0x800 bytes).
|
||||
*
|
||||
* Lists! (had no idea how to introduce them)
|
||||
* After their header, they have `element_count` BCD list corresponding
|
||||
* to the numbers real part.
|
||||
* If at least one of the elements have an imaginary part, there is another
|
||||
* list right behind of `element_count` BCDs, with all of the imaginary
|
||||
* parts.
|
||||
*
|
||||
* Here's their header: */
|
||||
|
||||
struct mcs_listheader {
|
||||
/* probably the title */
|
||||
uint8_t title[8];
|
||||
|
||||
/* the elements count */
|
||||
uint16_t element_count;
|
||||
|
||||
/* undocumented (always 0?) */
|
||||
uint8_t undocumented[5];
|
||||
|
||||
/* undocumented (either 0x00 or 0x4F) */
|
||||
uint8_t undocumented2;
|
||||
};
|
||||
|
||||
/* Now, let's see matrixes! */
|
||||
|
||||
struct mcs_matheader {
|
||||
/* undocumented */
|
||||
uint8_t undocumented[8];
|
||||
|
||||
/* height */
|
||||
uint16_t height;
|
||||
|
||||
/* width */
|
||||
uint16_t width;
|
||||
|
||||
/* re-undocumented */
|
||||
uint8_t undocumented2[4];
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* E-Activities (g1e, g2e, g3e)
|
||||
* -----------------------------------------------------------------------------
|
||||
* E-Activities are the format CASIO uses for in-calc documents.
|
||||
* It is the funniest subformat the the libg1m can parse.
|
||||
*
|
||||
* There is only one part for e-activities.
|
||||
* The overall header has two zones:
|
||||
* - the special header, that keeps the global info;
|
||||
* - the setup area, that keeps one part of the calculator setup.
|
||||
*
|
||||
* There are several types for E-Activies, identified by the extensions
|
||||
* CASIO give them.
|
||||
* The E-Act version in the header is 0x10100 for g1e, 0x10200 for g2e,
|
||||
* and 0x10400 for g3e. */
|
||||
|
||||
# define EACT_G1E 0x10100
|
||||
# define EACT_G2E 0x10200
|
||||
# define EACT_G3E 0x10400
|
||||
|
||||
/* The differences between these formats are the following:
|
||||
* - On g1e, the OS version is always 0x5061636B, where on g2e/g3e,
|
||||
* the OS version is the version of the OS on which the file was written.
|
||||
* - On g1e/g2e, the setup directory is 0x10-bytes long, where on g3e,
|
||||
* the setup directory is 0x2C-bytes long.
|
||||
*
|
||||
* So the main header is: */
|
||||
|
||||
struct eact_header {
|
||||
/* the filesize */
|
||||
uint32_t filesize;
|
||||
|
||||
/* the size of the setup area */
|
||||
uint32_t setup_area_size;
|
||||
|
||||
/* the E-Act version */
|
||||
uint32_t eact_version;
|
||||
|
||||
/* The OS version - only with G2E/G3E */
|
||||
uint32_t os_version;
|
||||
};
|
||||
|
||||
/* And now, the funniest part: the content.
|
||||
* So a content has this header: */
|
||||
|
||||
struct eact_contentheader {
|
||||
/* content type: @EACT, @RUNMAT, ... */
|
||||
uint8_t type[8];
|
||||
|
||||
/* align for element name */
|
||||
uint8_t align[8];
|
||||
|
||||
/* element name: "EACT1", "TEXT1", ... */
|
||||
uint8_t name[16];
|
||||
|
||||
/* align for subheader */
|
||||
uint8_t align2[4];
|
||||
};
|
||||
|
||||
/* There are several types of contents.
|
||||
* The most basic one is also called "@EACT".
|
||||
* It has this content subheader: */
|
||||
|
||||
struct eact_eactheader {
|
||||
/* line count */
|
||||
uint32_t line_count;
|
||||
};
|
||||
|
||||
/* And after, lines come. First of all, there is a line descriptor table,
|
||||
* that contain the line type and the line offset from the subheader begin.
|
||||
* A line type can have these types: */
|
||||
|
||||
enum eact_linetype {
|
||||
/* contains a calculation */
|
||||
eact_ltype_calc = 0x03,
|
||||
|
||||
/* contains a calculation result (right after the calculation line) */
|
||||
eact_ltype_calc_result = 0x04,
|
||||
|
||||
/* a subcontent, with content header and everything */
|
||||
eact_ltype_content = 0x06,
|
||||
|
||||
/* standard heading - the title surrounded by '=' */
|
||||
eact_ltype_stdheading = 0x07,
|
||||
|
||||
/* a picture - the content is the path to the picture
|
||||
* ex: '\\fls0\Pict\Pict01.g3p' (With CASIO's encoding on 16-bits) */
|
||||
eact_ltype_picture = 0x08,
|
||||
|
||||
/* Some pure text */
|
||||
eact_ltype_text = 0x81,
|
||||
|
||||
/* Text mixed up with functions -- described by SimLo, untested here */
|
||||
eact_ltype_code = 0x82
|
||||
};
|
||||
|
||||
/* And here's the line descriptor structure: */
|
||||
|
||||
struct line_descriptor {
|
||||
/* the entry type */
|
||||
uint8_t entry_type;
|
||||
|
||||
/* the entry offset */
|
||||
uint32_t entry_offset :24;
|
||||
};
|
||||
|
||||
/* And there is only one content after the main header, and it's a content
|
||||
* of EACT type. But hey, why the heck have we defined several types and stuff?
|
||||
* Well, that's the funniest thing of E-Activities:
|
||||
*
|
||||
* they are RECURSIVE. [drama alert]
|
||||
*
|
||||
* Which means in each node, there can be a content to parse. */
|
||||
/* -----------------------------------------------------------------------------
|
||||
* fx-CG Pictures (g3p)
|
||||
* -----------------------------------------------------------------------------
|
||||
* These are pictures for fx-CG. They only have one part.
|
||||
*
|
||||
* Some color depth things: */
|
||||
|
||||
enum g3p_colorsize {
|
||||
g3p_color_4bit = 0x03,
|
||||
g3p_color_16bit = 0x10
|
||||
};
|
||||
|
||||
/* The subheader is: */
|
||||
|
||||
struct g3p_subheader {
|
||||
/* some magic sequence: "CP0100Ly755"
|
||||
* "CP" is check by a syscall */
|
||||
uint8_t magic[11];
|
||||
|
||||
/* unused */
|
||||
uint8_t unused[11];
|
||||
|
||||
/* plenty of unused space, or undocumented, for some reason */
|
||||
uint8_t undocumented[130];
|
||||
|
||||
/* magic sequence? "0100" */
|
||||
uint8_t magic2[4];
|
||||
|
||||
/* control sequence? <length of packed data + 4> */
|
||||
uint32_t control;
|
||||
|
||||
/* undocumented, again */
|
||||
uint16_t undocumented2;
|
||||
|
||||
/* dimensions */
|
||||
uint16_t width;
|
||||
uint16_t height;
|
||||
|
||||
/* color depth - see `g3p_colorsize` */
|
||||
uint16_t color_depth;
|
||||
|
||||
/* undocumented, the third coming */
|
||||
uint16_t undocumented3;
|
||||
|
||||
/* length of the deflated image */
|
||||
uint32_t dimage_length;
|
||||
};
|
||||
|
||||
/* And after the image: */
|
||||
|
||||
struct g3p_footer {
|
||||
/* magic sequence: "0100" */
|
||||
uint8_t magic[4];
|
||||
|
||||
/* some unused bytes? */
|
||||
uint8_t unused[0x88];
|
||||
};
|
||||
|
||||
# pragma pack()
|
||||
|
||||
/* Credits: most of the things described here were described by Simon Lothar.
|
||||
* Thanks to Zezombye for the List and Matrixes format. */
|
||||
|
||||
#endif /* LIBG1M_INTERNALS_FORMAT_H */
|
|
@ -9,6 +9,7 @@
|
|||
/* ************************************************************************** */
|
||||
#ifndef LIBG1M_INTERNALS_LOG_H
|
||||
# define LIBG1M_INTERNALS_LOG_H
|
||||
# include <stdint.h>
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Log utility */
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
#include <libg1m/format.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#define min(A, B) ((A) < (B) ? (A) : (B))
|
752
src/parse.c
752
src/parse.c
|
@ -1,752 +0,0 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/10/30 18:24:25 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
#include <string.h>
|
||||
#include <endian.h>
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Useful macros */
|
||||
/* ************************************************************************** */
|
||||
#define min(A, B) ((A) < (B) ? (A) : (B))
|
||||
/* read with EOF check */
|
||||
#define READ(TO, SZ) { \
|
||||
size_t READ_size = fread(TO, 1, SZ, stream); \
|
||||
if (READ_size < (SZ)) { \
|
||||
log_info("READING failed: read %zu/%zu bytes, %zu missing.", \
|
||||
READ_size, SZ, (SZ) - READ_size); \
|
||||
return (g1m_error_eof); \
|
||||
}}
|
||||
/* read with EOF check, declare var before */
|
||||
#define DREAD(NAM, STRUCT) \
|
||||
struct STRUCT NAM; \
|
||||
READ(&NAM, sizeof(struct STRUCT))
|
||||
/* skip */
|
||||
#define SKIP(SZ) \
|
||||
if (skip(stream, SZ, NULL)) return (g1m_error_eof);
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Skip utilities */
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* skip:
|
||||
* Skip some bytes.
|
||||
*
|
||||
* I've made two loops to avoid conditions in each loop iteration
|
||||
* when checksum is not calculated.
|
||||
*
|
||||
* @arg stream the stream where to skip bytes.
|
||||
* @arg size the size to skip.
|
||||
* @arg checksum pointer to the checksum variable if there is one.
|
||||
*/
|
||||
|
||||
static int skip(FILE *stream, size_t size, uint_fast32_t *checksum)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
size_t size_ini = size;
|
||||
|
||||
if (checksum) {
|
||||
uint_fast32_t add = 0;
|
||||
while (1) {
|
||||
/* if no size left, exit */
|
||||
if (!size) break;
|
||||
|
||||
/* read that much */
|
||||
{
|
||||
size_t curlen = min(size, 1024);
|
||||
size_t read_len = fread(buf, 1, curlen, stream);
|
||||
size -= read_len;
|
||||
if (read_len < curlen) {
|
||||
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
|
||||
"%zu missing", size_ini - size, size_ini, size);
|
||||
return (g1m_error_eof);
|
||||
}
|
||||
|
||||
/* feed the checksum */
|
||||
for (size_t i = 0; i < curlen; i++)
|
||||
add += buf[i];
|
||||
}
|
||||
}
|
||||
*checksum += add;
|
||||
} else {
|
||||
while (1) {
|
||||
/* if no size left, exit */
|
||||
if (!size) break;
|
||||
|
||||
/* read that much */
|
||||
{
|
||||
size_t curlen = min(size, 1024);
|
||||
size_t read_len = fread(buf, 1, curlen, stream);
|
||||
size -= read_len;
|
||||
if (read_len < curlen) {
|
||||
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
|
||||
"%zu missing", size_ini - size, size_ini, size);
|
||||
return (g1m_error_eof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* fx-CG Add-ins (g3a) */
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* g1m_parse_addin_cg:
|
||||
* Parse fx-CG add-in (after Standard Header).
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg std the standard header.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_addin_cg(g1m_t *handle, FILE *stream,
|
||||
struct standard_header *std)
|
||||
{
|
||||
/* get the subheader */
|
||||
DREAD(hd, g3a_subheader)
|
||||
|
||||
/* correct subheader endianness */
|
||||
hd.checksum = be32toh(hd.checksum);
|
||||
hd.control = be32toh(hd.control);
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
|
||||
/* check the control */
|
||||
if (hd.control != hd.filesize - 0x7000 - 4) {
|
||||
log_info("control value is incorrect");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* skip while making checksum */
|
||||
uint_fast32_t checksum = 0;
|
||||
for (size_t i = 0; i < sizeof(struct standard_header); i++)
|
||||
checksum += ((uint8_t*)std)[i];
|
||||
for (size_t i = 4; i < sizeof(struct g3a_subheader); i++)
|
||||
checksum += ((uint8_t*)&hd)[i];
|
||||
if (skip(stream, hd.filesize - sizeof(struct g3a_subheader), &checksum))
|
||||
return (g1m_error_eof);
|
||||
checksum -= 4;
|
||||
|
||||
/* check the checksum */
|
||||
if (checksum != hd.checksum) {
|
||||
log_info("Invalid checksum.");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* log */
|
||||
log_info("title is '%s'", hd.title);
|
||||
log_info("internal name is '%s'", hd.intname);
|
||||
log_info("version is '%s'", hd.version);
|
||||
log_info("timestamp is '%s'", hd.stamp);
|
||||
log_info("G3A filename is '%s' (yea srsly)", hd.g3a_filename);
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Add-ins (g1a) */
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* g1m_parse_addin:
|
||||
* Parse "normal" add-in (after Standard Header).
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_addin(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* get the subheader */
|
||||
DREAD(hd, g1a_subheader)
|
||||
|
||||
/* correct subheader endianess */
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
|
||||
/* log info about the subheader */
|
||||
log_info("internal name is '%.8s'", hd.internal_name);
|
||||
log_info("estrips count is %hhu", hd.estrips_count);
|
||||
log_info("version is %.10s", hd.version);
|
||||
log_info("creation date is %.14s", hd.creation_date);
|
||||
|
||||
/* skip size */
|
||||
SKIP(hd.filesize - sizeof(struct standard_header)
|
||||
- sizeof(struct g1a_subheader))
|
||||
|
||||
/* no errors */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* MCS saves (g1m, g1r) */
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* g1m_parse_mcs_program:
|
||||
* Parse a program.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg length the data length.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_program(g1m_t *handle, FILE *stream,
|
||||
uint_fast32_t length)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_programheader)
|
||||
|
||||
/* print header data */
|
||||
log_info("Program password is '%.8s'.", hd.password);
|
||||
|
||||
/* seek for what's left in the file */
|
||||
SKIP(length - sizeof(struct mcs_programheader))
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_spreadsheet:
|
||||
* Parse a spreadsheet.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_spreadsheet(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_spreadsheetheader)
|
||||
|
||||
/* correct endianess */
|
||||
uint_fast32_t colcount = be32toh(hd.column_count << 8);
|
||||
hd.defs_size = be32toh(hd.defs_size);
|
||||
|
||||
/* log some info */
|
||||
log_info("%ld columns to parse!", colcount);
|
||||
|
||||
/* get the column directory */
|
||||
uint32_t column_directory[colcount];
|
||||
READ(&column_directory, sizeof(uint32_t) * colcount)
|
||||
/* - we should use `be32toh` here, but it isn't necessary for bool check. */
|
||||
|
||||
/* browse columns */
|
||||
for (uint_fast32_t c = 0; c < colcount; c++) {
|
||||
/* check if column is empty */
|
||||
if (!column_directory[c])
|
||||
continue;
|
||||
|
||||
/* get the row directory */
|
||||
uint8_t row_directory[0x80];
|
||||
READ(&row_directory, (size_t)0x80)
|
||||
|
||||
/* initialize loop values */
|
||||
uint8_t *rd = row_directory;
|
||||
int bit = 1 << 7;
|
||||
|
||||
/* explore each cell */
|
||||
for (uint_fast32_t i = 0; i < 1000; i++) {
|
||||
/* check if used */
|
||||
if (*rd & bit) {
|
||||
/* get cell */
|
||||
DREAD(cell, bcd)
|
||||
|
||||
/* log it */
|
||||
log_info("[%ld, %ld] not empty.", c, i);
|
||||
}
|
||||
|
||||
/* iterate bit and rd */
|
||||
rd += (bit & 1);
|
||||
bit = (bit >> 1) | ((bit & 1) << 7);
|
||||
}
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_list:
|
||||
* Parse an List.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg size the
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_list(g1m_t *handle, FILE *stream, uint_fast32_t size)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_listheader)
|
||||
|
||||
/* correct endianess */
|
||||
uint16_t elcount = hd.element_count;
|
||||
elcount = be16toh(elcount);
|
||||
|
||||
/* prepare browsing */
|
||||
size -= sizeof(struct mcs_listheader);
|
||||
size_t elsize = sizeof(struct bcd) * elcount;
|
||||
|
||||
/* make tabs */
|
||||
struct bcd real[elcount];
|
||||
struct bcd imgn[elcount];
|
||||
bzero(real, elsize);
|
||||
bzero(imgn, elsize);
|
||||
|
||||
/* browse */
|
||||
log_info("Browsing %d list elements", elcount);
|
||||
READ(&real, elsize)
|
||||
if (size > elsize) /* FIXME: HACK */
|
||||
READ(&imgn, elsize)
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_mat:
|
||||
* Parse a Matrix.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_mat(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_matheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.width = be16toh(hd.width);
|
||||
hd.height = be16toh(hd.height);
|
||||
|
||||
/* log info */
|
||||
log_info("Matrix is %d/%d", hd.width, hd.height);
|
||||
|
||||
/* parse cells */
|
||||
size_t num = hd.width * hd.height;
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
/* read the cell */
|
||||
DREAD(cell, bcd)
|
||||
|
||||
/* get x using `i % height` and y using `i / height` */
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_capture:
|
||||
* Parse a capture.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_capture(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_captureheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.width = be16toh(hd.width);
|
||||
hd.height = be16toh(hd.height);
|
||||
|
||||
/* print info */
|
||||
log_info("capture is %ux%u sized", hd.width, hd.height);
|
||||
|
||||
/* skip */
|
||||
SKIP(hd.height * (hd.width / 8 + !!(hd.width % 8)))
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs:
|
||||
* We have passed the Standard Header and it's a g... MCS! Let's parse it!
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg num number of sizes.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
|
||||
{
|
||||
log_info("%ld parts to browse", num);
|
||||
|
||||
/* read all of the parts */
|
||||
for (uint_fast16_t i = 0; i < num; i++) {
|
||||
/* get the subheader */
|
||||
DREAD(hd, mcs_subheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.subcount = be32toh(hd.subcount);
|
||||
|
||||
/* log info about part */
|
||||
log_info("[%ld] Internal name is '%.16s'", i, hd.intname);
|
||||
log_info("[%ld] %d subparts to browse", i, hd.subcount);
|
||||
|
||||
/* foreach subpart */
|
||||
uint_fast32_t subcount = hd.subcount;
|
||||
for (uint_fast32_t j = 0; j < subcount; j++) {
|
||||
/* get the part header */
|
||||
DREAD(phd, mcs_partheader)
|
||||
|
||||
/* correct endianess */
|
||||
phd.datalength = be32toh(phd.datalength);
|
||||
|
||||
/* log info about the subpart */
|
||||
log_info("[%ld,%ld] directory name is '%.8s'", i, j, phd.dirname);
|
||||
log_info("[%ld,%ld] filename is '%.8s'", i, j, phd.filename);
|
||||
log_info("[%ld,%ld] directory type is '%s' (0x%02d)",
|
||||
i, j, g1m_get_dtype_string(phd.dirtype), phd.dirtype);
|
||||
log_info("[%ld,%ld] data length is %u", i, j, phd.datalength);
|
||||
|
||||
/* and read that much data */
|
||||
int err = 0;
|
||||
switch (phd.dirtype) {
|
||||
case mcs_dtype_program:
|
||||
err = g1m_parse_mcs_program(handle, stream, phd.datalength);
|
||||
break;
|
||||
case mcs_dtype_spreadsheet:
|
||||
err = g1m_parse_mcs_spreadsheet(handle, stream);
|
||||
break;
|
||||
case mcs_dtype_capture:
|
||||
err = g1m_parse_mcs_capture(handle, stream);
|
||||
break;
|
||||
case mcs_dtype_list:
|
||||
err = g1m_parse_mcs_list(handle, stream, phd.datalength);
|
||||
break;
|
||||
case mcs_dtype_mat:
|
||||
err = g1m_parse_mcs_mat(handle, stream);
|
||||
break;
|
||||
default:
|
||||
SKIP(phd.datalength)
|
||||
}
|
||||
|
||||
/* if there is an error, return it */
|
||||
if (err) return (err);
|
||||
}
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Prizm pictures */
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* g1m_parse_g3p:
|
||||
* Parse a G3P file.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg std the standard header.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_g3p(g1m_t *handle, FILE *stream,
|
||||
struct standard_header *std)
|
||||
{
|
||||
/* get the header and footer */
|
||||
DREAD(hd, g3p_subheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.control = be32toh(hd.control);
|
||||
hd.width = be16toh(hd.width);
|
||||
hd.height = be16toh(hd.height);
|
||||
hd.color_depth = be16toh(hd.color_depth);
|
||||
hd.dimage_length = be32toh(hd.dimage_length);
|
||||
|
||||
/* check control */
|
||||
if (memcmp(hd.magic, "CP", 2)) {
|
||||
log_info("could not read CP.");
|
||||
return (g1m_error_magic);
|
||||
} else if (memcmp(hd.magic, "0100", 4)) {
|
||||
log_info("could not read header 0100");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* TODO: what about "length of packed data + 4" control? */
|
||||
/* TODO: get footer, check control 0100 */
|
||||
|
||||
/* check if image is encrypted */
|
||||
int is_encrypted = (((std->filesize & 0xff) +
|
||||
((std->filesize & 0xff00) >> 8) + g1m_type_g3p) & 0xff) ^ 0x1c;
|
||||
|
||||
/* log info */
|
||||
log_info("Width: %dpx, height: %dpx", hd.width, hd.height);
|
||||
log_info("Pixel depth: %s",
|
||||
(hd.color_depth == g3p_color_4bit) ? "4-bit" : "16-bit");
|
||||
log_info("Deflated image length: %uo", hd.dimage_length);
|
||||
log_info("Is encrypted: %s", is_encrypted ? "yes" : "no");
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* E-Activities */
|
||||
/* ************************************************************************** */
|
||||
/* prototype for content parsing */
|
||||
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
|
||||
int depth);
|
||||
|
||||
/**
|
||||
* g1m_parse_eact_eact:
|
||||
* Parse an EACT in an EACT, from the subheader.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg buf the buffer.
|
||||
* @arg bufsize the buffer size.
|
||||
* @arg depth the current depth.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_eact_eact(g1m_t *handle, uint8_t *buf, size_t bufsize,
|
||||
int depth)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* get e-act subheader */
|
||||
if (bufsize < sizeof(struct eact_eactheader)) return (g1m_error_eof);
|
||||
struct eact_eactheader ehd;
|
||||
memcpy(&ehd, buf, sizeof(struct eact_eactheader));
|
||||
|
||||
/* correct endianess */
|
||||
ehd.line_count = be32toh(ehd.line_count);
|
||||
|
||||
/* get the line descriptors
|
||||
* there is actually a "bonus" line descriptor at the end, but the
|
||||
* fact that we're using offsets lets us unaware of it. */
|
||||
if (bufsize < sizeof(struct eact_eactheader)
|
||||
+ (ehd.line_count + 1) * sizeof(struct line_descriptor))
|
||||
return (g1m_error_eof);
|
||||
struct line_descriptor *lds = (void*)&buf[sizeof(struct eact_eactheader)];
|
||||
|
||||
/* browse the lines */
|
||||
log_info("[>%d] %d lines to browse", depth, ehd.line_count);
|
||||
if (ehd.line_count)
|
||||
lds[0].entry_offset = be32toh(lds[0].entry_offset << 8);
|
||||
for (uint_fast32_t i = 0; i < ehd.line_count; i++) {
|
||||
/* get line size
|
||||
* for this, we'll calculate the distance to the next line offset.
|
||||
* if there is no next line (last line), then the data left will be
|
||||
* attributed to the line. */
|
||||
size_t linesize;
|
||||
if (i == ehd.line_count - 1)
|
||||
linesize = bufsize - lds[i].entry_offset;
|
||||
else {
|
||||
/* correct endianess of the next element */
|
||||
lds[i + 1].entry_offset = be32toh(lds[i + 1].entry_offset << 8);
|
||||
/* then calculate */
|
||||
linesize = lds[i + 1].entry_offset - lds[i].entry_offset;
|
||||
}
|
||||
|
||||
/* store current info */
|
||||
uint_fast8_t entry_type = lds[i].entry_type;
|
||||
uint_fast32_t entry_offset = lds[i].entry_offset;
|
||||
|
||||
/* check if buffer is big enough */
|
||||
if (entry_offset + linesize > bufsize)
|
||||
return (g1m_error_eof);
|
||||
|
||||
/* log data */
|
||||
log_info("[>%d][%ld] Is '%s' (0x%02x)", depth, i + 1,
|
||||
g1m_get_eact_ltype_string(entry_type), entry_type);
|
||||
#if LOGLEVEL <= ll_info
|
||||
if (entry_type != eact_ltype_content) {
|
||||
log_info("[>%d][%ld] Buffer (0x%zxo) is:", depth, i + 1, linesize);
|
||||
logm_info(&buf[entry_offset], linesize);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* go recursive */
|
||||
if (lds[i].entry_type == eact_ltype_content
|
||||
&& (err = g1m_parse_eact_cont(handle, &buf[entry_offset], linesize,
|
||||
depth + 1)))
|
||||
return (err);
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_eact_cont:
|
||||
* Parse an E-Activity content.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg buf the buffer.
|
||||
* @arg bufsize the buffer length.
|
||||
* @arg depth the current depth.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
|
||||
int depth)
|
||||
{
|
||||
(void)depth;
|
||||
|
||||
/* read content header */
|
||||
size_t could_read = min(bufsize, sizeof(struct eact_contentheader));
|
||||
if (could_read < 9) return (g1m_error_eof);
|
||||
struct eact_contentheader hd = {};
|
||||
memcpy(&hd, buf, could_read);
|
||||
|
||||
/* log info */
|
||||
log_info("[>%d] Type is '%.8s'", depth, hd.type);
|
||||
log_info("[>%d] Name is '%.16s'", depth, hd.name);
|
||||
|
||||
/* prepare for next */
|
||||
if (bufsize == could_read)
|
||||
return (0);
|
||||
buf += could_read;
|
||||
bufsize -= could_read;
|
||||
|
||||
/* if type eact, parse eact */
|
||||
if (!strcmp((char*)hd.type, "@EACT"))
|
||||
return (g1m_parse_eact_eact(handle, buf, bufsize, depth));
|
||||
|
||||
/* otherwise, no error. */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_eact:
|
||||
* Parse an EACT.
|
||||
*
|
||||
* Thanks to Julese50/CoiledSpring for his help on e-acts parsing.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_eact(g1m_t * handle, FILE *stream)
|
||||
{
|
||||
/* parse the header */
|
||||
DREAD(hd, eact_header)
|
||||
|
||||
/* correct endianess */
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
hd.setup_area_size = be32toh(hd.setup_area_size);
|
||||
hd.eact_version = be32toh(hd.eact_version);
|
||||
hd.os_version = be32toh(hd.os_version);
|
||||
|
||||
/* find out the size of the setup area */
|
||||
log_info("E-Activity version is '%s'.",
|
||||
hd.eact_version == EACT_G1E ? "g1e" :
|
||||
hd.eact_version == EACT_G2E ? "g2e" : "g3e");
|
||||
log_info("Setup area size is 0x%x bytes long.",
|
||||
hd.setup_area_size);
|
||||
|
||||
/* skip the setup area */
|
||||
SKIP(hd.setup_area_size)
|
||||
|
||||
/* get content buffer */
|
||||
size_t bufsize = hd.filesize - sizeof(struct standard_header)
|
||||
- sizeof(struct eact_header) - hd.setup_area_size;
|
||||
uint8_t buf[bufsize];
|
||||
READ(&buf, bufsize)
|
||||
|
||||
/* parse content */
|
||||
return (g1m_parse_eact_cont(handle, buf, bufsize, 0));
|
||||
}
|
||||
|
||||
/* ************************************************************************** */
|
||||
/* Main parsing */
|
||||
/* ************************************************************************** */
|
||||
/**
|
||||
* g1m_parse:
|
||||
* Parse a G1M file.
|
||||
*
|
||||
* Read the standard header, correct endianness, check magic numbers,
|
||||
* then read subparts according to the G1M type.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* get the standard header */
|
||||
DREAD(hd, standard_header)
|
||||
|
||||
/* reverse */
|
||||
uint8_t *u = (uint8_t*)&hd;
|
||||
for (unsigned int i = 0; i < sizeof(struct standard_header); i++)
|
||||
u[i] = ~u[i];
|
||||
|
||||
/* print header */
|
||||
log_info("Raw standard header is:");
|
||||
logm_info(&hd, sizeof(struct standard_header));
|
||||
|
||||
/* correct standard header endianess */
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
hd.number = be16toh(hd.number);
|
||||
|
||||
/* what is the expected magic sequence? */
|
||||
const uint8_t magic_sequence_cg[] = {0x00, 0x01, 0x00, 0x01, 0x00};
|
||||
const uint8_t magic_sequence_0[] = {0x00, 0x10, 0x00, 0x10, 0x00};
|
||||
const uint8_t *magic_sequence =
|
||||
(hd.type == g1m_type_addin_cg) ? magic_sequence_cg : magic_sequence_0;
|
||||
|
||||
/* check standard header magics */
|
||||
if (memcmp(hd.magic, magic_sequence, 5)) {
|
||||
log_info("Magic sequence isn't right.");
|
||||
return (g1m_error_magic);
|
||||
} else if (hd.control != ((hd.filesize + 0x41) & 0xff)) {
|
||||
log_info("First control byte isn't right.");
|
||||
return (g1m_error_magic);
|
||||
} else if (hd.control2 != ((hd.filesize + 0xb8) & 0xff)) {
|
||||
log_info("Second control byte isn't right.");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* log some data */
|
||||
log_info("Standard Header Main ID is '%.8s'", hd.main_id);
|
||||
log_info("Standard Header Type is '%s' (0x%02x)",
|
||||
g1m_get_type_string(hd.type), hd.type);
|
||||
log_info("Standard Header filesize is %uo", hd.filesize);
|
||||
log_info("Standard Header num is %d.", hd.number);
|
||||
|
||||
/* subparse. */
|
||||
switch (hd.type) {
|
||||
case g1m_type_addin:
|
||||
return (g1m_parse_addin(handle, stream));
|
||||
case g1m_type_addin_cg:
|
||||
return (g1m_parse_addin_cg(handle, stream, &hd));
|
||||
case g1m_type_g2r: case g1m_type_mcs:
|
||||
return (g1m_parse_mcs(handle, stream, hd.number));
|
||||
case g1m_type_eact:
|
||||
return (g1m_parse_eact(handle, stream));
|
||||
case g1m_type_g3p:
|
||||
return (g1m_parse_g3p(handle, stream, &hd));
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/addin.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:47:49 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/**
|
||||
* g1m_parse_addin:
|
||||
* Parse "normal" add-in (after Standard Header).
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse_addin(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* get the subheader */
|
||||
DREAD(hd, g1a_subheader)
|
||||
|
||||
/* correct subheader endianess */
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
|
||||
/* log info about the subheader */
|
||||
log_info("internal name is '%.8s'", hd.internal_name);
|
||||
log_info("estrips count is %hhu", hd.estrips_count);
|
||||
log_info("version is %.10s", hd.version);
|
||||
log_info("creation date is %.14s", hd.creation_date);
|
||||
|
||||
/* skip size */
|
||||
SKIP(hd.filesize - sizeof(struct standard_header)
|
||||
- sizeof(struct g1a_subheader))
|
||||
|
||||
/* no errors */
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/addin_cg.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:47:49 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/**
|
||||
* g1m_parse_addin_cg:
|
||||
* Parse fx-CG add-in (after Standard Header).
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg std the standard header.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse_addin_cg(g1m_t *handle, FILE *stream,
|
||||
struct standard_header *std)
|
||||
{
|
||||
/* get the subheader */
|
||||
DREAD(hd, g3a_subheader)
|
||||
|
||||
/* correct subheader endianness */
|
||||
hd.checksum = be32toh(hd.checksum);
|
||||
hd.control = be32toh(hd.control);
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
|
||||
/* check the control */
|
||||
if (hd.control != hd.filesize - 0x7000 - 4) {
|
||||
log_info("control value is incorrect");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* skip while making checksum */
|
||||
uint_fast32_t checksum = 0;
|
||||
for (size_t i = 0; i < sizeof(struct standard_header); i++)
|
||||
checksum += ((uint8_t*)std)[i];
|
||||
for (size_t i = 4; i < sizeof(struct g3a_subheader); i++)
|
||||
checksum += ((uint8_t*)&hd)[i];
|
||||
if (g1m_parse_skip(stream, hd.filesize - sizeof(struct g3a_subheader),
|
||||
&checksum))
|
||||
return (g1m_error_eof);
|
||||
checksum -= 4;
|
||||
|
||||
/* check the checksum */
|
||||
if (checksum != hd.checksum) {
|
||||
log_info("Invalid checksum.");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* log */
|
||||
log_info("title is '%s'", hd.title);
|
||||
log_info("internal name is '%s'", hd.intname);
|
||||
log_info("version is '%s'", hd.version);
|
||||
log_info("timestamp is '%s'", hd.stamp);
|
||||
log_info("G3A filename is '%s' (yea srsly)", hd.g3a_filename);
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/eact.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:47:49 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/* prototype for content parsing */
|
||||
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
|
||||
int depth);
|
||||
|
||||
/**
|
||||
* g1m_parse_eact_eact:
|
||||
* Parse an EACT in an EACT, from the subheader.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg buf the buffer.
|
||||
* @arg bufsize the buffer size.
|
||||
* @arg depth the current depth.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_eact_eact(g1m_t *handle, uint8_t *buf, size_t bufsize,
|
||||
int depth)
|
||||
{
|
||||
int err;
|
||||
|
||||
/* get e-act subheader */
|
||||
if (bufsize < sizeof(struct eact_eactheader)) return (g1m_error_eof);
|
||||
struct eact_eactheader ehd;
|
||||
memcpy(&ehd, buf, sizeof(struct eact_eactheader));
|
||||
|
||||
/* correct endianess */
|
||||
ehd.line_count = be32toh(ehd.line_count);
|
||||
|
||||
/* get the line descriptors
|
||||
* there is actually a "bonus" line descriptor at the end, but the
|
||||
* fact that we're using offsets lets us unaware of it. */
|
||||
if (bufsize < sizeof(struct eact_eactheader)
|
||||
+ (ehd.line_count + 1) * sizeof(struct line_descriptor))
|
||||
return (g1m_error_eof);
|
||||
struct line_descriptor *lds = (void*)&buf[sizeof(struct eact_eactheader)];
|
||||
|
||||
/* browse the lines */
|
||||
log_info("[>%d] %d lines to browse", depth, ehd.line_count);
|
||||
if (ehd.line_count)
|
||||
lds[0].entry_offset = be32toh(lds[0].entry_offset << 8);
|
||||
for (uint_fast32_t i = 0; i < ehd.line_count; i++) {
|
||||
/* get line size
|
||||
* for this, we'll calculate the distance to the next line offset.
|
||||
* if there is no next line (last line), then the data left will be
|
||||
* attributed to the line. */
|
||||
size_t linesize;
|
||||
if (i == ehd.line_count - 1)
|
||||
linesize = bufsize - lds[i].entry_offset;
|
||||
else {
|
||||
/* correct endianess of the next element */
|
||||
lds[i + 1].entry_offset = be32toh(lds[i + 1].entry_offset << 8);
|
||||
/* then calculate */
|
||||
linesize = lds[i + 1].entry_offset - lds[i].entry_offset;
|
||||
}
|
||||
|
||||
/* store current info */
|
||||
uint_fast8_t entry_type = lds[i].entry_type;
|
||||
uint_fast32_t entry_offset = lds[i].entry_offset;
|
||||
|
||||
/* check if buffer is big enough */
|
||||
if (entry_offset + linesize > bufsize)
|
||||
return (g1m_error_eof);
|
||||
|
||||
/* log data */
|
||||
log_info("[>%d][%ld] Is '%s' (0x%02x)", depth, i + 1,
|
||||
g1m_get_eact_ltype_string(entry_type), entry_type);
|
||||
#if LOGLEVEL <= ll_info
|
||||
if (entry_type != eact_ltype_content) {
|
||||
log_info("[>%d][%ld] Buffer (0x%zxo) is:", depth, i + 1, linesize);
|
||||
logm_info(&buf[entry_offset], linesize);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* go recursive */
|
||||
if (lds[i].entry_type == eact_ltype_content
|
||||
&& (err = g1m_parse_eact_cont(handle, &buf[entry_offset], linesize,
|
||||
depth + 1)))
|
||||
return (err);
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_eact_cont:
|
||||
* Parse an E-Activity content.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg buf the buffer.
|
||||
* @arg bufsize the buffer length.
|
||||
* @arg depth the current depth.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_eact_cont(g1m_t *handle, uint8_t *buf, size_t bufsize,
|
||||
int depth)
|
||||
{
|
||||
(void)depth;
|
||||
|
||||
/* read content header */
|
||||
size_t could_read = min(bufsize, sizeof(struct eact_contentheader));
|
||||
if (could_read < 9) return (g1m_error_eof);
|
||||
struct eact_contentheader hd = {};
|
||||
memcpy(&hd, buf, could_read);
|
||||
|
||||
/* log info */
|
||||
log_info("[>%d] Type is '%.8s'", depth, hd.type);
|
||||
log_info("[>%d] Name is '%.16s'", depth, hd.name);
|
||||
|
||||
/* prepare for next */
|
||||
if (bufsize == could_read)
|
||||
return (0);
|
||||
buf += could_read;
|
||||
bufsize -= could_read;
|
||||
|
||||
/* if type eact, parse eact */
|
||||
if (!strcmp((char*)hd.type, "@EACT"))
|
||||
return (g1m_parse_eact_eact(handle, buf, bufsize, depth));
|
||||
|
||||
/* otherwise, no error. */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_eact:
|
||||
* Parse an EACT.
|
||||
*
|
||||
* Thanks to Julese50/CoiledSpring for his help on e-acts parsing.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse_eact(g1m_t * handle, FILE *stream)
|
||||
{
|
||||
/* parse the header */
|
||||
DREAD(hd, eact_header)
|
||||
|
||||
/* correct endianess */
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
hd.setup_area_size = be32toh(hd.setup_area_size);
|
||||
hd.eact_version = be32toh(hd.eact_version);
|
||||
hd.os_version = be32toh(hd.os_version);
|
||||
|
||||
/* find out the size of the setup area */
|
||||
log_info("E-Activity version is '%s'.",
|
||||
hd.eact_version == EACT_G1E ? "g1e" :
|
||||
hd.eact_version == EACT_G2E ? "g2e" : "g3e");
|
||||
log_info("Setup area size is 0x%x bytes long.",
|
||||
hd.setup_area_size);
|
||||
|
||||
/* skip the setup area */
|
||||
SKIP(hd.setup_area_size)
|
||||
|
||||
/* get content buffer */
|
||||
size_t bufsize = hd.filesize - sizeof(struct standard_header)
|
||||
- sizeof(struct eact_header) - hd.setup_area_size;
|
||||
uint8_t buf[bufsize];
|
||||
READ(&buf, bufsize)
|
||||
|
||||
/* parse content */
|
||||
return (g1m_parse_eact_cont(handle, buf, bufsize, 0));
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/main.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:47:49 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/**
|
||||
* g1m_parse:
|
||||
* Parse a G1M file.
|
||||
*
|
||||
* Read the standard header, correct endianness, check magic numbers,
|
||||
* then read subparts according to the G1M type.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* get the standard header */
|
||||
DREAD(hd, standard_header)
|
||||
|
||||
/* reverse */
|
||||
uint8_t *u = (uint8_t*)&hd;
|
||||
for (unsigned int i = 0; i < sizeof(struct standard_header); i++)
|
||||
u[i] = ~u[i];
|
||||
|
||||
/* print header */
|
||||
log_info("Raw standard header is:");
|
||||
logm_info(&hd, sizeof(struct standard_header));
|
||||
|
||||
/* correct standard header endianess */
|
||||
hd.filesize = be32toh(hd.filesize);
|
||||
hd.number = be16toh(hd.number);
|
||||
|
||||
/* what is the expected magic sequence? */
|
||||
const uint8_t magic_sequence_cg[] = {0x00, 0x01, 0x00, 0x01, 0x00};
|
||||
const uint8_t magic_sequence_0[] = {0x00, 0x10, 0x00, 0x10, 0x00};
|
||||
const uint8_t *magic_sequence =
|
||||
(hd.type == g1m_type_addin_cg) ? magic_sequence_cg : magic_sequence_0;
|
||||
|
||||
/* check standard header magics */
|
||||
if (memcmp(hd.magic, magic_sequence, 5)) {
|
||||
log_info("Magic sequence isn't right.");
|
||||
return (g1m_error_magic);
|
||||
} else if (hd.control != ((hd.filesize + 0x41) & 0xff)) {
|
||||
log_info("First control byte isn't right.");
|
||||
return (g1m_error_magic);
|
||||
} else if (hd.control2 != ((hd.filesize + 0xb8) & 0xff)) {
|
||||
log_info("Second control byte isn't right.");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* log some data */
|
||||
log_info("Standard Header Main ID is '%.8s'", hd.main_id);
|
||||
log_info("Standard Header Type is '%s' (0x%02x)",
|
||||
g1m_get_type_string(hd.type), hd.type);
|
||||
log_info("Standard Header filesize is %uo", hd.filesize);
|
||||
log_info("Standard Header num is %d.", hd.number);
|
||||
|
||||
/* subparse. */
|
||||
switch (hd.type) {
|
||||
case g1m_type_addin:
|
||||
return (g1m_parse_addin(handle, stream));
|
||||
case g1m_type_addin_cg:
|
||||
return (g1m_parse_addin_cg(handle, stream, &hd));
|
||||
case g1m_type_g2r: case g1m_type_mcs:
|
||||
return (g1m_parse_mcs(handle, stream, hd.number));
|
||||
case g1m_type_eact:
|
||||
return (g1m_parse_eact(handle, stream));
|
||||
case g1m_type_g3p:
|
||||
return (g1m_parse_g3p(handle, stream, &hd));
|
||||
default: break;
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/mcs.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:47:49 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_program:
|
||||
* Parse a program.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg length the data length.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_program(g1m_t *handle, FILE *stream,
|
||||
uint_fast32_t length)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_programheader)
|
||||
|
||||
/* print header data */
|
||||
log_info("Program password is '%.8s'.", hd.password);
|
||||
|
||||
/* seek for what's left in the file */
|
||||
SKIP(length - sizeof(struct mcs_programheader))
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_spreadsheet:
|
||||
* Parse a spreadsheet.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_spreadsheet(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_spreadsheetheader)
|
||||
|
||||
/* correct endianess */
|
||||
uint_fast32_t colcount = be32toh(hd.column_count << 8);
|
||||
hd.defs_size = be32toh(hd.defs_size);
|
||||
|
||||
/* log some info */
|
||||
log_info("%ld columns to parse!", colcount);
|
||||
|
||||
/* get the column directory */
|
||||
uint32_t column_directory[colcount];
|
||||
READ(&column_directory, sizeof(uint32_t) * colcount)
|
||||
/* - we should use `be32toh` here, but it isn't necessary for bool check. */
|
||||
|
||||
/* browse columns */
|
||||
for (uint_fast32_t c = 0; c < colcount; c++) {
|
||||
/* check if column is empty */
|
||||
if (!column_directory[c])
|
||||
continue;
|
||||
|
||||
/* get the row directory */
|
||||
uint8_t row_directory[0x80];
|
||||
READ(&row_directory, (size_t)0x80)
|
||||
|
||||
/* initialize loop values */
|
||||
uint8_t *rd = row_directory;
|
||||
int bit = 1 << 7;
|
||||
|
||||
/* explore each cell */
|
||||
for (uint_fast32_t i = 0; i < 1000; i++) {
|
||||
/* check if used */
|
||||
if (*rd & bit) {
|
||||
/* get cell */
|
||||
DREAD(cell, bcd)
|
||||
|
||||
/* log it */
|
||||
log_info("[%ld, %ld] not empty.", c, i);
|
||||
}
|
||||
|
||||
/* iterate bit and rd */
|
||||
rd += (bit & 1);
|
||||
bit = (bit >> 1) | ((bit & 1) << 7);
|
||||
}
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_list:
|
||||
* Parse an List.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg size the
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_list(g1m_t *handle, FILE *stream, uint_fast32_t size)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_listheader)
|
||||
|
||||
/* correct endianess */
|
||||
uint16_t elcount = hd.element_count;
|
||||
elcount = be16toh(elcount);
|
||||
|
||||
/* prepare browsing */
|
||||
size -= sizeof(struct mcs_listheader);
|
||||
size_t elsize = sizeof(struct bcd) * elcount;
|
||||
|
||||
/* make tabs */
|
||||
struct bcd real[elcount];
|
||||
struct bcd imgn[elcount];
|
||||
bzero(real, elsize);
|
||||
bzero(imgn, elsize);
|
||||
|
||||
/* browse */
|
||||
log_info("Browsing %d list elements", elcount);
|
||||
READ(&real, elsize)
|
||||
if (size > elsize) /* FIXME: HACK */
|
||||
READ(&imgn, elsize)
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_mat:
|
||||
* Parse a Matrix.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_mat(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_matheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.width = be16toh(hd.width);
|
||||
hd.height = be16toh(hd.height);
|
||||
|
||||
/* log info */
|
||||
log_info("Matrix is %d/%d", hd.width, hd.height);
|
||||
|
||||
/* parse cells */
|
||||
size_t num = hd.width * hd.height;
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
/* read the cell */
|
||||
DREAD(cell, bcd)
|
||||
|
||||
/* get x using `i % height` and y using `i / height` */
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs_capture:
|
||||
* Parse a capture.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
static int g1m_parse_mcs_capture(g1m_t *handle, FILE *stream)
|
||||
{
|
||||
/* read header */
|
||||
DREAD(hd, mcs_captureheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.width = be16toh(hd.width);
|
||||
hd.height = be16toh(hd.height);
|
||||
|
||||
/* print info */
|
||||
log_info("capture is %ux%u sized", hd.width, hd.height);
|
||||
|
||||
/* skip */
|
||||
SKIP(hd.height * (hd.width / 8 + !!(hd.width % 8)))
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
||||
|
||||
/**
|
||||
* g1m_parse_mcs:
|
||||
* We have passed the Standard Header and it's a g... MCS! Let's parse it!
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg num number of sizes.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse_mcs(g1m_t *handle, FILE *stream, uint_fast16_t num)
|
||||
{
|
||||
log_info("%ld parts to browse", num);
|
||||
|
||||
/* read all of the parts */
|
||||
for (uint_fast16_t i = 0; i < num; i++) {
|
||||
/* get the subheader */
|
||||
DREAD(hd, mcs_subheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.subcount = be32toh(hd.subcount);
|
||||
|
||||
/* log info about part */
|
||||
log_info("[%ld] Internal name is '%.16s'", i, hd.intname);
|
||||
log_info("[%ld] %d subparts to browse", i, hd.subcount);
|
||||
|
||||
/* foreach subpart */
|
||||
uint_fast32_t subcount = hd.subcount;
|
||||
for (uint_fast32_t j = 0; j < subcount; j++) {
|
||||
/* get the part header */
|
||||
DREAD(phd, mcs_partheader)
|
||||
|
||||
/* correct endianess */
|
||||
phd.datalength = be32toh(phd.datalength);
|
||||
|
||||
/* log info about the subpart */
|
||||
log_info("[%ld,%ld] directory name is '%.8s'", i, j, phd.dirname);
|
||||
log_info("[%ld,%ld] filename is '%.8s'", i, j, phd.filename);
|
||||
log_info("[%ld,%ld] directory type is '%s' (0x%02d)",
|
||||
i, j, g1m_get_dtype_string(phd.dirtype), phd.dirtype);
|
||||
log_info("[%ld,%ld] data length is %u", i, j, phd.datalength);
|
||||
|
||||
/* and read that much data */
|
||||
int err = 0;
|
||||
switch (phd.dirtype) {
|
||||
case mcs_dtype_program:
|
||||
err = g1m_parse_mcs_program(handle, stream, phd.datalength);
|
||||
break;
|
||||
case mcs_dtype_spreadsheet:
|
||||
err = g1m_parse_mcs_spreadsheet(handle, stream);
|
||||
break;
|
||||
case mcs_dtype_capture:
|
||||
err = g1m_parse_mcs_capture(handle, stream);
|
||||
break;
|
||||
case mcs_dtype_list:
|
||||
err = g1m_parse_mcs_list(handle, stream, phd.datalength);
|
||||
break;
|
||||
case mcs_dtype_mat:
|
||||
err = g1m_parse_mcs_mat(handle, stream);
|
||||
break;
|
||||
default:
|
||||
SKIP(phd.datalength)
|
||||
}
|
||||
|
||||
/* if there is an error, return it */
|
||||
if (err) return (err);
|
||||
}
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/picture_cg.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:47:49 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/**
|
||||
* g1m_parse_g3p:
|
||||
* Parse a G3P file.
|
||||
*
|
||||
* @arg handle the handle.
|
||||
* @arg stream the stream to parse from.
|
||||
* @arg std the standard header.
|
||||
* @return the error code (0 if ok).
|
||||
*/
|
||||
|
||||
int g1m_parse_g3p(g1m_t *handle, FILE *stream,
|
||||
struct standard_header *std)
|
||||
{
|
||||
/* get the header and footer */
|
||||
DREAD(hd, g3p_subheader)
|
||||
|
||||
/* correct endianess */
|
||||
hd.control = be32toh(hd.control);
|
||||
hd.width = be16toh(hd.width);
|
||||
hd.height = be16toh(hd.height);
|
||||
hd.color_depth = be16toh(hd.color_depth);
|
||||
hd.dimage_length = be32toh(hd.dimage_length);
|
||||
|
||||
/* check control */
|
||||
if (memcmp(hd.magic, "CP", 2)) {
|
||||
log_info("could not read CP.");
|
||||
return (g1m_error_magic);
|
||||
} else if (memcmp(hd.magic, "0100", 4)) {
|
||||
log_info("could not read header 0100");
|
||||
return (g1m_error_magic);
|
||||
}
|
||||
|
||||
/* TODO: what about "length of packed data + 4" control? */
|
||||
/* TODO: get footer, check control 0100 */
|
||||
|
||||
/* check if image is encrypted */
|
||||
int is_encrypted = (((std->filesize & 0xff) +
|
||||
((std->filesize & 0xff00) >> 8) + g1m_type_g3p) & 0xff) ^ 0x1c;
|
||||
|
||||
/* log info */
|
||||
log_info("Width: %dpx, height: %dpx", hd.width, hd.height);
|
||||
log_info("Pixel depth: %s",
|
||||
(hd.color_depth == g3p_color_4bit) ? "4-bit" : "16-bit");
|
||||
log_info("Deflated image length: %uo", hd.dimage_length);
|
||||
log_info("Is encrypted: %s", is_encrypted ? "yes" : "no");
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/* ************************************************************************** */
|
||||
/* _____ _ */
|
||||
/* parse/skip.c |_ _|__ _ _| |__ ___ _ _ */
|
||||
/* | Project: libg1m | |/ _ \| | | | '_ \ / _ \ | | | */
|
||||
/* | | (_) | |_| | | | | __/ |_| | */
|
||||
/* By: thomas <thomas@touhey.fr> |_|\___/ \__,_|_| |_|\___|\__, |.fr */
|
||||
/* Last updated: 2016/11/02 14:48:51 |___/ */
|
||||
/* */
|
||||
/* ************************************************************************** */
|
||||
#include <libg1m/internals.h>
|
||||
|
||||
/**
|
||||
* g1m_parse_skip:
|
||||
* Skip some bytes.
|
||||
*
|
||||
* I've made two loops to avoid conditions in each loop iteration
|
||||
* when checksum is not calculated.
|
||||
*
|
||||
* @arg stream the stream where to skip bytes.
|
||||
* @arg size the size to skip.
|
||||
* @arg checksum pointer to the checksum variable if there is one.
|
||||
*/
|
||||
|
||||
int g1m_parse_skip(FILE *stream, size_t size, uint_fast32_t *checksum)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
size_t size_ini = size;
|
||||
|
||||
if (checksum) {
|
||||
uint_fast32_t add = 0;
|
||||
while (1) {
|
||||
/* if no size left, exit */
|
||||
if (!size) break;
|
||||
|
||||
/* read that much */
|
||||
{
|
||||
size_t curlen = min(size, 1024);
|
||||
size_t read_len = fread(buf, 1, curlen, stream);
|
||||
size -= read_len;
|
||||
if (read_len < curlen) {
|
||||
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
|
||||
"%zu missing", size_ini - size, size_ini, size);
|
||||
return (g1m_error_eof);
|
||||
}
|
||||
|
||||
/* feed the checksum */
|
||||
for (size_t i = 0; i < curlen; i++)
|
||||
add += buf[i];
|
||||
}
|
||||
}
|
||||
*checksum += add;
|
||||
} else {
|
||||
while (1) {
|
||||
/* if no size left, exit */
|
||||
if (!size) break;
|
||||
|
||||
/* read that much */
|
||||
{
|
||||
size_t curlen = min(size, 1024);
|
||||
size_t read_len = fread(buf, 1, curlen, stream);
|
||||
size -= read_len;
|
||||
if (read_len < curlen) {
|
||||
log_info("SKIPPING failed, skipped %zu/%zu bytes, "
|
||||
"%zu missing", size_ini - size, size_ini, size);
|
||||
return (g1m_error_eof);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* no error */
|
||||
return (0);
|
||||
}
|
Reference in New Issue