summaryrefslogtreecommitdiff
path: root/common/binfile.c
diff options
context:
space:
mode:
Diffstat (limited to 'common/binfile.c')
-rw-r--r--common/binfile.c334
1 files changed, 334 insertions, 0 deletions
diff --git a/common/binfile.c b/common/binfile.c
new file mode 100644
index 0000000..fa0a120
--- /dev/null
+++ b/common/binfile.c
@@ -0,0 +1,334 @@
+ /*
+ * AUTHOR
+ * N. Nielsen
+ *
+ * LICENSE
+ * This software is in the public domain.
+ *
+ * The software is provided "as is", without warranty of any kind,
+ * express or implied, including but not limited to the warranties
+ * of merchantability, fitness for a particular purpose, and
+ * noninfringement. In no event shall the author(s) be liable for any
+ * claim, damages, or other liability, whether in an action of
+ * contract, tort, or otherwise, arising from, out of, or in connection
+ * with the software or the use or other dealings in the software.
+ *
+ * SUPPORT
+ * Send bug reports to: <nielsen@memberwebs.com>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include "binfile.h"
+
+#define BF_FILE 0x00000010
+#define BF_ERROR 0x00000040
+
+/* bfctx: ---------------------------------------------------
+ * This is the structure that becomes a BFILE
+ * to the outside world
+ */
+typedef struct _bfctx
+{
+ union
+ {
+ FILE* file; /* Valid when working with a FILE */
+ void* data; /* Valid when working with memory */
+ } d;
+ size_t cur; /* Current position */
+ size_t len; /* Size of the memory buffer */
+ int flags;
+}
+bfctx;
+
+
+/* bfWriteRaw: -----------------------------------------------
+ * Write data to appropriate buffer/file
+ */
+int bfWriteRaw(BFILE h, const void* data, size_t len)
+{
+ bfctx* ctx = (bfctx*)h;
+
+ /* file */
+ if(ctx->flags & BF_FILE)
+ {
+ len = fwrite(data, len, 1, ctx->d.file);
+ ctx->cur += len;
+ return len;
+ }
+
+ /* memory */
+ else
+ {
+ /* Check if enough space */
+ if((ctx->cur + len) > ctx->len)
+ {
+ /* If we ran out of space try and reallocate */
+ if(ctx->flags & BF_REALLOC)
+ {
+ void* old = ctx->d.data;
+
+ ctx->len = ctx->len + (((len / 1024) + 1) * 1024);
+ ctx->d.data = realloc(ctx->d.data, ctx->len);
+
+ if(!ctx->d.data)
+ free(old);
+ }
+
+ /*
+ * If there's no more space and we can't
+ * reallocate then it's an error
+ */
+ if(!ctx->d.data || !(ctx->flags & BF_REALLOC))
+ {
+ ctx->flags |= BF_ERROR;
+ return 0;
+ }
+
+ }
+
+ /* write the data in */
+ memcpy(((unsigned char*)ctx->d.data) + ctx->cur, data, len);
+ ctx->cur += len;
+ return len;
+ }
+}
+
+
+/* bfReadRaw: ------------------------------------------------------
+ * Read data from either buffer or file
+ */
+int bfReadRaw(BFILE h, void* data, size_t len)
+{
+ bfctx* ctx = (bfctx*)h;
+
+ /* file */
+ if(ctx->flags & BF_FILE)
+ {
+ len = fread(data, 1, len, ctx->d.file);
+ ctx->cur += len;
+ return len;
+ }
+
+ /* memory */
+ else
+ {
+ if((ctx->len - ctx->cur) < len)
+ len = ctx->len - ctx->cur;
+
+ memcpy(data, ((unsigned char*)ctx->d.data) + ctx->cur, len);
+ ctx->cur += len;
+ return len;
+ }
+}
+
+
+/* bfSeek: ---------------------------------------------------------
+ * Seek forward in either buffer or file
+ */
+int bfSeek(bfctx* ctx, long offset)
+{
+ /* file */
+ if(ctx->flags & BF_FILE)
+ {
+ int ret = fseek(ctx->d.file, offset, SEEK_CUR);
+ if(ret == 0)
+ ctx->cur += offset;
+ return ret;
+ }
+
+ /* memory */
+ else
+ {
+ if((long)(ctx->len - ctx->cur) < offset)
+ return 1;
+ ctx->cur += offset;
+
+ return 0;
+ }
+}
+
+
+/* bfError: -------------------------------------------------
+ * Check for errors on output
+ */
+int bfError(BFILE h)
+{
+ bfctx* ctx = (bfctx*)h;
+ if(ctx->flags & BF_FILE)
+ return ferror(ctx->d.file);
+ else
+ return ctx->flags & BF_ERROR;
+}
+
+
+/* bfStartFile: -----------------------------------------------
+ * Get a BFILE based on a FILE output
+ * file should be opened for appropriate input or output
+ */
+BFILE bfStartFile(FILE* file)
+{
+ bfctx* ctx = (bfctx*)malloc(sizeof(bfctx));
+ if(!ctx) return 0;
+ memset(ctx, 0, sizeof(bfctx));
+
+ ctx->d.file = file;
+ ctx->flags = BF_FILE;
+ return ctx;
+}
+
+/* bfStartMem: -------------------------------------------------
+ * Get a BFILE based on memory
+ * If flags have BF_REALLOC set then NULL can be passed
+ * and/or we can reallocate the buffer
+ */
+BFILE bfStartMem(void* mem, size_t len, int flags)
+{
+ bfctx* ctx = (bfctx*)malloc(sizeof(bfctx));
+ if(!ctx) return 0;
+ memset(ctx, 0, sizeof(bfctx));
+
+ ctx->len = len;
+ ctx->flags = flags;
+ ctx->flags &= ~(BF_FILE | BF_ERROR);
+ ctx->d.data = mem;
+
+ return ctx;
+}
+
+/* bfClose: -----------------------------------------------------
+ * Close a BFILE
+ * Returns either the file or memory buffer, and returns size
+ * in the len argument
+ */
+void bfClose(BFILE h)
+{
+ if(h)
+ free(h);
+}
+
+/* bfInternal: --------------------------------------------------
+ * Get the internal file handle or memory pointer for this
+ * binfile
+ */
+void* bfInternal(BFILE h)
+{
+ bfctx* ctx = (bfctx*)h;
+ if(ctx->flags & BF_FILE)
+ return ctx->d.file;
+ else
+ return ctx->d.data;
+}
+
+/* bfCount: ----------------------------------------------------
+ * Returns the number of bytes written or read to this binfile
+ */
+size_t bfCount(BFILE h)
+{
+ bfctx* ctx = (bfctx*)h;
+ return ctx->cur;
+}
+
+/* bfWriteEnd: -------------------------------------------------
+ * Puts down an end of tags marker in the binfile
+ */
+int bfWriteEnd(BFILE h)
+{
+ bfval opt;
+ int c;
+
+ opt.id = BINTYPE_END;
+ opt.len = 0;
+ opt.type = BINTYPE_END;
+ c = bfWriteRaw(h, &opt, sizeof(opt));
+
+ return bfError(h) ? 0 : c;
+}
+
+/* bfWriteValue: -----------------------------------------------
+ * Writes a tagged value to the binfile
+ */
+int bfWriteValue(BFILE h, const bfval* val, const void* data)
+{
+ int c = bfWriteRaw(h, val, sizeof(bfval));
+ c += bfWriteRaw(h, data, val->len);
+ return bfError(h) ? 0 : c;
+}
+
+/* bfReadValueInfo: --------------------------------------------
+ * Gets the next tag (without data) and returns the length
+ * of the memory required to hold it.
+ */
+int bfReadValueInfo(BFILE h, bfval* val)
+{
+ int c = bfReadRaw(h, val, sizeof(bfval));
+ return c == sizeof(bfval) && !bfError(h);
+}
+
+/* bfReadValueData: ---------------------------------------------
+ * Gets the data for a value. val should be same one returned
+ * from bfReadValueInfo
+ */
+int bfReadValueData(BFILE h, const bfval* val, void* data)
+{
+ size_t c;
+ if((c = bfReadRaw(h, data, val->len)) != val->len ||
+ bfError(h))
+ return 0;
+
+ /* Special handling for these guys */
+ switch(val->type)
+ {
+ case BINTYPE_ASCII:
+ {
+ char* str = (char*)data;
+ str[val->len] = 0;
+ }
+ break;
+
+ case BINTYPE_INT32:
+ case BINTYPE_INT16:
+ /* TODO: do endianness here */
+ break;
+ };
+
+ return c;
+}
+
+/* bfSkipValueData: ------------------------------------------------
+ * Skips the data for a tagged value
+ */
+int bfSkipValueData(BFILE h, const bfval* val)
+{
+ bfctx* ctx = (bfctx*)h;
+ return bfSeek(ctx, val->len) == 0;
+}
+
+
+/* bfWriteString: --------------------------------------------------
+ * Convenience function for writing out strings to file
+ */
+int bfWriteString(BFILE h, short id, const char* data)
+{
+ bfval val;
+ val.id = id;
+ val.type = BINTYPE_ASCII;
+ val.len = strlen(data);
+
+ return bfWriteValue(h, &val, (void*)data);
+}
+
+/* bfWriteInt: -----------------------------------------------------
+ * Convenience function for writing out 4 byte integers to file
+ */
+int bfWriteInt(BFILE h, short id, int data)
+{
+ bfval val;
+ val.id = id;
+ val.type = BINTYPE_INT32;
+ val.len = BINSIZE_INT32;
+
+ /* TODO: take care of endianess here */
+ return bfWriteValue(h, &val, &data);
+}
+