#include <sys/stat.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <time.h>
#include <unistd.h>
#include "lootsack.h"
extern int verbose;
struct treasure_type_limits limits[NUM_TREASURE_TYPES] = {
{ 1, 1, 1, 1 }, /* COIN */
{ 10, 300, 10, 75 }, /* CROWN */
{ 2, 15, 7, 20 }, /* DAGGER */
{ 1, 1500, 1, 3 }, /* GEM */
{ 1, 1200, 3, 15 }, /* NECKLACE */
{ 1, 100, 2, 3 }, /* RING */
{ 3, 1000, 100, 1000 }, /* SWORD */
};
const char *type_names[NUM_TREASURE_TYPES] = {
"coin",
"crown",
"dagger",
"gem",
"necklace",
"ring",
"sword",
};
const char *type_code_names[NUM_TREASURE_TYPES] = {
"COIN",
"CROWN",
"DAGGER",
"GEM",
"NECKLACE",
"RING",
"SWORD",
};
uint16_t type_frequency[NUM_TREASURE_TYPES] = {
/* If a random int between 1 and 1000 is selected, it will be compared
* against these values, in order. The first value it finds greater than
* or equal to itself will be selected. */
900, /* "COIN", */
991, /* "CROWN", */
994, /* "DAGGER", */
995, /* "GEM", */
996, /* "NECKLACE", */
997, /* "RING", */
1000, /* "SWORD", */
};
static enum treasure_type match_treasure_type(const char *name);
void
print_treasure_item(struct treasure *t)
{
printf("%8s weighing %4u and worth %4u gold pieces.\n",
type_names[t->t], t->w, t->v);
}
void
print_treasure(struct treasure *all_treasure, size_t n)
{
size_t i;
struct treasure *t;
for (i = 0; i < n; i++) {
t = all_treasure + i;
print_treasure_item(t);
}
}
void print_stats(struct treasure *all_treasure, size_t n)
{
size_t i;
struct treasure *t;
for (i = 0; i < n; i++) {
t = all_treasure + i;
printf("%03zu %8s w: %4u, v, %4u rel w/v: %7u.\n",
i, type_names[t->t], t->w, t->v, SCALED_W_V_RATIO(t));
}
}
int
write_treasure_csv(int fd, struct treasure *items, size_t n)
{
FILE *f;
struct treasure *t;
size_t i;
if (n > TREASURE_ITEM_MAX)
return -1;
if ((f = fdopen(fd, "w")) == NULL)
err(1, "write_treasure_csv:fdopen");
if (verbose)
fprintf(stderr, "Writing CSV header line,\n");
fprintf(f, "TYPE,WEIGHT,VALUE\n");
for (i = 0; i < n; i++) {
t = items + i;
fprintf(f, "%s,%u,%u\n", type_code_names[t->t], t->w, t->v);
}
if (verbose)
fprintf(stderr, "Wrote %zu CSV entries.\n", i);
return i;
}
int
write_treasure_bin(int fd, struct treasure *t, size_t n)
{
struct treasure_file_header h;
struct timespec now;
size_t i;
size_t expect;
ssize_t nb;
if (n > TREASURE_ITEM_MAX)
return -1;
if (clock_gettime(CLOCK_REALTIME, &now) == -1)
err(1, "clock_gettime");
h.created_on = now;
h.num_entries = n;
h.version = TREASURE_FILE_VERSION;
/* write the header */
i = -1;
expect = sizeof(struct treasure_file_header);
if ((nb = write(fd, &h, expect)) == -1 || nb == 0)
goto cleanup;
i = 0;
expect = sizeof(struct treasure);
while (i < n) {
if ((nb = write(fd, t + i, expect)) == -1 || nb == 0)
goto cleanup;
i++;
}
cleanup:
close(fd);
return i;
}
int
read_treasure_bin(int fd, struct treasure_file_header* h,
struct treasure **out)
{
ssize_t nr;
size_t expected;
size_t i;
expected = sizeof(struct treasure_file_header);
/* read the header */
i = -1;
if ((nr = read(fd, h, expected)) == -1 || nr == 0)
goto cleanup;
/* Ensure version in header matches TREASURE_FILE_VERSION */
if (h->version != TREASURE_FILE_VERSION)
goto cleanup;
/* Ensure number of entries <= TREASURE_ITEM_MAX */
if (h->num_entries > TREASURE_ITEM_MAX)
goto cleanup;
expected = sizeof(struct treasure);
if ((*out = calloc(h->num_entries, expected)) == NULL)
err(1, "read_treasure_bin:calloc");
i = 0;
while (i < h->num_entries) {
if ((nr = read(fd, (*out) + i, expected)) == -1 || nr == 0)
goto cleanup;
i++;
}
cleanup:
close(fd);
return i;
}
int
read_treasure_csv(int fd, struct treasure_file_header* h, struct treasure **out)
{
ssize_t linelen;
size_t linesize, n, lineno;
struct treasure *t;
FILE *f;
char *line, *ptr, *cursor;
char buf[16];
if ((f = fdopen(fd, "r")) == NULL)
err(1, "read_treasure_csv:fdopen");
bzero(h, sizeof(struct treasure_file_header));
n = 10;
line = NULL;
if ((*out = calloc(10, sizeof(struct treasure))) == NULL)
err(1, "calloc");
lineno = linesize = 0;
/* discard header row */
if ((linelen = getline(&line, &linesize, f)) == -1)
err(1, "Empty csv file");
lineno++;
while (h->num_entries < TREASURE_ITEM_MAX &&
((linelen = getline(&line, &linesize, f)) != -1)) {
if (h->num_entries == n) {
if (verbose)
fprintf(stderr, "Resizing buffer. New n: %zu\n",
n * 2);
/* resize buffer */
if ((*out = recallocarray(*out, n, n * 2,
sizeof(struct treasure))) == NULL)
err(1, "recallocarray");
n = n * 2;
}
lineno++;
t = (*out) + h->num_entries;
/* line will now have the full line, ending with \n\0 */
/* "TYPE,WEIGHT,VALUE" */
ptr = strchr(line, ',');
if (ptr == NULL) {
if (verbose)
fprintf(stderr, "Skipping malformed row at "
"line: %zu\n", lineno);
continue;
} else {
/* type */
strlcpy(buf, line, ptr - line + 1);
if ((t->t = match_treasure_type(buf)) ==
NUM_TREASURE_TYPES) {
if (verbose)
fprintf(stderr, "Skipping unknown type,"
"\"%s\", at line %zu.\n", buf,
lineno);
}
ptr++;
cursor = ptr;
}
ptr = strchr(cursor, ',');
if (ptr == NULL) {
if (verbose)
fprintf(stderr, "Skipping malformed row at "
"line: %zu\n", lineno);
continue;
} else {
/* weight */
strlcpy(buf, cursor, ptr - cursor + 1);
t->w = atoll(buf);
ptr++;
cursor = ptr;
}
/* value */
t->v = atoll(cursor);
h->num_entries++;
}
free(line);
return h->num_entries;
}
enum treasure_type
match_treasure_type(const char *name)
{
if (strcmp("COIN", name) == 0) { return COIN; }
else if (strcmp("CROWN", name) == 0) { return CROWN; }
else if (strcmp("DAGGER", name) == 0) { return DAGGER; }
else if (strcmp("GEM", name) == 0) { return GEM; }
else if (strcmp("NECKLACE", name) == 0) { return NECKLACE; }
else if (strcmp("RING", name) == 0) { return RING; }
else if (strcmp("SWORD", name) == 0) { return SWORD; }
else return NUM_TREASURE_TYPES;
}