449 lines
7.4 KiB
C
449 lines
7.4 KiB
C
/************************************************************************
|
|
* file.c
|
|
* voxelands - 3d voxel world sandbox game
|
|
* Copyright (C) Lisa 'darkrose' Milne 2016 <lisa@ltmnet.com>
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
|
************************************************************************/
|
|
|
|
#include "common.h"
|
|
|
|
#include "file.h"
|
|
#include "path.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <stdarg.h>
|
|
|
|
#define FILE_BUFF_STEP 1024
|
|
|
|
static int file_extend(file_t *file, int min)
|
|
{
|
|
unsigned char* b;
|
|
int inc = min;
|
|
int ttl;
|
|
if (min < FILE_BUFF_STEP)
|
|
inc = FILE_BUFF_STEP;
|
|
ttl = file->size+inc;
|
|
|
|
b = realloc(file->data,ttl);
|
|
if (!b)
|
|
return 0;
|
|
|
|
file->data = b;
|
|
file->size = ttl;
|
|
|
|
return inc;
|
|
}
|
|
|
|
/* load a file into memory */
|
|
file_t *file_load(char* type, char* name)
|
|
{
|
|
file_t *ft;
|
|
FILE *f;
|
|
char* fn;
|
|
char* path;
|
|
|
|
if (type) {
|
|
fn = name;
|
|
path = path_get(type,name,1,NULL,0);
|
|
}else{
|
|
path = strdup(name);
|
|
fn = strrchr(name,'/');
|
|
if (!fn)
|
|
return NULL;
|
|
fn++;
|
|
}
|
|
|
|
if (!path)
|
|
return NULL;
|
|
|
|
f = fopen(path, "rb");
|
|
if (!f) {
|
|
free(path);
|
|
return NULL;
|
|
}
|
|
|
|
ft = malloc(sizeof(file_t));
|
|
if (!ft) {
|
|
free(path);
|
|
fclose(f);
|
|
return NULL;
|
|
}
|
|
|
|
ft->pos = 0;
|
|
ft->modified = 0;
|
|
|
|
fseek(f, 0, SEEK_END);
|
|
ft->len = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
|
|
ft->size = ft->len+1;
|
|
|
|
ft->data = malloc(ft->size);
|
|
if (!ft->data) {
|
|
free(path);
|
|
fclose(f);
|
|
free(ft);
|
|
return NULL;
|
|
}
|
|
|
|
if (ft->len != fread(ft->data, 1, ft->len, f)) {
|
|
free(path);
|
|
fclose(f);
|
|
free(ft->data);
|
|
free(ft);
|
|
return NULL;
|
|
}
|
|
|
|
/* this lets us do string operations on text files */
|
|
ft->data[ft->len] = 0;
|
|
|
|
fclose(f);
|
|
|
|
ft->name = strdup(fn);
|
|
ft->path = path;
|
|
|
|
return ft;
|
|
}
|
|
|
|
/* load a file into memory */
|
|
file_t *file_create(char* type, char *name)
|
|
{
|
|
file_t *ft;
|
|
char* fn;
|
|
char* path;
|
|
|
|
if (name) {
|
|
if (type) {
|
|
fn = name;
|
|
path = path_get(type,name,0,NULL,0);
|
|
}else{
|
|
path = strdup(name);
|
|
fn = strrchr(name,'/');
|
|
if (!fn)
|
|
return NULL;
|
|
fn++;
|
|
}
|
|
}else{
|
|
fn = NULL;
|
|
path = NULL;
|
|
}
|
|
|
|
ft = malloc(sizeof(file_t));
|
|
if (!ft) {
|
|
free(path);
|
|
return NULL;
|
|
}
|
|
|
|
ft->pos = 0;
|
|
ft->len = 0;
|
|
ft->size = 0;
|
|
ft->modified = 0;
|
|
|
|
ft->data = NULL;
|
|
|
|
if (fn)
|
|
ft->name = strdup(fn);
|
|
ft->path = path;
|
|
|
|
return ft;
|
|
}
|
|
|
|
/* free a loaded file */
|
|
void file_free(file_t *file)
|
|
{
|
|
if (!file)
|
|
return;
|
|
|
|
if (file->data)
|
|
free(file->data);
|
|
|
|
if (file->name)
|
|
free(file->name);
|
|
|
|
free(file);
|
|
}
|
|
|
|
/* flush file data to disk */
|
|
void file_flush(file_t *file)
|
|
{
|
|
FILE *f;
|
|
if (!file || !file->modified || !file->path)
|
|
return;
|
|
|
|
if (!path_exists(file->path)) {
|
|
if (!path_create(NULL,file->path))
|
|
return;
|
|
}
|
|
|
|
f = fopen(file->path, "wb");
|
|
if (!f)
|
|
return;
|
|
|
|
if (file->len) {
|
|
if (fwrite(file->data,1,file->len,f) != file->len) {
|
|
fclose(f);
|
|
return;
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
file->modified = 0;
|
|
}
|
|
|
|
/* find the next occurance of value from offset in file, return it's position
|
|
* relative to offset */
|
|
int file_find(file_t *file, int offset, unsigned char value)
|
|
{
|
|
int r;
|
|
if (!file)
|
|
return -1;
|
|
|
|
for (r=offset; r<file->len; r++) {
|
|
if (file->data[r] == value)
|
|
return (r-offset);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
/* find the next occurance of value from offset in file, return it's position
|
|
* relative to offset */
|
|
int file_strfind(file_t *file, int offset, char* value)
|
|
{
|
|
int r;
|
|
char* v;
|
|
if (!file || file->len <= offset)
|
|
return -1;
|
|
|
|
v = strstr((char*)(file->data+offset),value);
|
|
if (!v)
|
|
return -1;
|
|
|
|
r = (v-(char*)file->data);
|
|
if (r < 0)
|
|
return -1;
|
|
|
|
return (r-offset);
|
|
}
|
|
|
|
/* read from a file buffer to dst */
|
|
int file_read(file_t *file, void* dst, int size)
|
|
{
|
|
int len;
|
|
if (!file || !file->len)
|
|
return -1;
|
|
|
|
len = file->len - file->pos;
|
|
if (size < len) {
|
|
len = size;
|
|
}
|
|
|
|
if (len > 0) {
|
|
memcpy(dst, file->data+file->pos, len);
|
|
file->pos += len;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
/* read a line from a file buffer to dst */
|
|
int file_readline(file_t *file, char* dst, int size)
|
|
{
|
|
int len;
|
|
char* b;
|
|
char* e;
|
|
if (!file || !file->len)
|
|
return -1;
|
|
if (file->pos >= file->len)
|
|
return -1;
|
|
|
|
b = (char*)(file->data+file->pos);
|
|
|
|
e = strchr(b,'\n');
|
|
if (e) {
|
|
len = (e-b);
|
|
file->pos += len+1;
|
|
}else{
|
|
len = strlen(b);
|
|
file->pos += len;
|
|
}
|
|
|
|
if (len >= size)
|
|
len = size-1;
|
|
|
|
memcpy(dst, b, len);
|
|
dst[len] = 0;
|
|
|
|
if (file->pos > file->len)
|
|
file->pos = file->len;
|
|
|
|
return len;
|
|
}
|
|
|
|
/* read integer from a file buffer */
|
|
int file_read_int(file_t *file)
|
|
{
|
|
int32_t r;
|
|
file_read(file,&r,4);
|
|
return r;
|
|
}
|
|
|
|
/* read short integer from a file buffer */
|
|
int16_t file_read_short(file_t *file)
|
|
{
|
|
int16_t r;
|
|
file_read(file,&r,2);
|
|
return r;
|
|
}
|
|
|
|
/* read char from a file buffer */
|
|
char file_read_char(file_t *file)
|
|
{
|
|
char r = 0;
|
|
file_read(file,&r,1);
|
|
return r;
|
|
}
|
|
|
|
/* read unsigned integer from a file buffer */
|
|
uint32_t file_read_uint(file_t *file)
|
|
{
|
|
uint32_t r;
|
|
file_read(file,&r,4);
|
|
return r;
|
|
}
|
|
|
|
/* read float from a file buffer */
|
|
float file_read_float(file_t *file)
|
|
{
|
|
float r;
|
|
file_read(file,&r,4);
|
|
return r;
|
|
}
|
|
|
|
/* seek to a position in a file buffer */
|
|
int file_seek(file_t *file, int offset, int origin)
|
|
{
|
|
if (!file)
|
|
return -1;
|
|
|
|
switch (origin) {
|
|
case SEEK_SET:
|
|
if (file->len < offset)
|
|
offset = file->len;
|
|
|
|
file->pos = offset;
|
|
break;
|
|
case SEEK_CUR:
|
|
offset += file->pos;
|
|
if (file->len < offset)
|
|
offset = file->len;
|
|
|
|
file->pos = offset;
|
|
break;
|
|
case SEEK_END:
|
|
file->pos = file->len;
|
|
break;
|
|
default:;
|
|
};
|
|
|
|
if (file->pos < 0)
|
|
file->pos = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* get the position in a file buffer */
|
|
int file_tell(file_t *file)
|
|
{
|
|
if (!file)
|
|
return -1;
|
|
|
|
return file->pos;
|
|
}
|
|
|
|
/* get the pointer to the current file position */
|
|
void *file_get(file_t *file)
|
|
{
|
|
return file->data+file->pos;
|
|
}
|
|
|
|
/* write data to a file buffer */
|
|
int file_write(file_t *file, void *buff, int size)
|
|
{
|
|
if (!file || !buff || size)
|
|
return -1;
|
|
|
|
if (file->size <= (file->pos+size+1)) {
|
|
int inc = file->pos+size+2;
|
|
if (file_extend(file,inc) < inc)
|
|
return 0;
|
|
}
|
|
|
|
if (!memcpy(file->data+file->pos,buff,size)) {
|
|
file->data[file->pos] = 0;
|
|
return 0;
|
|
}
|
|
|
|
file->pos += size;
|
|
if (file->pos >= file->len) {
|
|
file->len = file->pos;
|
|
file->data[file->pos] = 0;
|
|
}
|
|
file->modified = 1;
|
|
|
|
return size;
|
|
}
|
|
|
|
/* write a formatted string to a file buffer (printf style) */
|
|
int file_writef(file_t *file, char* fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int l;
|
|
int s;
|
|
if (!file)
|
|
return -1;
|
|
|
|
if (!file->size || !file->data) {
|
|
if (file_extend(file,FILE_BUFF_STEP) <= 0)
|
|
return 0;
|
|
}
|
|
|
|
/* basically "keep trying till it all fits" */
|
|
while (1) {
|
|
va_start(ap, fmt);
|
|
s = (file->size-1)-file->pos;
|
|
l = vsnprintf((char*)(file->data+file->pos), s, fmt, ap);
|
|
va_end(ap);
|
|
if (l < s)
|
|
break;
|
|
if (file_extend(file,s+FILE_BUFF_STEP) <= 0) {
|
|
if (file->data)
|
|
file->data[file->len] = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
file->pos += l;
|
|
if (file->pos >= file->len) {
|
|
file->len = file->pos;
|
|
file->data[file->pos] = 0;
|
|
}
|
|
file->modified = 1;
|
|
|
|
return l;
|
|
}
|