/* vi: set ts=2: +-------------------+ Enno Rehling | Eressea PBEM host | Christian Schlittchen | (c) 1998 - 2008 | Katja Zedel +-------------------+ This program may not be used, modified or distributed without prior permission by the authors of Eressea. */ #include #include "config.h" #include "textstore.h" #include "save.h" #include "version.h" #include #include #include #include #include #include #ifdef HAVE_LIBXML #include #endif #define file(store) (FILE *)((store)->userdata) #define STREAM_VERSION 2 INLINE_FUNCTION size_t pack_int(int v, char * buffer) { int sign = (v<0); if (sign) { v = ~v + 1; sign = 0x40; } if (v<0x40) { buffer[0] = (char)(v | sign); return 1; } else if (v<0x2000) { buffer[0] = (char)((v>> 6) | 0x80); buffer[1] = (char)((v & 0x3F) | sign); return 2; } else if (v<0x100000) { buffer[0] = (char)(((v>>13) & 0x7f) | 0x80); buffer[1] = (char)(((v>> 6) & 0x7f) | 0x80); buffer[2] = (char)((v & 0x3F) | sign); return 3; } else if (v<0x8000000) { buffer[0] = (char)(((v>>20) & 0x7f) | 0x80); buffer[1] = (char)(((v>>13) & 0x7f) | 0x80); buffer[2] = (char)(((v>> 6) & 0x7f) | 0x80); buffer[3] = (char)((v & 0x3F) | sign); return 4; } buffer[0] = (char)(((v>>27) & 0x7f) | 0x80); buffer[1] = (char)(((v>>20) & 0x7f) | 0x80); buffer[2] = (char)(((v>>13) & 0x7f) | 0x80); buffer[3] = (char)(((v>> 6) & 0x7f) | 0x80); buffer[4] = (char)((v & 0x3F) | sign); return 5; } INLINE_FUNCTION int unpack_int(const char * buffer) { int i = 0, v = 0; while (buffer[i] & 0x80) { v = (v << 7) | (buffer[i++] & 0x7f); } v = (v << 6) | (buffer[i] & 0x3f); if (buffer[i] & 0x40) { v = ~v + 1; } return v; } static int bin_w_brk(struct storage * store) { return 0; } static int bin_w_int_pak(struct storage * store, int arg) { char buffer[5]; size_t size = pack_int(arg, buffer); return (int)fwrite(buffer, sizeof(char), size, file(store)); } static int bin_r_int_pak(struct storage * store) { int v = 0; char ch; fread(&ch, sizeof(char), 1, file(store)); while (ch & 0x80) { v = (v << 7) | (ch & 0x7f); fread(&ch, sizeof(char), 1, file(store)); } v = (v << 6) | (ch & 0x3f); if (ch & 0x40) { v = ~v + 1; } return v; } static int bin_w_int(struct storage * store, int arg) { return (int)fwrite(&arg, sizeof(arg), 1, file(store)); } static int bin_r_int(struct storage * store) { int result; fread(&result, sizeof(result), 1, file(store)); return result; } static int bin_w_flt(struct storage * store, float arg) { return (int)fwrite(&arg, sizeof(arg), 1, file(store)); } static float bin_r_flt(struct storage * store) { float result; fread(&result, sizeof(result), 1, file(store)); return result; } static int bin_w_str(struct storage * store, const char * tok) { int result; if (tok==NULL || tok[0]==0) { result = store->w_int(store, 0); } else { int size = (int)strlen(tok); result = store->w_int(store, size); result += (int)fwrite(tok, size, 1, file(store)); } return result; } #define FIX_INVALID_CHARS /* required for data pre-574 */ static char * bin_r_str(struct storage * store) { int len; len = store->r_int(store); if (len>=0) { char * result = malloc(len+1); fread(result, sizeof(char), len, file(store)); result[len] = 0; #ifdef FIX_INVALID_CHARS { char * p = strpbrk(result, "\n\r"); while (p) { log_error(("Invalid character %d in input string \"%s\".\n", *p, result)); strcpy(p, p+1); p = strpbrk(p, "\n\r"); } } #endif return result; } else if (len<0) { log_error(("invalid string-length %d in input.\n", len)); } return NULL; } static void bin_r_str_buf(struct storage * store, char * result, size_t size) { int i; size_t rd, len; i = store->r_int(store); assert(i>=0); if (i==0) { result[0] = 0; } else { len = (size_t)i; rd = MIN(len, size-1); fread(result, sizeof(char), rd, file(store)); if (rdw_int(store, len); if (len>0) { result += (int)fwrite(arg, len, 1, file(store)); } return result; } static void bin_r_bin(struct storage * store, void * result, size_t size) { int len = store->r_int(store); if (len>0) { if ((size_t)len>size) { log_error(("destination buffer too small %d %u.\n", len, size)); fseek(file(store), len, SEEK_CUR); } else { fread(result, len, 1, file(store)); } } } static int bin_open(struct storage * store, const char * filename, int mode) { const char * modes[] = { 0, "rb", "wb", "ab" }; FILE * F = fopen(filename, modes[mode]); store->userdata = F; store->encoding = ENCODING_UTF8; /* always utf8 it is */ if (F) { if (mode==IO_READ) { int stream_version = 0; store->version = bin_r_int(store); if (store->version>=INTPAK_VERSION) { stream_version = bin_r_int(store); } if (stream_version<=1) { store->r_id = bin_r_int; store->w_id = bin_w_int; } if (stream_version==0) { store->r_int = bin_r_int; store->w_int = bin_w_int; } } else if (store->encoding==ENCODING_UTF8) { bin_w_int(store, RELEASE_VERSION); bin_w_int(store, STREAM_VERSION); } } return (F==NULL); } static int bin_close(struct storage * store) { return fclose(file(store)); } const storage binary_store = { bin_w_brk, /* newline (ignore) */ bin_w_int_pak, bin_r_int_pak, /* int storage */ bin_w_flt, bin_r_flt, /* float storage */ bin_w_int_pak, bin_r_int_pak, /* id storage */ bin_w_str, bin_r_str, bin_r_str_buf, /* token storage */ bin_w_str, bin_r_str, bin_r_str_buf, /* string storage */ bin_w_bin, bin_r_bin, /* binary storage */ bin_open, bin_close, 0, 0, NULL };