/* Meta-tile optimised file storage * * Instead of storing each individual tile as a file, * bundle the 8x8 meta tile into a special meta-file. * This reduces the Inode usage and more efficient * utilisation of disk space. */ #include #include #include #include #include #include #include #include #include #include #include "store.h" #include "render_config.h" #include "dir_utils.h" #include "protocol.h" #ifdef METATILE int read_from_meta(const char *xmlconfig, int x, int y, int z, unsigned char *buf, size_t sz) { char path[PATH_MAX]; int meta_offset, fd; unsigned int pos; char header[4096]; struct meta_layout *m = (struct meta_layout *)header; size_t file_offset, tile_size; meta_offset = xyz_to_meta(path, sizeof(path), HASH_PATH, xmlconfig, x, y, z); fd = open(path, O_RDONLY); if (fd < 0) return -1; pos = 0; while (pos < sizeof(header)) { size_t len = sizeof(header) - pos; int got = read(fd, header + pos, len); if (got < 0) { close(fd); return -2; } else if (got > 0) { pos += got; } else { break; } } if (pos < sizeof(struct meta_layout)) { fprintf(stderr, "Meta file %s too small to contain header\n", path); return -3; } if (memcmp(m->magic, META_MAGIC, strlen(META_MAGIC))) { fprintf(stderr, "Meta file %s header magic mismatch\n", path); return -4; } #if 1 // Currently this code only works with fixed metatile sizes (due to xyz_to_meta above) if (m->count != (METATILE * METATILE)) { fprintf(stderr, "Meta file %s header bad count %d != %d\n", path, m->count, METATILE * METATILE); return -5; } #else if (m->count < 0 || m->count > 256) { fprintf(stderr, "Meta file %s header bad count %d\n", path, m->count); return -5; } #endif file_offset = m->index[meta_offset].offset; tile_size = m->index[meta_offset].size; if (lseek(fd, file_offset, SEEK_SET) < 0) { fprintf(stderr, "Meta file %s seek error %d\n", path, m->count); return -6; } if (tile_size > sz) { fprintf(stderr, "Truncating tile %zd to fit buffer of %zd\n", tile_size, sz); tile_size = sz; } pos = 0; while (pos < tile_size) { size_t len = tile_size - pos; int got = read(fd, buf + pos, len); if (got < 0) { close(fd); return -7; } else if (got > 0) { pos += got; } else { break; } } close(fd); return pos; } #endif int read_from_file(const char *xmlconfig, int x, int y, int z, unsigned char *buf, size_t sz) { char path[PATH_MAX]; int fd; size_t pos; xyz_to_path(path, sizeof(path), HASH_PATH, xmlconfig, x, y, z); fd = open(path, O_RDONLY); if (fd < 0) return -1; pos = 0; while (pos < sz) { size_t len = sz - pos; int got = read(fd, buf + pos, len); if (got < 0) { close(fd); return -2; } else if (got > 0) { pos += got; } else { break; } } if (pos == sz) { fprintf(stderr, "file %s truncated at %zd bytes\n", path, sz); } close(fd); return pos; } int tile_read(const char *xmlconfig, int x, int y, int z, unsigned char *buf, int sz) { #ifdef METATILE int r; r = read_from_meta(xmlconfig, x, y, z, buf, sz); if (r >= 0) return r; #endif return read_from_file(xmlconfig, x, y, z, buf, sz); } #ifdef METATILE void process_meta(const char *xmlconfig, int x, int y, int z) { int fd; int ox, oy, limit; size_t offset, pos; const int buf_len = 10 * MAX_SIZE; // To store all tiles in this .meta unsigned char *buf; struct meta_layout *m; char meta_path[PATH_MAX]; char tmp[PATH_MAX]; struct stat s; buf = (unsigned char *)malloc(buf_len); if (!buf) return; m = (struct meta_layout *)buf; offset = sizeof(struct meta_layout) + (sizeof(struct entry) * (METATILE * METATILE)); memset(buf, 0, offset); limit = (1 << z); limit = MIN(limit, METATILE); for (ox=0; ox < limit; ox++) { for (oy=0; oy < limit; oy++) { //fprintf(stderr, "Process %d/%d/%d\n", num, ox, oy); int len = read_from_file(xmlconfig, x + ox, y + oy, z, buf + offset, buf_len - offset); int mt = xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x + ox, y + oy, z); if (len <= 0) { #if 1 fprintf(stderr, "Problem reading sub tiles for metatile xml(%s) x(%d) y(%d) z(%d), got %d\n", xmlconfig, x, y, z, len); free(buf); return; #else m->index[mt].offset = 0; m->index[mt].size = 0; #endif } else { m->index[mt].offset = offset; m->index[mt].size = len; offset += len; } } } m->count = METATILE * METATILE; memcpy(m->magic, META_MAGIC, strlen(META_MAGIC)); m->x = x; m->y = y; m->z = z; xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z); snprintf(tmp, sizeof(tmp), "%s.tmp.%d", meta_path, getpid()); fd = open(tmp, O_WRONLY | O_TRUNC | O_CREAT, 0666); if (fd < 0) { fprintf(stderr, "Error creating file: %s\n", meta_path); free(buf); return; } pos = 0; while (pos < offset) { int len = write(fd, buf + pos, offset - pos); if (len < 0) { perror("Writing file"); free(buf); close(fd); return; } else if (len > 0) { pos += len; } else { break; } } close(fd); free(buf); // Reset meta timestamp to match one of the original tiles xyz_to_path(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z); if (stat(meta_path, &s) == 0) { struct utimbuf b; b.actime = s.st_atime; b.modtime = s.st_mtime; xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z); utime(tmp, &b); } rename(tmp, meta_path); printf("Produced .meta: %s\n", meta_path); // Remove raw .png's for (ox=0; ox < limit; ox++) { for (oy=0; oy < limit; oy++) { xyz_to_path(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x + ox, y + oy, z); if (unlink(meta_path)<0) perror(meta_path); } } } void process_pack(const char *name) { char meta_path[PATH_MAX]; char xmlconfig[XMLCONFIG_MAX]; int x, y, z; int meta_offset; if (path_to_xyz(name, xmlconfig, &x, &y, &z)) return; // Launch the .meta creation for only 1 tile of the whole block meta_offset = xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z); //fprintf(stderr,"Requesting x(%d) y(%d) z(%d) - mo(%d)\n", x, y, z, meta_offset); if (meta_offset == 0) process_meta(xmlconfig, x, y, z); } static void write_tile(const char *xmlconfig, int x, int y, int z, const unsigned char *buf, size_t sz) { int fd; char path[PATH_MAX]; size_t pos; xyz_to_path(path, sizeof(path), HASH_PATH, xmlconfig, x, y, z); fd = open(path, O_WRONLY | O_TRUNC | O_CREAT, 0666); if (fd < 0) { fprintf(stderr, "Error creating file: %s\n", path); return; } pos = 0; while (pos < sz) { int len = write(fd, buf + pos, sz - pos); if (len < 0) { perror("Writing file"); close(fd); return; } else if (len > 0) { pos += len; } else { break; } } close(fd); printf("Produced tile: %s\n", path); } void process_unpack(const char *name) { char meta_path[PATH_MAX]; char xmlconfig[XMLCONFIG_MAX]; int x, y, z; int ox, oy, limit; const int buf_len = 1024 * 1024; unsigned char *buf; struct stat s; // path_to_xyz is valid for meta tile names as well if (path_to_xyz(name, xmlconfig, &x, &y, &z)) return; buf = (unsigned char *)malloc(buf_len); if (!buf) return; limit = (1 << z); limit = MIN(limit, METATILE); for (ox=0; ox < limit; ox++) { for (oy=0; oy < limit; oy++) { int len = read_from_meta(xmlconfig, x + ox, y + oy, z, buf, buf_len); if (len <= 0) fprintf(stderr, "Failed to get tile x(%d) y(%d) z(%d)\n", x + ox, y + oy, z); else write_tile(xmlconfig, x + ox, y + oy, z, buf, len); } } // Grab timestamp of the meta file and update tile timestamps if (stat(name, &s) == 0) { struct utimbuf b; b.actime = s.st_atime; b.modtime = s.st_mtime; for (ox=0; ox < limit; ox++) { for (oy=0; oy < limit; oy++) { xyz_to_path(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x+ox, y+oy, z); utime(meta_path, &b); } } } // Remove the .meta file xyz_to_meta(meta_path, sizeof(meta_path), HASH_PATH, xmlconfig, x, y, z); if (unlink(meta_path)<0) perror(meta_path); } #endif