From 00338af264eb240069d9d23db2138a32cf364235 Mon Sep 17 00:00:00 2001 From: "Thomas \"Cakeisalie5\" Touhey" Date: Sun, 19 Mar 2017 23:27:21 +0100 Subject: [PATCH] Started correcting pictures, started encoding (placeholder) --- include/libg1m.h | 17 +++++- include/libg1m/buffer.h | 24 ++++++-- include/libg1m/handle.h | 8 +-- include/libg1m/internals.h | 27 ++++----- include/libg1m/internals/stdio_ext.h | 1 + include/libg1m/mcs.h | 1 + src/core/free.c | 2 + src/core/strerror.c | 6 ++ src/decode/cas.c | 2 +- src/decode/casemul.c | 1 - src/decode/std/picture.c | 67 ++++++++++++-------- src/encode/main.c | 91 ++++++++++++++++++++++++++++ src/manage/mcs.c | 6 +- src/utils/buffer.c | 38 +++++++++++- src/{core/open.c => utils/file.c} | 62 +++++++++++++++++-- 15 files changed, 285 insertions(+), 68 deletions(-) create mode 100644 src/encode/main.c rename src/{core/open.c => utils/file.c} (58%) diff --git a/include/libg1m.h b/include/libg1m.h index 58bcccd..97a42e6 100644 --- a/include/libg1m.h +++ b/include/libg1m.h @@ -58,6 +58,9 @@ extern "C" { # define g1m_error_nostream 0x10 # define g1m_error_noread 0x11 # define g1m_error_noseek 0x12 +# define g1m_error_nowrite 0x13 +# define g1m_error_read 0x14 +# define g1m_error_write 0x15 /* Then, the decoding errors */ # define g1m_error_unrecognized 0x20 @@ -97,6 +100,15 @@ extern int g1m_make_picture(g1m_t **handle, unsigned int width, unsigned int height); extern int g1m_make_addin(g1m_t **h, g1m_platform_t platform, const g1m_version_t *version, const time_t *created); + +/* encode a handle, get the extension */ +extern int g1m_encode(g1m_t *handle, g1m_buffer_t *buffer); + +/* put into a FILE */ +#ifndef G1M_DISABLED_FILE +extern int g1m_write(g1m_t *handle, const char *path); +extern int g1m_fwrite(g1m_t *handle, FILE *stream); +#endif /* ************************************************************************** */ /* Main MCS functions */ /* ************************************************************************** */ @@ -135,9 +147,8 @@ extern int g1m_decode_casfile_part(g1m_mcsfile_t *file, g1m_buffer_t *buffer); /* MCS archive management */ /* ************************************************************************** */ /* create and insert an MCS file into a MCS archive */ -extern int g1m_mcs_insert(g1m_t *handle, const g1m_mcshead_t *head, - g1m_mcsfile_t **tofile); - +extern int g1m_mcs_insert(g1m_t *handle, g1m_mcsfile_t **tofile, + const g1m_mcshead_t *head); /* ************************************************************************** */ /* Miscallaneous utilities */ /* ************************************************************************** */ diff --git a/include/libg1m/buffer.h b/include/libg1m/buffer.h index b5866f0..080e697 100644 --- a/include/libg1m/buffer.h +++ b/include/libg1m/buffer.h @@ -24,7 +24,8 @@ * called with the size of the file to write, in order to prepare space for * it. If the `announce` callback returns an error, then the file will not * be written. If the announcement is successful, then the `write` callback - * is used. + * is used. If there is an error and the `unannounce` callback is set, it + * will be called in order to free any allocated buffer. * * In each case, the `cookie` is sent as the first argument to your callbacks. * ************************************************************************** */ @@ -39,7 +40,8 @@ /* The callbacks types... */ typedef int (*g1m_buffer_read_t)(void*, unsigned char*, size_t); typedef int (*g1m_buffer_write_t)(void*, const unsigned char*, size_t); -typedef int (*g1m_buffer_announce_t)(void*, uint_fast32_t size); +typedef int (*g1m_buffer_announce_t)(void*, size_t); +typedef void (*g1m_buffer_unannounce_t)(void*); /* ... and the structure of a buffer. */ typedef struct { @@ -49,8 +51,15 @@ typedef struct { g1m_buffer_read_t read; g1m_buffer_write_t write; g1m_buffer_announce_t announce; + g1m_buffer_unannounce_t unannounce; } g1m_buffer_t; - +/* ************************************************************************** */ +/* File buffer definition */ +/* ************************************************************************** */ +/* Callbacks (the cookie is the FILE* pointer) */ +extern int g1m_filebuffer_read(void *vcookie, unsigned char *buf, size_t size); +extern int g1m_filebuffer_write(void *cookie, const unsigned char *dest, + size_t size); /* ************************************************************************** */ /* Memory buffer definition */ /* ************************************************************************** */ @@ -61,8 +70,7 @@ typedef struct { } g1m_cursor_t; /* Related callbacks and functions */ -int g1m_membuffer_read(void *cookie, unsigned char *dest, size_t size); - +extern int g1m_membuffer_read(void *cookie, unsigned char *dest, size_t size); /* ************************************************************************** */ /* Limited buffer definition */ /* ************************************************************************** */ @@ -75,10 +83,14 @@ typedef struct { /* Related functions and callbacks */ int g1m_limbuffer_read(void *cookie, unsigned char *dest, size_t size); int g1m_empty_limbuffer(g1m_buffer_t *limbuffer); - /* ************************************************************************** */ /* Buffer macros */ /* ************************************************************************** */ +/* Initialize a file buffer */ +# define g1m_filebuffer(F) (g1m_filebuffer_t){ \ + .cookie = (void*)(F), .read = g1m_filebuffer_read, \ + .write = g1m_filebuffer_write} + /* Initialize a memory buffer */ # define g1m_membuffer(P, SZ) (g1m_buffer_t){ \ .cookie = (g1m_cursor_t[]){{.p = (P), .left = (SZ)}}, \ diff --git a/include/libg1m/handle.h b/include/libg1m/handle.h index a94afed..5eb26e6 100644 --- a/include/libg1m/handle.h +++ b/include/libg1m/handle.h @@ -48,12 +48,6 @@ typedef unsigned int g1m_platform_t; # define g1m_platform_cg 0x0004 # define g1m_platform_cas 0x0008 # define g1m_platform_casemul 0x0010 - -/* Platform flags */ -# define g1m_platflag_be 0x8000 - -/* Platform macros */ -# define g1m_platform(P) ((P) & 0x7FFF) /* ************************************************************************** */ /* Helpers */ /* ************************************************************************** */ @@ -67,7 +61,7 @@ typedef struct g1m_version_s { typedef struct { /* file type, the source destination platform */ g1m_type_t type; - g1m_platform_t platform; /* use the `g1m_platorm` macro! */ + g1m_platform_t platform; /* Add-in related data */ char title[17]; diff --git a/include/libg1m/internals.h b/include/libg1m/internals.h index 9b91e6a..7fddbc3 100644 --- a/include/libg1m/internals.h +++ b/include/libg1m/internals.h @@ -28,7 +28,8 @@ # include # include # include -# define MEMBUFFER(P, SZ) g1m_membuffer(P, SZ) +# define FILEBUFFER(F) g1m_filebuffer(F) +# define MEMBUFFER(P, SZ) g1m_membuffer(P, SZ) # define LIMBUFFER(BUFFER, SZ) g1m_limbuffer(BUFFER, SZ) /* ************************************************************************** */ @@ -94,12 +95,12 @@ # define WRITE(BUF, SZ) \ (*buffer->write)(buffer->cookie, (unsigned char*)(BUF), (SZ)); # define DWRITE(S) WRITE(&(S), sizeof(S)) - /* ************************************************************************** */ /* Decoding functions */ /* ************************************************************************** */ /* with expected types */ -int g1m_decode_std(g1m_t **handle, const char *path, g1m_buffer_t *buffer, +extern int g1m_decode_std(g1m_t **handle, const char *path, + g1m_buffer_t *buffer, struct standard_header*, g1m_type_t expected_types); /* w/o expected types */ @@ -159,10 +160,11 @@ G1M_MCSFUNC(string) /* CASPRO-specific decoding functions */ /* ************************************************************************** */ # define G1M_CASFUNC(NAME) \ -int g1m_decode_caspart_##NAME(g1m_mcsfile_t *handle, g1m_buffer_t *buffer); -# define G1M_CASHFUNC(NAME) \ -int g1m_decode_cashpart_##NAME(g1m_mcshead_t *head, g1m_mcshead_t *heads, \ +extern int g1m_decode_caspart_##NAME(g1m_mcsfile_t *handle, \ g1m_buffer_t *buffer); +# define G1M_CASHFUNC(NAME) \ +extern int g1m_decode_cashpart_##NAME(g1m_mcshead_t *head, \ + g1m_mcshead_t *heads, g1m_buffer_t *buffer); G1M_CASFUNC(var) G1M_CASFUNC(program) @@ -184,19 +186,16 @@ G1M_CASFUNC(capture) /* Utilities */ /* ************************************************************************** */ /* Free-ing */ -void g1m_free_line_content(g1m_line_t *line); +extern void g1m_free_line_content(g1m_line_t *line); /* Skipping */ -int g1m_skip(g1m_buffer_t *buffer, size_t size, uint32_t *checksum); +extern int g1m_skip(g1m_buffer_t *buffer, size_t size, uint32_t *checksum); /* Checksum-ing */ -uint8_t g1m_checksum8(void *mem, size_t size); -uint32_t g1m_checksum32(void *mem, size_t size, uint32_t checksum); +extern uint8_t g1m_checksum8(void *mem, size_t size); +extern uint32_t g1m_checksum32(void *mem, size_t size, uint32_t checksum); /* Get extension */ -int g1m_getext(const char *path, char *buf, size_t n); - -/* File buffer */ -int g1m_filebuffer_read(void *vcookie, unsigned char *buf, size_t size); +extern int g1m_getext(const char *path, char *buf, size_t n); #endif /* LIBG1M_INTERNALS_H */ diff --git a/include/libg1m/internals/stdio_ext.h b/include/libg1m/internals/stdio_ext.h index 46a1d78..8852146 100644 --- a/include/libg1m/internals/stdio_ext.h +++ b/include/libg1m/internals/stdio_ext.h @@ -34,6 +34,7 @@ # include # else # define __freadable(F) (1) +# define __fwritable(F) (1) # endif #endif /* LIBG1M_INTERNALS_STDIO_EXT_H */ diff --git a/include/libg1m/mcs.h b/include/libg1m/mcs.h index 3a967bd..93f95eb 100644 --- a/include/libg1m/mcs.h +++ b/include/libg1m/mcs.h @@ -55,6 +55,7 @@ typedef g1m_mcsfile_type_t g1m_mcstype_t; # define g1m_mcstype_capture g1m_mcstype_capt # define g1m_mcstype_spreadsheet g1m_mcstype_ssheet # define g1m_mcstype_vector g1m_mcstype_vct +# define g1m_mcstype_alpha g1m_mcstype_var # define g1m_mcstype_alphamem g1m_mcstype_var # define g1m_mcstype_variable g1m_mcstype_var # define g1m_mcstype_variables g1m_mcstype_var diff --git a/src/core/free.c b/src/core/free.c index 65b6310..da6a49c 100644 --- a/src/core/free.c +++ b/src/core/free.c @@ -15,6 +15,8 @@ * * You should have received a copy of the GNU Lesser General Public License * along with libg1m; if not, see . + * + * TODO: dispatch this file into sources in the `manage` module! * ************************************************************************** */ #include #include diff --git a/src/core/strerror.c b/src/core/strerror.c index fb21479..bef15ef 100644 --- a/src/core/strerror.c +++ b/src/core/strerror.c @@ -37,6 +37,12 @@ const char *g1m_error_strings[] = { "given stream was not readable", [g1m_error_noseek] = "given stream was not seekable", + [g1m_error_nowrite] = + "given stream was not writable", + [g1m_error_read] = + "a read operation failed", + [g1m_error_write] = + "a write operation failed", [g1m_error_wrong_type] = "was not of the expected type", diff --git a/src/decode/cas.c b/src/decode/cas.c index 25567f3..86fdc80 100644 --- a/src/decode/cas.c +++ b/src/decode/cas.c @@ -278,7 +278,7 @@ int g1m_decode_cas(g1m_t **h, g1m_buffer_t *buffer) for (int i = 0; i < numheads; i++) { /* prepare the file */ log_info("Preparing the group file #%d", i); - g1m_mcsfile_t *file; err = g1m_mcs_insert(handle, &heads[i], &file); + g1m_mcsfile_t *file; err = g1m_mcs_insert(handle, &file, &heads[i]); if (err) goto fail; /* read each part */ diff --git a/src/decode/casemul.c b/src/decode/casemul.c index 7db66b5..0ea9493 100644 --- a/src/decode/casemul.c +++ b/src/decode/casemul.c @@ -388,7 +388,6 @@ int g1m_decode_casemul(g1m_t **h, g1m_buffer_t *buffer, int big_endian) log_warn("Should read compiled part here!"); /* no error! */ - handle->platform &= ~g1m_platflag_be; return (0); fail: g1m_free(handle); diff --git a/src/decode/std/picture.c b/src/decode/std/picture.c index 4839102..a38214f 100644 --- a/src/decode/std/picture.c +++ b/src/decode/std/picture.c @@ -48,6 +48,9 @@ static void g3p_deobfuscate(uint8_t *buf, size_t n) int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer, struct standard_header *std) { + int err; *h = NULL; + uint8_t *defbuf = NULL, *infbuf = NULL; + /* get the G3P global header */ DREAD(hd, g3p_subheader) hd.g3p_size = be32toh(hd.g3p_size); @@ -58,8 +61,13 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer, return (g1m_error_magic); } + /* start a checksum */ + uint32_t check = g1m_checksum32((void*)std, + sizeof(struct standard_header), 0); + /* read the image header */ DREAD(ihd, g3p_imageheader) + check = g1m_checksum32(&ihd, sizeof(struct g3p_imageheader), check); ihd.df_size = be32toh(ihd.df_size); ihd.width = be16toh(ihd.width); ihd.height = be16toh(ihd.height); @@ -70,7 +78,7 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer, int is_obfuscated = ((std->obfuscated + 8) & 0xff) != ((std->filesize & 0xff00) >> 8); int has_footer = 1; /* might change? */ - size_t deflated_image_size = ihd.data_size - has_footer * 0x4; + size_t deflated_image_size = ihd.data_size; /* log info */ log_info("Width: %" PRIu16 "px, height: %" PRIu16 "px", @@ -80,16 +88,18 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer, log_info("Deflated image length: %" PRIuSIZE "o", deflated_image_size); log_info("Is obfuscated: %s", is_obfuscated ? "yes" : "no"); - /* read image */ - uint8_t deflated_image[deflated_image_size]; - READ(deflated_image, deflated_image_size) + /* read image: FIXME allocate in heap? */ + err = g1m_error_alloc; + defbuf = malloc(deflated_image_size); + if (!defbuf) goto fail; + READ(defbuf, deflated_image_size) /* read footer and check sum */ if (has_footer) { DREAD(ft, g3p_imagefooter); ft.checksum = be32toh(ft.checksum); - uLong adl = adler32(0, deflated_image, deflated_image_size); + uLong adl = adler32(0, defbuf, deflated_image_size); if (adl != ft.checksum) { log_fatal("Incorrect Adler32 checksum!"); log_fatal("Expected %" PRIu32 ", got %lu", @@ -99,38 +109,45 @@ int g1m_decode_std_g3p(g1m_t **h, g1m_buffer_t *buffer, } /* unobfuscate if required */ - if (is_obfuscated) - g3p_deobfuscate(deflated_image, deflated_image_size); + if (is_obfuscated) g3p_deobfuscate(defbuf, deflated_image_size); - /* find out length of inflated data */ - uLongf inflated_size = 0L; int z_err; - if ((z_err = uncompress(Z_NULL, &inflated_size, deflated_image, - deflated_image_size)) != Z_MEM_ERROR) { + /* make the destination buffer */ + size_t rawsize = ihd.color_depth == g3p_color_4bit + ? g1m_picturesize_4bit_code(ihd.width, ihd.height) + : g1m_picturesize_16bit(ihd.width, ihd.height); + err = g1m_error_alloc; + infbuf = malloc(rawsize); + if (!infbuf) goto fail; + + /* uncompress */ + err = g1m_error_alloc; + uLongf inflated_size = 0L; // rawsize? + int z_err = uncompress(infbuf, &inflated_size, defbuf, deflated_image_size); + if (z_err) { log_fatal("Zlib error: error #%d", z_err); - return (g1m_error_magic); + goto fail; } - log_info("Inflated image size is %lu", inflated_size); + free(defbuf); defbuf = NULL; - /* allocate space for it, and uncompress */ - uint8_t inflated_image[inflated_size]; - if ((z_err = uncompress(inflated_image, &inflated_size, deflated_image, - deflated_image_size))) { - log_fatal("Zlib error: error #%d", z_err); - return (g1m_error_magic); - } - - /* allocate picture in handle */ - int err = g1m_make_picture(h, ihd.width, ihd.height); - if (err) return (err); + /* make the handle */ + err = g1m_make_picture(h, ihd.width, ihd.height); + if (err) goto fail; g1m_t *handle = *h; /* then store it */ g1m_decode_picture(handle->pixels, ihd.color_depth == g3p_color_4bit ? g1m_pictureformat_4bit_code : g1m_pictureformat_16bit, - inflated_image, handle->width, handle->height); + infbuf, handle->width, handle->height); + free(infbuf); infbuf = NULL; + + /* TODO: footers? */ /* no error */ return (0); +fail: + g1m_free(*h); *h = NULL; + free(infbuf); free(defbuf); + return (err); } /** diff --git a/src/encode/main.c b/src/encode/main.c new file mode 100644 index 0000000..12052aa --- /dev/null +++ b/src/encode/main.c @@ -0,0 +1,91 @@ +/* ***************************************************************************** + * encode/main.c -- encode a file. + * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey + * + * This file is part of libg1m. + * libg1m is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation; either version 3.0 of the License, + * or (at your option) any later version. + * + * libg1m is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libg1m; if not, see . + * ************************************************************************** */ +#include +#define A(NAME) g1m_announce_##NAME +#define E(NAME) g1m_encode_##NAME + +/* ************************************************************************** */ +/* Correspondances */ +/* ************************************************************************** */ +/* Types */ +typedef int (*announce_t)(g1m_t*, size_t*); +typedef int (*encode_t)(g1m_t*, g1m_buffer_t*); +struct corresp { + /* 'identification' */ + g1m_type_t types; + g1m_platform_t platforms; + + /* functions */ + announce_t announce; + encode_t encode; +}; + +/* All correspondances */ +static const struct corresp encodings[] = { + /* sentinel */ + {0, 0, NULL, NULL} +}; + +/* ************************************************************************** */ +/* Main function */ +/* ************************************************************************** */ +/** + * g1m_encode: + * Encode a file. + * + * @arg handle the handle. + * @arg buffer the buffer to write to. + * @return the error code (0 if ok). + */ + +int g1m_encode(g1m_t *handle, g1m_buffer_t *buffer) +{ + int err; + + /* find the announcement and encoding function */ + const struct corresp *c = encodings - 1; + while ((++c)->encode) { + if (c->types && !(c->types & handle->type)) + continue; + if (c->platforms && !(c->platforms & handle->platform)) + continue; + break; + } + if (!c->encode) return (g1m_error_op); + + /* announce, if necessary */ + if (buffer->announce) { + size_t size; + if ((err = (*c->announce)(handle, &size)) + || (err = (*buffer->announce)(buffer->cookie, size))) + return (err); + } + + /* encode */ + if (buffer->write) { + err = (*c->encode)(handle, buffer); + if (err) { + (*buffer->unannounce)(buffer->cookie); + return (err); + } + } + + /* everything went well! */ + return (0); +} diff --git a/src/manage/mcs.c b/src/manage/mcs.c index 0456570..b40d08b 100644 --- a/src/manage/mcs.c +++ b/src/manage/mcs.c @@ -27,13 +27,13 @@ * Find space for a file and allocate. * * @arg handle the handle. + * @arg tofile the user file pointer, to set. * @arg head the head. - * @arg tofile the file pointer for the user. * @return the mcs file (NULL if allocation error). */ -int g1m_mcs_insert(g1m_t *handle, const g1m_mcshead_t *head, - g1m_mcsfile_t **tofile) +int g1m_mcs_insert(g1m_t *handle, g1m_mcsfile_t **tofile, + const g1m_mcshead_t *head) { g1m_mcsfile_t **pfile = NULL; *tofile = NULL; diff --git a/src/utils/buffer.c b/src/utils/buffer.c index 895ec1c..264cb06 100644 --- a/src/utils/buffer.c +++ b/src/utils/buffer.c @@ -17,7 +17,6 @@ * along with libg1m; if not, see . * ************************************************************************** */ #include - #ifndef G1M_DISABLED_FILE /* ************************************************************************** */ /* FILE buffer */ @@ -35,7 +34,9 @@ int g1m_filebuffer_read(void *vcookie, unsigned char *buf, size_t size) { FILE *file = (void*)vcookie; +# if LOGLEVEL <= ll_error size_t orig = size; +# endif while (size) { size_t read = fread(buf, 1, size, file); @@ -50,7 +51,7 @@ int g1m_filebuffer_read(void *vcookie, unsigned char *buf, size_t size) logm_info(buf, orig - size); } # endif - return (g1m_error_eof); + return (feof(file) ? g1m_error_eof : g1m_error_read); } } @@ -58,6 +59,39 @@ int g1m_filebuffer_read(void *vcookie, unsigned char *buf, size_t size) return (0); } +/** + * g1m_filebuffer_write: + * Write to a filebuffer. + * + * @arg vcookie the FILE* (uncasted) + * @arg buf the buffer to write. + * @arg size the size to write. + * @return the error (if any). + */ + +int g1m_filebuffer_write(void *vcookie, const unsigned char *buf, size_t size) +{ + FILE *file = (void*)vcookie; +# if LOGLEVEL <= ll_error + size_t orig = size; +# endif + + while (size) { + size_t written = fwrite(buf, 1, size, file); + buf += written; + size -= written; + + if (!written) { + log_error("WRITING failed: wrote %" PRIuSIZE "/%" PRIuSIZE + " bytes, %" PRIuSIZE " missing.", orig - size, orig, size); + return (g1m_error_write); + } + } + + /* no error! */ + return (0); +} + #endif /* ************************************************************************** */ /* Memory buffer */ diff --git a/src/core/open.c b/src/utils/file.c similarity index 58% rename from src/core/open.c rename to src/utils/file.c index b58f9bc..301a5fa 100644 --- a/src/core/open.c +++ b/src/utils/file.c @@ -1,5 +1,5 @@ /* ***************************************************************************** - * core/open.c -- open a file and decode it. + * utils/file.c -- FILE-related functions. * Copyright (C) 2017 Thomas "Cakeisalie5" Touhey * * This file is part of libg1m. @@ -21,9 +21,12 @@ #include #ifndef G1M_DISABLED_FILE +/* ************************************************************************** */ +/* Open a FILE */ +/* ************************************************************************** */ /** * g1m_open: - * Open handle using path. + * Open a file using its path, and decode it. * * @arg handle the handle to create. * @arg path the path of the file to open. @@ -34,10 +37,8 @@ int g1m_open(g1m_t **handle, const char *path, g1m_type_t expected) { - /* open stream */ + /* open stream and decode */ FILE *f = fopen(path, "r"); - - /* open using `g1m_fopen` */ int err = g1m_fopen(handle, path, f, expected); /* close opened stream and return error code (or 0 if ok) */ @@ -47,7 +48,7 @@ int g1m_open(g1m_t **handle, const char *path, /** * g1m_fopen: - * Open handle using pointer on FILE. + * Decode a file using a FILE structure. * * Will not seek, and will not keep the stream. * Make sure to fclose the stream after use. @@ -76,4 +77,53 @@ int g1m_fopen(g1m_t **handle, const char *path, FILE *stream, return (g1m_decode(handle, path, &buffer, expected)); } +/* ************************************************************************** */ +/* Open a FILE and write into it */ +/* ************************************************************************** */ +/** + * g1m_write: + * Open a file and encode the file into it. + * + * @arg handle the handle to encode. + * @arg path the file path. + * @return the error code (0 if ok). + */ + +int g1m_write(g1m_t *handle, const char *path) +{ + /* open the stream and write */ + FILE *f = fopen(path, "w"); + int err = g1m_fwrite(handle, f); + + /* close opened stream in case of error */ + if (f) fclose(f); + if (f && err) remove(path); + return (err); +} + +/** + * g1m_fwrite: + * Write to a FILE pointer. + * + * @arg handle the handle to encode. + * @arg stream the FILE stream. + * @return the error code (0 if ok). + */ + +int g1m_fwrite(g1m_t *handle, FILE *stream) +{ + /* check stream */ + if (!stream) return (g1m_error_nostream); + if (!__fwritable(stream)) return (g1m_error_nowrite); + + /* make the buffer */ + g1m_buffer_t buffer = { + .cookie = stream, + .write = g1m_filebuffer_write + }; + + /* encode to file */ + return (g1m_encode(handle, &buffer)); +} + #endif