// Copyright © 2016-2017 Graham Sanderson, all rights reserved. #define _GNU_SOURCE // Needed for fopencookie. See: man 7 feature_test_macros #include "lith_common.h" #include "lith_file.h" #include "lith_world.h" #include "lith_base64.h" #include #define SAVE_BLOCK_SIZE 230 // Type Definitions ----------------------------------------------------------| // // memfile_t // typedef struct memfile_t { byte *mem; size_t len; size_t pos; } memfile_t; // // netfile_t // typedef struct netfile_s { anonymous memfile_t memfile; __str pcvar; int pnum; } netfile_t; // Static Functions ----------------------------------------------------------| // // PrintMem // void PrintMem(byte const *data, size_t size) { int termpos = 0; for(size_t i = 0; i < size; i++) { if(termpos + 3 > 79) { printf(c"\n"); termpos = 0; } if(isprint(data[i])) printf(c"%c ", data[i]); else printf(c"%.2X ", data[i]); termpos += 3; } printf(c"\nEOF\n\n"); } // // NetClose // // fclose for netfiles. // Output to the CVar with a Base64 representation of the output buffer. // static int NetClose(void *nfdata) { netfile_t *nf = nfdata; // If debugging, print out information about the buffer being written. if(world.dbgSave) { printf(c"NetClose: Writing netfile \"%S\" (%zub)\n", nf->pcvar, nf->pos); printf(c"Data follows\n"); PrintMem((void *)nf->mem, nf->pos); } // Base64 encode the buffer. size_t outsize; byte *coded = base64_encode((void *)nf->mem, nf->pos, &outsize); if(coded) { int cvarnum = 0; for(byte const *itr = coded; outsize; cvarnum++) { size_t itrsize; if(outsize <= SAVE_BLOCK_SIZE) itrsize = outsize; else itrsize = SAVE_BLOCK_SIZE; ACS_SetUserCVarString(nf->pnum, StrParam("%S_%i", nf->pcvar, cvarnum), StrParam("%.*s", itrsize, itr)); itr += itrsize; outsize -= itrsize; } ACS_SetUserCVarString(nf->pnum, StrParam("%S_%i", nf->pcvar, cvarnum), ""); Dalloc(coded); } Dalloc(nf->mem); Dalloc(nf); return 0; } // // MemRead // static ssize_t MemRead(void *memdata, char *buf, size_t size) { memfile_t *mem = memdata; size_t avail = mem->len - mem->pos; if(size > avail) size = avail; memcpy(buf, mem->mem + mem->pos, size); mem->pos += size; return size; } // // MemWrite // // static ssize_t MemWrite(void *memdata, char const *buf, size_t size) { memfile_t *mem = memdata; size_t avail = mem->len - mem->pos; if(size >= avail) { size_t len = mem->len + mem->len / 2 + size + 1; void *newmem = Ralloc(mem->mem, len); if(!mem) return 0; mem->len = len; mem->mem = newmem; } memcpy(mem->mem + mem->pos, buf, size); mem->mem[mem->pos += size] = '\0'; return size; } // // MemSeek // static int MemSeek(void *memdata, off_t *offset, int whence) { memfile_t *mem = memdata; size_t pos; switch(whence) { case SEEK_SET: pos = *offset; break; case SEEK_CUR: pos = *offset + mem->pos; break; case SEEK_END: pos = *offset + mem->len; break; default: return EOF; } if(pos > mem->len) return EOF; *offset = mem->pos = pos; return 0; } // // MemClose // static int MemClose(void *memdata) { memfile_t *mem = memdata; Dalloc(mem->mem); Dalloc(mem); return 0; } // Extern Functions ----------------------------------------------------------| // // Lith_NFOpen // // fopen() equivalent for netfiles. // FILE *Lith_NFOpen(int pnum, __str pcvar, char rw) { FILE *fp = null; if(rw == 'w') { netfile_t *nf = Salloc(netfile_t); nf->pcvar = pcvar; nf->pnum = pnum; fp = fopencookie(nf, c"w", (cookie_io_functions_t){ .write = MemWrite, .close = NetClose }); } else if(rw == 'r') { // Get inputs from all possible CVars. char *input = null; size_t inputsz = 0; for(int cvarnum;; cvarnum++) { __str cvar = ACS_GetUserCVarString(pnum, StrParam("%S_%i", pcvar, cvarnum)); size_t inlen = ACS_StrLen(cvar); if(inlen) { input = Ralloc(input, inputsz + inlen + 1); Lith_strcpy_str(input + inputsz, cvar); inputsz += inlen; } else break; } if(input) { // Decode the base64 input. size_t size; byte *data = base64_decode((void *)input, inputsz, &size); Dalloc(input); // If debugging, print out information about the buffer being read. if(world.dbgSave) { printf(c"Lith_NFOpen: Opening memfile \"%S\" (%zub)\n", pcvar, size); printf(c"Data follows\n"); PrintMem(data, size); } if(data) { memfile_t *mem = Salloc(memfile_t); mem->mem = data; mem->len = size; fp = fopencookie(mem, c"r", (cookie_io_functions_t){ .read = MemRead, .seek = MemSeek, .close = MemClose, }); } } } return fp; } // // Lith_FWrite32 // // Unpacks integers into a file stream. // size_t Lith_FWrite32(void const *restrict ptr, size_t count, size_t bytes, FILE *restrict fp) { size_t res = 0; for(byte const *itr = ptr; count--; res += bytes) { u32 c = *itr++; for(int i = 0; i < bytes; i++) if(fputc((c & (0xFF << (i * 8))) >> (i * 8), fp) == EOF) return res; } return res; } // // Lith_FWrite_str // size_t Lith_FWrite_str(void const __str_ars *restrict ptr, size_t count, FILE *restrict fp) { size_t res = 0; for(char const __str_ars *itr = ptr; count--; res++) if(fputc(*itr++, fp) == EOF) return res; return res; } // // Lith_FRead32 // // Reads packed integers from a file stream. // size_t Lith_FRead32(void *restrict buf, size_t count, size_t bytes, FILE *restrict fp) { size_t res = 0; for(char *itr = buf; count--;) { int c = 0, t; for(int i = 0; i < bytes; i++, res++) { if((t = fgetc(fp)) == EOF) { *itr = c; return res; } c |= (t & 0xFF) << (i * 8); } *itr++ = c; } return res; } // // W_Open // FILE *W_Open(__str fname, char const *mode) { __str content = LanguageNull(fname); if(content) return __fmemopen_str(content, ACS_StrLen(content), mode); else return null; } // EOF