blob: 79e5d90f6655fcdf8e6a07af928486247f700dfc [file] [log] [blame]
/*
* PES file parsing.
*
* All format credit goes to Robert Heel and
*
* https://bugs.launchpad.net/inkscape/+bug/247463
*
* which has a php script to do so. He in turn seems to have
* gotten it from NJ Crawford's C# PES viewer. I just turned
* it into C.
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "pes.h"
static struct color color_def[256] = {
{ NULL, 0, 0, 0 },
{ "Color1", 14, 31, 124 },
{ "Color2", 10, 85, 163 },
{ "Color3", 48, 135, 119 },
{ "Color4", 75, 107, 175 },
{ "Color5", 237, 23, 31 },
{ "Color6", 209, 92, 0 },
{ "Color7", 145, 54, 151 },
{ "Color8", 228, 154, 203 },
{ "Color9", 145, 95, 172 },
{ "Color10", 157, 214, 125 },
{ "Color11", 232, 169, 0 },
{ "Color12", 254, 186, 53 },
{ "Color13", 255, 255, 0 },
{ "Color14", 112, 188, 31 },
{ "Color15", 192, 148, 0 },
{ "Color16", 168, 168, 168 },
{ "Color17", 123, 111, 0 },
{ "Color18", 255, 255, 179 },
{ "Color19", 79, 85, 86 },
{ "Black", 0, 0, 0 },
{ "Color21", 11, 61, 145 },
{ "Color22", 119, 1, 118 },
{ "Color23", 41, 49, 51 },
{ "Color24", 42, 19, 1 },
{ "Color25", 246, 74, 138 },
{ "Color26", 178, 118, 36 },
{ "Color27", 252, 187, 196 },
{ "Color28", 254, 55, 15 },
{ "White", 240, 240, 240 },
{ "Color30", 106, 28, 138 },
{ "Color31", 168, 221, 196 },
{ "Color32", 37, 132, 187 },
{ "Color33", 254, 179, 67 },
{ "Color34", 255, 240, 141 },
{ "Color35", 208, 166, 96 },
{ "Color36", 209, 84, 0 },
{ "Color37", 102, 186, 73 },
{ "Color38", 19, 74, 70 },
{ "Color39", 135, 135, 135 },
{ "Color40", 216, 202, 198 },
{ "Color41", 67, 86, 7 },
{ "Color42", 254, 227, 197 },
{ "Color43", 249, 147, 188 },
{ "Color44", 0, 56, 34 },
{ "Color45", 178, 175, 212 },
{ "Color46", 104, 106, 176 },
{ "Color47", 239, 227, 185 },
{ "Color48", 247, 56, 102 },
{ "Color49", 181, 76, 100 },
{ "Color50", 19, 43, 26 },
{ "Color51", 199, 1, 85 },
{ "Color52", 254, 158, 50 },
{ "Color53", 168, 222, 235 },
{ "Color54", 0, 103, 26 },
{ "Color55", 78, 41, 144 },
{ "Color56", 47, 126, 32 },
{ "Color57", 253, 217, 222 },
{ "Color58", 255, 217, 17 },
{ "Color59", 9, 91, 166 },
{ "Color60", 240, 249, 112 },
{ "Color61", 227, 243, 91 },
{ "Color62", 255, 200, 100 },
{ "Color63", 255, 200, 150 },
{ "Color64", 255, 200, 200 },
};
static struct color *my_colors[256];
#define CHUNKSIZE (8192)
int read_file(int fd, struct region *region)
{
int len = 0, done = 0;
char *buf = NULL;
for (;;) {
int space = len - done, ret;
if (!space) {
space = CHUNKSIZE;
len += space;
buf = realloc(buf, len);
}
ret = read(fd, buf + done, space);
if (ret > 0) {
done += ret;
continue;
}
if (!ret)
break;
if (errno == EINTR || errno == EAGAIN)
continue;
free(buf);
return -1;
}
/* "len+8" guarantees that there is some slop at the end */
region->ptr = realloc(buf, len+8);
region->size = len;
return 0;
}
int read_path(const char *path, struct region *region)
{
if (path) {
int fd = open(path, O_RDONLY);
if (fd > 0) {
int ret = read_file(fd, region);
int saved_errno = errno;
close(fd);
errno = saved_errno;
return ret;
}
return fd;
}
return read_file(0, region);
}
#define get_u8(buf, offset) (*(unsigned char *)((offset)+(const char *)(buf)))
#define get_le32(buf, offset) (*(unsigned int *)((offset)+(const char *)(buf)))
static int parse_pes_colors(struct region *region, unsigned int pec)
{
const void *buf = region->ptr;
int nr_colors = get_u8(buf, pec+48) + 1;
int i;
for (i = 0; i < nr_colors; i++) {
struct color *color;
color = color_def + get_u8(buf, pec+49+i);
my_colors[i] = color;
}
return 0;
}
static struct pes_block *new_block(struct pes *pes)
{
int size = sizeof(struct pes_block) + 64*sizeof(struct stitch);
struct pes_block *block = malloc(size);
if (block) {
memset(block, 0, size);
block->max_stitches = 64;
pes->last = block;
*pes->listp = block;
pes->listp = &block->next;
block->color = my_colors[pes->nr_colors++];
}
return block;
}
static int add_stitch(struct pes *pes, int x, int y)
{
struct pes_block *block = pes->last;
int nr_stitches = block->nr_stitches;
if (x < pes->min_x)
pes->min_x = x;
if (x > pes->max_x)
pes->max_x = x;
if (y < pes->min_y)
pes->min_y = y;
if (y > pes->max_y)
pes->max_y = y;
if (block->max_stitches == nr_stitches) {
int new_stitches = (nr_stitches * 3) / 2 + 64;
int size = sizeof(struct pes_block) + new_stitches*sizeof(struct stitch);
block = realloc(block, size);
if (!block)
return -1;
block->max_stitches = new_stitches;
}
block->stitch[nr_stitches].x = x;
block->stitch[nr_stitches].y = y;
block->nr_stitches = nr_stitches+1;
return 0;
}
static int parse_pes_stitches(struct region *region, unsigned int pec, struct pes *pes)
{
int oldx, oldy;
const unsigned char *buf = region->ptr, *p, *end;
struct pes_block *block;
p = buf + pec + 532;
end = buf + region->size;
oldx = oldy = 0;
block = new_block(pes);
while (p < end) {
int val1 = p[0], val2 = p[1];
p += 2;
if (val1 == 255 && !val2)
return 0;
if (val1 == 254 && val2 == 176) {
if (block->nr_stitches) {
block = new_block(pes);
if (!block)
return -1;
}
p++; /* Skip byte */
continue;
}
/* High bit set means 12-bit offset, otherwise 7-bit signed delta */
if (val1 & 0x80) {
val1 = ((val1 & 15) << 8) + val2;
/* Signed 12-bit arithmetic */
if (val1 & 2048)
val1 -= 4096;
val2 = *p++;
} else {
if (val1 & 64)
val1 -= 128;
}
if (val2 & 0x80) {
val2 = ((val2 & 15) << 8) + *p++;
/* Signed 12-bit arithmetic */
if (val2 & 2048)
val2 -= 4096;
} else {
if (val2 & 64)
val2 -= 128;
}
val1 += oldx;
val2 += oldy;
oldx = val1;
oldy = val2;
if (add_stitch(pes, val1, val2))
return -1;
}
return 0;
}
int parse_pes(struct region *region, struct pes *pes)
{
const void *buf = region->ptr;
unsigned int size = region->size;
unsigned int pec;
if (size < 48)
return -1;
if (memcmp(buf, "#PES", 4))
return -1;
pec = get_le32(buf, 8);
if (pec > region->size)
return -1;
if (pec + 532 >= size)
return -1;
if (parse_pes_colors(region, pec) < 0)
return -1;
return parse_pes_stitches(region, pec, pes);
}