shithub: mcfs

ref: 74533c8ff1ab84ff0949d52c3726ccfcfc1bf76a
dir: /extra/mkvdump.c/

View raw version
#include "../ebml.c"

#define min(a,b) ((a)<=(b)?(a):(b))

static vlong stack[128];

static void
usage(void)
{
	fprint(2, "usage: %s [-d] [-h hxsz] [file.mkv]\n", argv0);
	exits("usage");
}

void
main(int argc, char **argv)
{
	int n, sti, i, hxsz, lacing, fd, debug;
	vlong sz, off, x;
	uchar *b, t[3];
	Biobuf in, out;
	Elspec s;
	double d;
	Tm tm;

	debug = 0;
	hxsz = 32;
	ARGBEGIN{
	case 'd':
		debug++;
		break;
	case 'h':
		if((hxsz = atoi(EARGF(usage()))) > 0)
			break;
	default:
		usage();
	}ARGEND

	fd = 0;
	if(argc == 1){
		if((fd = open(*argv, OREAD)) < 0)
			sysfatal("%r");
	}else if(argc != 0)
		usage();

	if((b = malloc(hxsz)) == nil)
		sysfatal("memory");

	quotefmtinstall();
	fmtinstall('H', encodefmt);
	tmfmtinstall();
	Binit(&in, fd, OREAD);
	Binit(&out, 1, OWRITE);

	sti = 0;
	for(;;){
		off = Boffset(&in);
		if((n = ebmlel(&in, 0x7fffffffffffffffLL, &s, &sz)) < 0){
			werrstr("invalid ebml: %r at %#llx (size %lld)", off, sz);
			goto err;
		}
		if(s.type < 0)
			continue;
		if(n == 0) /* eof */
			break;
		while(sti > 0 && off >= stack[sti-1])
			sti--;
		if(s.name){
			for(i = 0; i < sti; i++)
				Bputc(&out, '\t');
			Bprint(&out, "%s ", s.name);
			if(debug)
				Bprint(&out, " (id %#llx) ", s.id);
			if(s.type == Emaster && sti < nelem(stack))
				stack[sti++] = off+n+sz;
		}else{
			Bprint(&out, "%#llx ", s.id);
		}
		if(debug)
			Bprint(&out, "(%lld bytes) ", sz);

		switch(s.type){
		case Ebinary:
			if(s.id == ESimpleBlock || s.id == EBlock){
				if((n = ebmluint(&in, sz, &x)) < 0){
					werrstr("block: %r");
					goto err;
				}
				sz -= n;
				if(Bread(&in, t, 3) != 3){
					werrstr("block: %r");
					goto err;
				}
				sz -= 3;
				lacing = (t[2] >> 1) & 3;
				Bprint(&out, "(track %lld, timecode %d) ", x, (s16int)(t[0]<<8 | t[1]));
				if(s.id == ESimpleBlock){
					if(t[2] & 0x80)
						Bprint(&out, "(key) ");
					if(t[2] & 0x01)
						Bprint(&out, "(discard) ");
				}
				if(t[2] & 0x08)
					Bprint(&out, "(hidden) ");
				if(lacing != 0)
					Bprint(&out, "(lacing %d) ", lacing);
			}
			n = min(hxsz, sz);
			if(Bread(&in, b, n) != n){
				werrstr("binary: %r");
				goto err;
			}
			Bprint(&out, "[%.*H%s] (%lld bytes)", n, b, sz > n ? "..." : "", sz);
			sz -= n;
			break;
		case Efloat:
			if(ebmlfloat(&in, sz, &d) < 0)
				goto err;
			Bprint(&out, "%g", d);
			sz = 0;
			break;
		case Eunsigned:
			if(ebmlrawuint(&in, sz, &x) < 0)
				goto err;
			Bprint(&out, "%llud", (uvlong)x);
			sz = 0;
			break;
		case Esigned:
			if(ebmlrawsint(&in, sz, &x) < 0)
				goto err;
			Bprint(&out, "%lld", x);
			sz = 0;
			break;
		case Eunicode:
		case Eascii:
			n = min(hxsz, sz);
			if(Bread(&in, b, n) != n){
				werrstr("string: %r");
				goto err;
			}
			Bprint(&out, "%#.*q%s", n, (char*)b, sz > n ? "..." : "");
			sz -= n;
			break;
		case Etimestamp:
			if(ebmlrawsint(&in, sz, &x) < 0)
				goto err;
			x /= 1000000000LL;
			x += 978307200LL;
			Bprint(&out, "%τ", tmfmt(tmtime(&tm, x, nil), nil));
			sz = 0;
			break;
		case Emaster:
			break;
		default:
			Bprint(&out, "???");
		}

		if(s.id == ETrackType)
			Bprint(&out, " (%s)", ebmltracktype(x));

		if(Bputc(&out, '\n') < 0)
			break;
		if(s.type != Emaster && sz > 0 && Bseek(&in, sz, 1) < 0)
			goto err;
			
	}
	Bterm(&out);
	Bterm(&in);

	exits(nil);
err:
	fprint(2, "%s: %r\n", fd == 0 ? "stdin" : *argv);
	exits("error");
}