233 lines
4.8 KiB
C
233 lines
4.8 KiB
C
#include <container.h>
|
|
#include <gfx.h>
|
|
#include <sfx.h>
|
|
#include <errno.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static int readstr(char *const str, const size_t n, FILE *const f)
|
|
{
|
|
int ret = -1;
|
|
size_t i = 0;
|
|
|
|
do
|
|
{
|
|
if (i >= n)
|
|
{
|
|
fprintf(stderr, "%s: string too long (> %zu bytes): %*.s\n",
|
|
__func__, n, (int)n, str);
|
|
goto end;
|
|
}
|
|
else if (!fread(&str[i], sizeof *str, 1, f))
|
|
{
|
|
fprintf(stderr, "%s: expected null-terminated string\n", __func__);
|
|
goto end;
|
|
}
|
|
} while (str[i++]);
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
static int get_file_size(size_t *const sz, FILE *const f)
|
|
{
|
|
char szstr[sizeof "2147483647"];
|
|
|
|
if (readstr(szstr, sizeof szstr, f))
|
|
return -1;
|
|
|
|
errno = 0;
|
|
char *end;
|
|
const unsigned long val = strtoul(szstr, &end, 10);
|
|
|
|
if (errno)
|
|
{
|
|
fprintf(stderr, "%s: strtoul(3): %s\n", __func__, strerror(errno));
|
|
return -1;
|
|
}
|
|
else if (*end)
|
|
{
|
|
fprintf(stderr, "%s: invalid number %s\n", __func__, szstr);
|
|
return -1;
|
|
}
|
|
|
|
*sz = val;
|
|
return 0;
|
|
}
|
|
|
|
static int read_file_contents(const struct container *const el, FILE *const f,
|
|
const long init_off, const size_t sz)
|
|
{
|
|
int ret = -1;
|
|
|
|
switch (el->type)
|
|
{
|
|
case CONTAINER_TYPE_SPRITE:
|
|
if (sprite_from_fp(el->data.sprite, f))
|
|
goto end;
|
|
|
|
break;
|
|
|
|
case CONTAINER_TYPE_SOUND:
|
|
if (sfx_sound_from_fp(el->data.sound, f, sz))
|
|
goto end;
|
|
|
|
break;
|
|
}
|
|
|
|
const long off = ftell(f);
|
|
|
|
if (off < 0)
|
|
{
|
|
fprintf(stderr, "%s:%d: fseek failed: %s\n",
|
|
__func__, __LINE__, strerror(errno));
|
|
goto end;
|
|
}
|
|
else if (off > init_off + sz)
|
|
{
|
|
fprintf(stderr, "%s: %s: detected read past file contents\n",
|
|
__func__, el->path);
|
|
goto end;
|
|
}
|
|
else if (off != init_off + sz)
|
|
{
|
|
fprintf(stderr, "only %ld bytes read, %ld expected\n",
|
|
off, init_off + sz);
|
|
goto end;
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
static const struct container *find_element(const struct container *const list,
|
|
const size_t n, FILE *const f)
|
|
{
|
|
char name[128];
|
|
|
|
if (readstr(name, sizeof name, f))
|
|
return NULL;
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
const struct container *const el = &list[i];
|
|
|
|
if (!strcmp(el->path, name))
|
|
return el;
|
|
}
|
|
|
|
fprintf(stderr, "file %s not found on the list\n", name);
|
|
return NULL;
|
|
}
|
|
|
|
static int read_element(const struct container *const list, const size_t n,
|
|
FILE *const f, bool *const done)
|
|
{
|
|
long init_off;
|
|
const struct container *el = NULL;
|
|
size_t sz;
|
|
|
|
if (!(el = find_element(list, n, f))
|
|
|| get_file_size(&sz, f))
|
|
return -1;
|
|
else if ((init_off = ftell(f)) < 0)
|
|
{
|
|
fprintf(stderr, "%s:%d: fseek failed: %s\n",
|
|
__func__, __LINE__, strerror(errno));
|
|
return -1;
|
|
}
|
|
else if (read_file_contents(el, f, init_off, sz))
|
|
return -1;
|
|
|
|
done[el - list] = true;
|
|
return 0;
|
|
}
|
|
|
|
static int read_all_elements(const struct container *const list, const size_t n,
|
|
FILE *const f)
|
|
{
|
|
int ret = -1;
|
|
/* VLAs are generally frowned upon, but we can safely assume
|
|
* 'n' is reasonably low. */
|
|
bool done[n];
|
|
|
|
memset(&done, 0, sizeof done);
|
|
|
|
for (size_t i = 0; i < sizeof done / sizeof *done; i++)
|
|
{
|
|
if (feof(f) || ferror(f))
|
|
{
|
|
fprintf(stderr, "feof or ferror found\n");
|
|
goto end;
|
|
}
|
|
else if (read_element(list, n, f, done))
|
|
goto end;
|
|
}
|
|
|
|
for (size_t i = 0; i < sizeof done / sizeof *done; i++)
|
|
{
|
|
if (!done[i])
|
|
{
|
|
fprintf(stderr, "%s: %s not found inside container file\n",
|
|
__func__, list[i].path);
|
|
goto end;
|
|
}
|
|
}
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
return ret;
|
|
}
|
|
|
|
void container_free(const struct container *const list, const size_t n)
|
|
{
|
|
if (!list)
|
|
return;
|
|
|
|
for (size_t i = 0; i < n; i++)
|
|
{
|
|
const struct container *const el = &list[i];
|
|
|
|
switch (el->type)
|
|
{
|
|
case CONTAINER_TYPE_SPRITE:
|
|
sprite_free(el->data.sprite);
|
|
break;
|
|
|
|
case CONTAINER_TYPE_SOUND:
|
|
sfx_free(el->data.sound);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
int container_load_ex(const char *const path, const struct container *const list,
|
|
const size_t n)
|
|
{
|
|
int ret = -1;
|
|
FILE *f = NULL;
|
|
|
|
if (!(f = fopen(path, "rb")))
|
|
{
|
|
fprintf(stderr, "could not open %s: %s\n", path, strerror(errno));
|
|
goto end;
|
|
}
|
|
else if (read_all_elements(list, n, f))
|
|
goto end;
|
|
|
|
ret = 0;
|
|
|
|
end:
|
|
if (f)
|
|
fclose(f);
|
|
|
|
return ret;
|
|
}
|