#if 0
gcc -s -O2 -o ./imgtofhm -Wno-unused-result -fwrapv imgtofhm.c
exit
#endif
#define _GNU_SOURCE
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
unsigned char size,meth;
unsigned char data[0xFFFE]; // the first row is all 0, since the compression algorithm requires this
} Picture;
typedef struct {
unsigned char x[8];
} Color;
static const char default_palette[]=
"C020FF "
"000000 222222 333333 444444 555555 666666 777777 888888 999999 AAAAAA BBBBBB CCCCCC DDDDDD EEEEEE FFFFFF "
"281400 412300 5F3200 842100 A05000 C35F14 E1731E FF8232 FF9141 FFA050 FFAF5F FFBE73 FFD282 FFE191 FFF0A0 "
"321E1E 412220 5F2830 823040 A03A4C BE4658 E15464 FF6670 FF7F7B FF8E7F FF9F7F FFAF7F FFBF7F FFCF7F FFDF7F "
"280D0D 401515 602020 802A2A A03535 C04040 E04A4A FF5555 FF6764 FF6F64 FF7584 FF849D FF94B7 FF9FD1 FFAEEA "
"901400 A02000 B03000 C04000 D05000 E06000 F07000 FF8000 FF9000 FFA000 FFB000 FFC000 FFD000 FFE000 FFF000 "
"280000 400000 600000 800000 A00000 C00000 E00000 FF0000 FF2828 FF4040 FF6060 FF8080 FFA0A0 FFC0C0 FFE0E0 "
"280028 400040 600060 800080 A000A0 C000C0 E000E0 FF00FF FF28FF FF40FF FF60FF FF80FF FFA0FF FFC0FF FFE0FF "
"281428 402040 603060 804080 A050A0 C060C0 E070E0 FF7CFF FF8CFF FF9CFF FFACFF FFBCFF FFCCFF FFDCFF FFECFF "
"280050 350566 420A7C 4F0F92 5C14A8 6919BE 761ED4 8323EA 9028FF A040FF B060FF C080FF D0A0FF E0C0FF F0E0FF "
"000028 000040 000060 000080 0000A0 0000C0 0000E0 0000FF 0A28FF 284AFF 466AFF 678AFF 87AAFF A7CAFF C7EBFF "
"0F1E1E 142323 193232 1E4141 285050 325F5F 377373 418282 469191 50A0A0 5AAFAF 5FC3C3 69D2D2 73E1E1 78F0F0 "
"002828 004040 006060 008080 00A0A0 00C0C0 00E0E0 00FFFF 28FFFF 40FFFF 60FFFF 80FFFF A0FFFF C0FFFF E0FFFF "
"002800 004000 006000 008000 00A000 00C000 00E000 00FF00 28FF28 40FF40 60FF60 80FF80 A0FFA0 C0FFC0 E0FFE0 "
"002110 234123 325F32 418241 50A050 5FC35F 73E173 85FF7A 91FF6E A0FF5F B4FF50 C3FF41 D2FF32 E1FF23 F0FF0F "
"282800 404000 606000 808000 A0A000 C0C000 E0E000 FFFF00 FFFF28 FFFF40 FFFF60 FFFF80 FFFFA0 FFFFC0 FFFFE0 "
"442100 00FF55 0055FF FF5500 55FF00 FF0055 5500FF CA8B25 F078F0 F0F078 FF7F00 DD6D01 7AFF00 111111 "
"000000 0000AA 00AA00 00AAAA AA0000 AA00AA AAAA00 AAAAAA "
"555555 5555FF 55FF55 55FFFF FF5555 FF55FF FFFF55 FFFFFF "
;
static unsigned char head[16];
static int maxpic,size;
static const char*prefix;
static Color pal[256];
static Picture pic;
static FILE*sizer;
static int total=0;
static ssize_t cookie_write(void*cookie,const char*buf,size_t size) {
total+=size;
return size;
}
static int cookie_seek(void*cookie,off64_t*offset,int whence) {
if(whence==SEEK_SET) total=*offset;
else if(whence==SEEK_CUR) total+=*offset;
else return -1;
*offset=total;
return 0;
}
static void load_palette(void) {
int i;
for(i=0;i<256;i++) {
sscanf(default_palette+i*7,"%2hhX%2hhX%2hhX ",pal[i].x+0,pal[i].x+2,pal[i].x+4);
pal[i].x[1]=pal[i].x[0];
pal[i].x[3]=pal[i].x[2];
pal[i].x[5]=pal[i].x[4];
pal[i].x[7]=pal[i].x[6]=i?255:0;
}
}
static void set_transparency(const char*s) {
if(*s) switch(strlen(s)) {
case 6:
sscanf(s,"%2hhX%2hhX%2hhX",pal->x+0,pal->x+2,pal->x+4);
pal->x[1]=pal->x[0];
pal->x[3]=pal->x[2];
pal->x[5]=pal->x[4];
pal->x[7]=pal->x[6]=255;
break;
case 12:
sscanf(s,"%2hhX%2hhX%2hhX%2hhX%2hhX%2hhX",pal->x+0,pal->x+1,pal->x+2,pal->x+3,pal->x+4,pal->x+5);
pal->x[7]=pal->x[6]=255;
break;
default: errx(1,"Incorrect transparency specification");
}
}
static void load_rotate(void) {
int x,y;
int m=pic.meth;
unsigned char*d=pic.data+size;
static unsigned char buf[255*255];
unsigned char*p;
p=buf;
for(y=0;y<size;y++) for(x=0;x<size;x++) {
if(m&1) x=size-x-1;
if(m&2) y=size-y-1;
*p++=d[m&4?x*size+y:y*size+x];
if(m&1) x=size-x-1;
if(m&2) y=size-y-1;
}
memcpy(d,buf,size*size);
}
static void out_run(FILE*fp,int ch,int am,const unsigned char*d,int le) {
int n=am%85?:85;
fputc(ch+n-1,fp);
if(le) fwrite(d,1,le<n?le:n,fp);
if(ch) d+=n,le-=n;
while(am-=n) {
n=am>7225?7225:am;
fputc(ch+n/85-1,fp);
if(le>0) fwrite(d,1,le<n?le:n,fp);
if(ch) d+=n,le-=n;
}
}
static void compress_picture(FILE*fp) {
int ps=pic.size;
int ms=ps*ps;
const unsigned char*d=pic.data+ps;
int i=0;
int ca,homo,hetero;
while(i<ms) {
ca=0;
while(i+ca<ms && d[i+ca]==d[i+ca-ps]) ca++;
homo=1;
while(i+homo<ms && d[i+homo]==d[i]) homo++;
hetero=1;
while(i+hetero<ms) {
if(d[i+hetero]==d[i+hetero-ps] && d[i+hetero+1]==d[i+hetero+1-ps]) break;
if(d[i+hetero]==d[i+hetero+1] && i+hetero==ms-1) break;
if(d[i+hetero]==d[i+hetero+1] && d[i+hetero]==d[i+hetero+2]) break;
hetero++;
}
if(ca>=homo && (ca>=hetero || homo>1)) {
out_run(fp,170,ca,0,0);
i+=ca;
} else if(homo>1) {
out_run(fp,0,homo-1,d+i,1);
i+=homo;
} else {
if(hetero>85) hetero=85;
out_run(fp,85,hetero,d+i,hetero);
i+=hetero;
}
}
}
static void import_one(int numb) {
int a,b,i,j,m,s,x,y;
unsigned char buf[8];
unsigned char*q;
// Load picture data
pic.size=size;
pic.meth=15;
memset(pic.data,0,255);
q=pic.data+pic.size;
for(y=0;y<size;y++) for(x=0;x<size;x++) {
if(fread(buf,1,8,stdin)<=0) err(1,"I/O error");
if(buf[6]&0x80) {
for(i=0;i<255;i++) if(!memcmp(buf,pal[i].x,8)) goto found;
i=1;
a=0x300000;
for(j=1;j<255;j++) {
b=abs(buf[0]-pal[j].x[0])+abs(buf[2]-pal[j].x[2])+abs(buf[4]-pal[j].x[4]);
if(b<=a) a=b,i=j;
}
found: *q++=i;
} else {
*q++=0;
}
}
// Try compression
m=15;
s=size*size;
rewind(sizer);
for(i=0;i<8;i++) {
compress_picture(sizer);
j=ftell(sizer);
if(j>0 && j<s) s=j,m=i;
rewind(sizer);
pic.meth="\1\3\1\7\1\3\1\7"[i];
load_rotate();
}
// Write header
s+=2;
printf("%s%d.IMG",prefix,numb);
putchar(0);
putchar(s>>020);
putchar(s>>030);
putchar(s>>000);
putchar(s>>010);
// Write compressed data
putchar((m<<4)|1);
putchar(size);
pic.meth=m;
load_rotate();
if(m==15) fwrite(pic.data+pic.size,pic.size,pic.size,stdout); else compress_picture(stdout);
}
int main(int argc,char**argv) {
int i;
if(argc!=3 && argc!=4) errx(1,"Incorrect number of arguments");
maxpic=strtol(argv[1],0,10);
prefix=argv[2];
load_palette();
if(argc==4) set_transparency(argv[3]);
sizer=fopencookie("","w+",(cookie_io_functions_t){.write=cookie_write,.seek=cookie_seek});
if(!sizer) err(1,"Allocation failed");
fread(head,1,16,stdin);
if(memcmp(head,"farbfeld",8)) errx(1,"Unrecognized input format");
if(head[8]|head[9]|head[10]) errx(1,"Picture is too big");
size=head[11];
if(!size) errx(1,"Wrong horizontal size");
i=(head[12]<<030)|(head[13]<<020)|(head[14]<<010)|(head[15]<<000);
if(!i) errx(1,"Wrong vertical size");
if(maxpic>i/size || maxpic<=0) {
maxpic=i/size;
if(i%size) errx(1,"Wrong vertical size");
}
for(i=0;i<maxpic;i++) import_one(i);
return 0;
}