ref: a8644d01c34febd6e709c8968fbb926c9115e6c1
parent: 416aed9b662ad9e7ffa9a0bd608b29a878676a7f
author: qwx <devnull@localhost>
date: Wed Jul 25 01:02:46 EDT 2018
add games/dpic and games/todpic
--- /dev/null
+++ b/sys/man/1/dpic
@@ -1,0 +1,118 @@
+.TH DPIC 1
+.SH NAME
+dpic, todpic \- Doom picture decoder and encoder
+.SH SYNOPSIS
+.B dpic
+[
+.B -f
+] [
+.B -p
+.I palette
+] [
+.I pic
+]
+.PP
+.B todpic
+[
+.B -fw
+] [
+.B -b
+.I bgcol
+] [
+.B -p
+.I palette
+] [
+.I image
+]
+.SH DESCRIPTION
+.I Dpic
+reads a doom picture formatted image (default standard input),
+converts it to a Plan 9
+.IR image (6)
+and writes it to standard out.
+.I Todpic
+does the opposite transformation.
+.PP
+A color palette is needed for the process;
+its location is set to
+.B /mnt/wad/playpal
+by default.
+This may be overridden with the
+.B -p
+command line option.
+Both programs also accept an
+.B -f
+flag to indicate processing a doom 64x64 flat picture.
+.PP
+When encoding a doom picture,
+x and y offsets are set to the input's top left corner coordinates.
+The
+.B -w
+flag sets the offsets so as to center the picture when drawn by the doom engine,
+which is useful for wall patches.
+The
+.B -b
+option sets the RGB24 color to signal transparent pixels,
+.L 0x00FFFF
+by default.
+.SH EXAMPLES
+Create a patch
+.I WAD
+(see
+.BR wadfs (4))
+replacing a sky texture.
+First, create a 256x128 image, mirror it, and convert it for use with
+.BR tweak (1).
+.IP
+.EX
+% png -9t tuttleglenda.png \\
+ | resample -x 128 -y 128 \\
+ | crop -r 0 0 256 128 \\
+ | rotate -l \\
+ | iconv -c m8 > tuttlesky
+.EE
+.PP
+Next, use
+.BR tweak (1)
+to tile the 128x128 picture.
+Then, mount an
+.I IWAD
+containing the base color palette, convert to a doom picture,
+create a patch
+.IR WAD ,
+then launch doom using it.
+.IP
+.EX
+% games/wadfs /sys/games/lib/doom/doom2.wad
+createfile SW18_7: file already exists
+% games/wadfs -m /mnt/new
+% games/todpic tuttlesky > /mnt/new/rsky1
+% cp /mnt/new/WAD tuttle.wad
+% games/doom -file tuttle.wad
+.EE
+.PP
+Create a crude catclock weapon sprite.
+.IP
+.EX
+% games/wadfs /sys/games/lib/doom/doom2.wad
+createfile SW18_7: file already exists
+% mkdir /mnt/new/s
+adding end marker S_END
+% cp /mnt/wad/s/* /mnt/new/s/
+% crop -r 0 0 114 120 -t -120 -60 catclock.bit \\
+ | games/todpic -b 0xffffff > /mnt/new/s/punga0
+% games/doom -file /mnt/new/WAD
+.EE
+.SH SOURCE
+.B /sys/src/games/dpic.c
+.br
+.B /sys/src/games/todpic.c
+.SH "SEE ALSO"
+.IR games (1),
+.IR tweak (1),
+.IR wadfs (4)
+.SH HISTORY
+.I Dpic
+and
+.I todpic
+first appeared in 9front (July, 2018).
--- /dev/null
+++ b/sys/src/games/dpic.c
@@ -1,0 +1,132 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <bio.h>
+
+int dx = 64, dy = 64;
+Biobuf *bi, *bo;
+u32int pal[256];
+
+u8int
+get8(void)
+{
+ uchar v;
+
+ if(Bread(bi, &v, 1) != 1)
+ sysfatal("get8: short read");
+ return v;
+}
+
+u16int
+get16(void)
+{
+ u8int v;
+
+ v = get8();
+ return get8() << 8 | v;
+}
+
+u32int
+get32(void)
+{
+ u16int v;
+
+ v = get16();
+ return get16() << 16 | v;
+}
+
+u32int*
+unpic(void)
+{
+ int n, h;
+ u32int *p, *d, *cols, *buf;
+
+ dx = get16();
+ dy = get16();
+ cols = mallocz(dx * sizeof *cols, 1);
+ buf = mallocz(dx * dy * sizeof *buf, 1);
+ if(cols == nil || buf == nil)
+ sysfatal("mallocz: %r");
+ get32();
+ for(p=cols; p<cols+dx; p++)
+ *p = get32();
+ for(p=cols; p<cols+dx; p++){
+ Bseek(bi, *p, 0);
+ for(;;){
+ if((h = get8()) == 0xff)
+ break;
+ n = get8();
+ get8();
+ for(d=buf+(p-cols)+h*dx; n-->0; d+=dx)
+ *d = pal[get8()];
+ get8();
+ }
+ }
+ free(cols);
+ return buf;
+}
+
+u32int*
+unflat(void)
+{
+ u32int *p;
+ static u32int buf[4096];
+
+ for(p=buf; p<buf+nelem(buf); p++)
+ *p = pal[get8()];
+ return buf;
+}
+
+void
+getpal(char *f)
+{
+ uchar u[3];
+ u32int *p;
+ Biobuf *bp;
+
+ if((bp = Bopen(f, OREAD)) == nil)
+ sysfatal("getpal: %r");
+ for(p=pal; p<pal+nelem(pal); p++){
+ if(Bread(bp, u, 3) != 3)
+ sysfatal("getpal: short read: %r");
+ *p = u[2]<<16 | u[1]<<8 | u[0];
+ }
+ Bterm(bp);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-f] [-p palette] pic\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int fd, flat;
+ char *p, c[9];
+ u32int *buf;
+
+ flat = 0;
+ p = "/mnt/wad/playpal";
+ ARGBEGIN{
+ case 'f': flat = 1; break;
+ case 'p': p = EARGF(usage()); break;
+ default: usage();
+ }ARGEND
+ if(*argv == nil)
+ usage();
+ if((fd = open(*argv, OREAD)) < 0)
+ sysfatal("open: %r");
+ getpal(p);
+ bi = Bfdopen(fd, OREAD);
+ bo = Bfdopen(1, OWRITE);
+ if(bi == nil || bo == nil)
+ sysfatal("Bfdopen: %r");
+ buf = flat ? unflat() : unpic();
+ Bprint(bo, "%11s %11d %11d %11d %11d ",
+ chantostr(c, XBGR32), 0, 0, dx, dy);
+ Bwrite(bo, buf, dx * dy * sizeof *buf);
+ exits(nil);
+}
--- a/sys/src/games/mkfile
+++ b/sys/src/games/mkfile
@@ -17,6 +17,8 @@
midi\
wadfs\
dmid\
+ dpic\
+ todpic\
OFILES=
HFILES=
--- /dev/null
+++ b/sys/src/games/todpic.c
@@ -1,0 +1,190 @@
+#include <u.h>
+#include <libc.h>
+#include <draw.h>
+#include <memdraw.h>
+#include <bio.h>
+
+int wofs;
+u32int pal[256], bg = 0x00ffff;
+Biobuf *bp;
+
+#define abs(x) ((x) < 0 ? -(x) : (x))
+
+void
+put8(u8int v)
+{
+ if(Bwrite(bp, &v, sizeof v) != sizeof v)
+ sysfatal("put8: short write");
+}
+
+void
+put16(u16int v)
+{
+ put8(v);
+ put8(v >> 8);
+}
+
+void
+put32(u32int v)
+{
+ put16(v);
+ put16(v >> 16);
+}
+
+int
+pali(u32int v)
+{
+ int i, Δ, Δ´;
+ u32int *p;
+
+ i = 0;
+ Δ = abs((char)v - (char)*pal)
+ + abs((char)(v >> 8) - (char)(*pal >> 8))
+ + abs((char)(v >> 16) - (char)(*pal >> 16));
+ for(p=pal; p<pal+nelem(pal); p++){
+ Δ´ = abs((char)v - (char)*p)
+ + abs((char)(v >> 8) - (char)(*p >> 8))
+ + abs((char)(v >> 16) - (char)(*p >> 16));
+ if(Δ´ < Δ){
+ Δ = Δ´;
+ i = p - pal;
+ if(Δ == 0)
+ break;
+ }
+ }
+ return i;
+}
+
+void
+topic(Memimage *i)
+{
+ int w, h, dx, dy;
+ uchar *np, *b, *buf, *p, *pp;
+ u32int v;
+
+ p = i->data->bdata;
+ dx = Dx(i->r);
+ dy = Dy(i->r);
+ if(dy > 254)
+ sysfatal("topic: invalid pic height");
+ put16(dx);
+ put16(dy);
+ put16(wofs ? dx / 2 - 1 : i->r.min.x);
+ put16(wofs ? dy - 5 : i->r.min.y);
+ if(i->r.min.x != 0)
+ dx = i->width;
+ buf = mallocz((5 * dy / 2 + 5) * dx, 1);
+ if(buf == nil)
+ sysfatal("mallocz: %r");
+ for(w=dx, b=buf; w>0; w--, p+=3){
+ put32(b - buf + 8 + dx * 4);
+ for(h=0, np=b+1, pp=p; h<dy; h++, pp+=dx*3){
+ v = pp[2] << 16 | pp[1] << 8 | pp[0];
+ if(v == bg){
+ if(b - np - 2 > 0){
+ *np = b - np - 2;
+ *b++ = 0;
+ np = b + 1;
+ }
+ continue;
+ }
+ if(b - np - 2 < 0){
+ *b++ = h;
+ b++;
+ *b++ = 0;
+ }
+ *b++ = pali(v);
+ }
+ if(b - np - 2 >= 0){
+ *np = b - np - 2;
+ *b++ = 0;
+ }
+ *b++ = 0xff;
+ }
+ Bwrite(bp, buf, b - buf);
+ free(buf);
+}
+
+void
+toflat(Memimage *i)
+{
+ int n;
+ uchar *p;
+
+ if(Dx(i->r) != 64 || Dy(i->r) != 64)
+ sysfatal("toflat: invalid flatpic dimensions");
+ p = i->data->bdata;
+ n = 64*64;
+ while(n-- > 0){
+ put8(pali(p[2] << 16 | p[1] << 8 | p[0]));
+ p += 4;
+ }
+}
+
+static Memimage*
+iconv(Memimage *i)
+{
+ Memimage *ni;
+
+ if(i->chan == RGB24)
+ return i;
+ if((ni = allocmemimage(i->r, RGB24)) == nil)
+ sysfatal("allocmemimage: %r");
+ memimagedraw(ni, ni->r, i, i->r.min, nil, i->r.min, S);
+ freememimage(i);
+ return ni;
+}
+
+void
+getpal(char *f)
+{
+ uchar u[3];
+ u32int *p;
+ Biobuf *bp;
+
+ if((bp = Bopen(f, OREAD)) == nil)
+ sysfatal("getpal: %r");
+ for(p=pal; p<pal+nelem(pal); p++){
+ if(Bread(bp, u, 3) != 3)
+ sysfatal("getpal: short read: %r");
+ *p = u[0]<<16 | u[1]<<8 | u[2];
+ }
+ Bterm(bp);
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: %s [-fw] [-b bgcol] [-p palette] [image]\n", argv0);
+ exits("usage");
+}
+
+void
+main(int argc, char **argv)
+{
+ int fd, flat;
+ char *p;
+ Memimage *i;
+
+ fd = 0;
+ flat = 0;
+ p = "/mnt/wad/playpal";
+ ARGBEGIN{
+ case 'b': bg = strtoul(EARGF(usage()), nil, 0); break;
+ case 'f': flat = 1; break;
+ case 'p': p = EARGF(usage()); break;
+ case 'w': wofs = 1; break;
+ default: usage();
+ }ARGEND
+ if(*argv != nil)
+ if((fd = open(*argv, OREAD)) < 0)
+ sysfatal("open: %r");
+ getpal(p);
+ if((bp = Bfdopen(1, OWRITE)) == nil)
+ sysfatal("Bfdopen: %r");
+ memimageinit();
+ if((i = readmemimage(fd)) == nil)
+ sysfatal("readmemimage: %r");
+ (flat ? toflat : topic)(iconv(i));
+ exits(nil);
+}