#include "source.h" #include #include #include #include #include static void lf_free(loaded_file_t *lf) { if(lf) { free(lf->path); buffer_free(lf->buf); } free(lf); } static ssize_t lf_read(void *_cookie, void *buf, off_t offset, size_t size) { loaded_file_t *lf = _cookie; if(offset < 0 || offset >= (int)lf->buf->data_size) return -1; if(offset + size > lf->buf->data_size) size = lf->buf->data_size - offset; memcpy(buf, lf->buf->mem + offset, size); return size; } static bool lf_write(void *_cookie, void *data, size_t data_size, off_t offset, size_t segment_size) { loaded_file_t *lf = _cookie; if(buffer_write(lf->buf, data, data_size, offset, segment_size)) { lf->dirty = true; return true; } return false; } static size_t lf_size(void *_cookie) { loaded_file_t *lf = _cookie; return lf->buf->data_size; } static bool lf_dirty(void *_cookie) { loaded_file_t *lf = _cookie; return lf->dirty; } static bool lf_save_switch(loaded_file_t *lf, char const *outfile) { if(!lf->dirty && outfile == NULL) return true; /* If the new file is larger than the original, we can just overwrite it without truncating, which is much faster with Fugue */ int flags = O_CREAT | O_WRONLY | O_TRUNC; if(outfile == NULL && lf->buf->data_size >= lf->original_size) flags = O_CREAT | O_WRONLY; int fd = open(outfile ? outfile : lf->path, flags); if(!fd) goto fail; ssize_t rc = write(fd, lf->buf->mem, lf->buf->data_size); if(rc < 0 || (size_t)rc != lf->buf->data_size) goto fail; close(fd); lf->dirty = false; return true; fail: close(fd); return false; } static bool lf_save(void *_cookie, char const *outfile) { return gint_world_switch(GINT_CALL(lf_save_switch, _cookie, outfile)); } static void lf_close(void *_cookie) { loaded_file_t *lf = _cookie; lf_free(lf); } static source_intf_t lf_intf = { .name = "Loaded File", .close_on_return_to_menu = false, .read = lf_read, .write = lf_write, .size = lf_size, .dirty = lf_dirty, .save = lf_save, .close = lf_close, }; source_t *source_loaded_file_open(char const *path) { int fd = -1; source_t *source = NULL; loaded_file_t *lf = NULL; lf = calloc(1, sizeof *lf); if(!lf) goto fail; lf->path = strdup(path); if(!lf->path) goto fail; fd = open(path, O_RDONLY); if(fd < 0) goto fail; int size = lseek(fd, 0, SEEK_END); if(size < 0) goto fail; lf->original_size = size; lf->buf = buffer_create(size, 4096); if(!lf->buf) goto fail; lseek(fd, 0, SEEK_SET); ssize_t rc = read(fd, lf->buf->mem, size); lf->buf->data_size = size; if(rc != size) goto fail; source = source_open(&lf_intf, lf, path); if(!source) goto fail; source->cap = SOURCE_WRITE | SOURCE_RESIZE | SOURCE_SAVEAS; close(fd); return source; fail: free(source); close(fd); lf_free(lf); return NULL; } source_t *source_loaded_file_new(void) { source_t *source = NULL; loaded_file_t *lf = NULL; lf = calloc(1, sizeof *lf); if(!lf) goto fail; lf->original_size = 0; lf->buf = buffer_create(4096, 4096); if(!lf->buf) goto fail; lf->buf->data_size = 0; source = source_open(&lf_intf, lf, ""); if(!source) goto fail; source->no_origin = true; source->cap = SOURCE_WRITE | SOURCE_RESIZE | SOURCE_SAVEAS; return source; fail: free(source); lf_free(lf); return NULL; }