add games/dpic and games/todpic

front
qwx 2018-07-25 05:02:46 +02:00
parent 416aed9b66
commit a8644d01c3
4 changed files with 442 additions and 0 deletions

118
sys/man/1/dpic Normal file
View File

@ -0,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).

132
sys/src/games/dpic.c Normal file
View File

@ -0,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);
}

View File

@ -17,6 +17,8 @@ TARG=4s\
midi\
wadfs\
dmid\
dpic\
todpic\
OFILES=
HFILES=

190
sys/src/games/todpic.c Normal file
View File

@ -0,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);
}