view libmlk-core/core/zfile.c @ 295:aec448037320

rpg: add support for compressed tilesets/maps
author David Demelier <markand@malikania.fr>
date Wed, 10 Mar 2021 16:40:02 +0100
parents
children f09c166fd4d4
line wrap: on
line source

/*
 * zfile.c -- load potentially ZSTD compressed file
 *
 * Copyright (c) 2020 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <zstd.h>

#include "zfile.h"

static int
is_zstd(int fd)
{
	uint32_t magic = 0;

	read(fd, &magic, sizeof (magic));
	lseek(fd, 0, SEEK_SET);

#if SDL_BYTEORDER != SDL_LIL_ENDIAN
	magic = SDL_Swap32(magic);
#endif

	return magic == ZSTD_MAGICNUMBER;
}

static int
decompress(int fd, struct zfile *zf)
{
	char *in = NULL;
	unsigned long long datasz;
	struct stat st;

	/* Load uncompressed data. */
	if (fstat(fd, &st) < 0)
		goto fail;
	if (!(in = calloc(1, st.st_size)) || read(fd, in, st.st_size) != st.st_size)
		goto fail;

	switch ((datasz = ZSTD_getFrameContentSize(in, st.st_size))) {
	case ZSTD_CONTENTSIZE_ERROR:
		errno = EINVAL;
		goto fail;
	case ZSTD_CONTENTSIZE_UNKNOWN:
		errno = ENOTSUP;
		goto fail;
	default:
		break;
	}

	/* Finally decompress. */
	if (!(zf->data = calloc(1, datasz + 1)))
		goto fail;
	if (ZSTD_isError(ZSTD_decompress(zf->data, datasz, in, st.st_size))) {
		errno = EINVAL;
		goto fail;
	}
	if (!(zf->fp = fmemopen(zf->data, datasz, "r")))
		goto fail;

	close(fd);
	free(in);

	return 0;

fail:
	close(fd);
	free(zf->data);
	free(in);

	return -1;
}

static int
reopen(int fd, struct zfile *zf)
{
	if (!(zf->fp = fdopen(fd, "r")))
		return close(fd), -1;

	return 0;
}

int
zfile_open(struct zfile *zf, const char *path)
{
	assert(zf);
	assert(path);

	int fd, status;

	memset(zf, 0, sizeof (*zf));

	if ((fd = open(path, O_RDONLY)) < 0)
		return -1;

	return is_zstd(fd) ? decompress(fd, zf) : reopen(fd, zf);
}

void
zfile_close(struct zfile *zf)
{
	assert(zf);

	free(zf->data);
	fclose(zf->fp);
	memset(zf, 0, sizeof (*zf));
}