taisei/src/rwops/rwops_segment.c
Andrei Alexeyev 513d613387
Consistent indentation: indent with tabs, align with spaces (#104)
I would've preferred to just go with 4-spaces for indent and no tabs,
but lao is a bit conservative about it. :^)

Still, this is a ton better than mixing different styles all over the
place, especially within the same file.
2018-01-12 20:26:07 +02:00

205 lines
3.9 KiB
C

/*
* This software is licensed under the terms of the MIT-License
* See COPYING for further information.
* ---
* Copyright (c) 2011-2018, Lukas Weber <laochailan@web.de>.
* Copyright (c) 2012-2018, Andrei Alexeyev <akari@alienslab.net>.
*/
#include "taisei.h"
#include <stdbool.h>
#include "rwops_segment.h"
#include "util.h"
typedef struct Segment {
SDL_RWops *wrapped;
size_t start;
size_t end;
int64_t pos; // fallback for non-seekable streams
bool autoclose;
} Segment;
#define SEGMENT(rw) ((Segment*)((rw)->hidden.unknown.data1))
static int64_t segment_seek(SDL_RWops *rw, int64_t offset, int whence) {
Segment *s = SEGMENT(rw);
switch(whence) {
case RW_SEEK_CUR: {
if(offset) {
int64_t pos = SDL_RWtell(s->wrapped);
if(pos < 0) {
return pos;
}
if(pos + offset > s->end) {
offset = s->end - pos;
} else if(pos + offset < s->start) {
offset = s->start - pos;
}
}
break;
}
case RW_SEEK_SET: {
offset += s->start;
if(offset > s->end) {
offset = s->end;
}
break;
}
case RW_SEEK_END: {
int64_t size = SDL_RWsize(s->wrapped);
if(size < 0) {
return size;
}
if(size > s->end) {
offset -= size - s->end;
}
if(size + offset < s->start) {
offset += s->start - (size + offset);
}
break;
}
default: {
SDL_SetError("Bad whence value %i", whence);
return -1;
}
}
int64_t result = SDL_RWseek(s->wrapped, offset, whence);
if(result > 0) {
if(s->start > result) {
result = 0;
} else {
result -= s->start;
}
}
return result;
}
static int64_t segment_size(SDL_RWops *rw) {
Segment *s = SEGMENT(rw);
int64_t size = SDL_RWsize(s->wrapped);
if(size < 0) {
return size;
}
if(size > s->end) {
size = s->end;
}
return size - s->start;
}
static size_t segment_readwrite(SDL_RWops *rw, void *ptr, size_t size, size_t maxnum, bool write) {
Segment *s = SEGMENT(rw);
int64_t pos = SDL_RWtell(s->wrapped);
size_t onum;
if(pos < 0) {
log_debug("SDL_RWtell failed (%i): %s", (int)pos, SDL_GetError());
SDL_SetError("segment_readwrite: SDL_RWtell failed (%i) %s", (int)pos, SDL_GetError());
// this could be a non-seekable stream, like /dev/stdin...
// let's assume nothing else uses the wrapped stream and try to guess the current position
// this only works if the actual positon in the stream at the time of segment creation matched s->start...
pos = s->pos;
} else {
s->pos = pos;
}
if(pos < s->start || pos > s->end) {
log_warn("Segment range violation");
SDL_SetError("segment_readwrite: segment range violation");
return 0;
}
int64_t maxsize = s->end - pos;
while(size * maxnum > maxsize) {
if(!--maxnum) {
return 0;
}
}
if(write) {
onum = SDL_RWwrite(s->wrapped, ptr, size, maxnum);
} else {
onum = SDL_RWread(s->wrapped, ptr, size, maxnum);
}
s->pos += onum / size;
assert(s->pos <= s->end);
return onum;
}
static size_t segment_read(SDL_RWops *rw, void *ptr, size_t size, size_t maxnum) {
return segment_readwrite(rw, ptr, size, maxnum, false);
}
static size_t segment_write(SDL_RWops *rw, const void *ptr, size_t size, size_t maxnum) {
return segment_readwrite(rw, (void*)ptr, size, maxnum, true);
}
static int segment_close(SDL_RWops *rw) {
if(rw) {
Segment *s = SEGMENT(rw);
if(s->autoclose) {
SDL_RWclose(s->wrapped);
}
free(s);
SDL_FreeRW(rw);
}
return 0;
}
SDL_RWops* SDL_RWWrapSegment(SDL_RWops *src, size_t start, size_t end, bool autoclose) {
assert(end > start);
SDL_RWops *rw = SDL_AllocRW();
if(!rw) {
return NULL;
}
memset(rw, 0, sizeof(SDL_RWops));
rw->type = SDL_RWOPS_UNKNOWN;
rw->seek = segment_seek;
rw->size = segment_size;
rw->read = segment_read;
rw->write = segment_write;
rw->close = segment_close;
Segment *s = malloc(sizeof(Segment));
s->wrapped = src;
s->start = start;
s->end = end;
s->autoclose = autoclose;
// fallback for non-seekable streams
s->pos = start;
rw->hidden.unknown.data1 = s;
return rw;
}