/* 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 #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 (rd < len) { fseek(file(store), (long)(len - rd), SEEK_CUR); result[size - 1] = 0; } else { 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 } } static int bin_w_bin(struct storage *store, void *arg, size_t size) { int result; int len = (int)size; result = store->w_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 = XML_CHAR_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 == XML_CHAR_ENCODING_UTF8) { store->version = RELEASE_VERSION; 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 };