| #!/usr/bin/perl -w | |
| use Fcntl qw(SEEK_SET); | |
| use integer; | |
| # Extract information from within a TADS GAM file | |
| sub ord1($;$) { | |
| my $str = shift; | |
| my $ofs = shift || 0; | |
| return ord(substr($str, $ofs)); | |
| } | |
| sub read1(*) { | |
| my $FH = shift; | |
| return ord(getc($FH)); | |
| } | |
| sub ord2($;$) { | |
| my $str = shift; | |
| my $ofs = shift || 0; | |
| my $c1 = ord1($str, $ofs); | |
| my $c2 = ord1($str, $ofs + 1); | |
| return ($c2 << 8) | $c1; | |
| } | |
| sub ord2s($;$) { | |
| my $str = shift; | |
| my $ofs = shift || 0; | |
| my $result = ord2($str, $ofs); | |
| $result -= 0x10000 if ($result >= 0x8000); | |
| return $result; | |
| } | |
| sub read2(*) { | |
| my $FH = shift; | |
| my $str; | |
| read($FH, $str, 2); | |
| return ord2($str); | |
| } | |
| sub ord4($;$) { | |
| my $str = shift; | |
| my $ofs = shift || 0; | |
| my $c1 = ord1($str, $ofs); | |
| my $c2 = ord1($str, $ofs + 1); | |
| my $c3 = ord1($str, $ofs + 2); | |
| my $c4 = ord1($str, $ofs + 3); | |
| $c4 -= 256 if ($c4 >= 128); # In case of a signed value | |
| return ($c4 << 24) | ($c3 << 16) | ($c2 << 8) | $c1; | |
| } | |
| sub read4(*) { | |
| my $FH = shift; | |
| my $str; | |
| read($FH, $str, 4); | |
| return ord4($str); | |
| } | |
| $xorseed = 0x3f; | |
| $xorinc = 0x40; | |
| sub decode($) { | |
| my $block = shift; | |
| return $block unless ($crypt); | |
| my $len = length($block); | |
| my $mask = $xorseed; | |
| my $maskstr; | |
| for (my $i = 0; $i < $len; $i++) { | |
| $maskstr .= chr($mask); | |
| $mask = ($mask + $xorinc) % 256; | |
| } | |
| return $block ^ $maskstr; | |
| } | |
| sub hexl($) { | |
| my $block = shift; | |
| local *HEXL; | |
| open(HEXL, "|/usr/lib/xemacs-21.4.8/i386-debian-linux/hexl"); | |
| print HEXL $block; | |
| close HEXL; | |
| } | |
| sub sigend($) { | |
| print "(END REACHED)"; | |
| return shift; | |
| } | |
| # Datatype numbers | |
| sub DAT_NUMBER() { return 1; } | |
| sub DAT_OBJECT() { return 2; } | |
| sub DAT_SSTRING() { return 3; } | |
| sub DAT_BASEPTR() { return 4; } | |
| sub DAT_NIL() { return 5; } | |
| sub DAT_CODE() { return 6; } | |
| sub DAT_LIST() { return 7; } | |
| sub DAT_TRUE() { return 8; } | |
| sub DAT_DSTRING() { return 9; } | |
| sub DAT_FNADDR() { return 10; } | |
| sub DAT_TPL() { return 11; } | |
| sub DAT_PROPNUM() { return 13; } | |
| sub DAT_DEMAND() { return 14; } | |
| sub DAT_SYN() { return 15; } | |
| sub DAT_REDIR() { return 16; } | |
| sub DAT_TPL2() { return 17; } | |
| # Print a value, returning the length it occupied | |
| sub printValue($$$) { | |
| my $block = shift; | |
| my $pos = shift; | |
| my $type = shift; | |
| my $lenleft = length($block) - $pos; | |
| if ($type == DAT_NUMBER) { | |
| return sigend($lenleft) if ($lenleft < 4); | |
| print ord4($block, $pos); | |
| return 4; | |
| } | |
| elsif ($type == DAT_OBJECT || $type == DAT_FNADDR) { | |
| print "&" if ($type == DAT_FNADDR); | |
| return sigend($lenleft) if ($lenleft < 2); | |
| printObj(ord2($block, $pos)); | |
| return 2; | |
| } | |
| elsif ($type == DAT_SSTRING || $type == DAT_DSTRING) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| my $strlen = ord2($block, $pos); | |
| # Check for valid string length | |
| return sigend($lenleft) if ($strlen > $lenleft || $strlen < 2); | |
| my $delim = "\""; | |
| $delim = "'" if ($type == DAT_SSTRING); | |
| print $delim, substr($block, $pos + 2, $strlen - 2), $delim; | |
| return $strlen; | |
| } | |
| elsif ($type == DAT_NIL) { | |
| print "nil"; | |
| return 0; | |
| } | |
| elsif ($type == DAT_LIST) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| my $listlen = ord2($block, $pos); | |
| # Check for valid list length | |
| return sigend($lenleft) if ($listlen > $lenleft || $listlen < 2); | |
| printList(substr($block, $pos, $listlen)); | |
| return $listlen; | |
| } | |
| elsif ($type == DAT_TRUE) { | |
| print "true"; | |
| return 0; | |
| } | |
| elsif ($type == DAT_PROPNUM) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| print "&"; | |
| printProp(ord2($block, $pos)); | |
| return 2; | |
| } | |
| else { | |
| print "Unexpected data type: $type\n"; | |
| return $lenleft; | |
| } | |
| } | |
| sub printList($) { | |
| my $block = shift; | |
| print "["; | |
| my $pos = 2; | |
| my $len = ord2($block); | |
| return sigend(length($block)) if ($len > length($block) || $len < 2); | |
| while ($pos < $len) { | |
| my $type = ord1($block, $pos); | |
| $pos++; | |
| $pos += printValue($block, $pos, $type); | |
| print " " if ($pos < $len); | |
| } | |
| print "]"; | |
| return $len; | |
| } | |
| # Define operand type values | |
| sub OPBYTE() { return 1; } | |
| sub OPWORD() { return 2; } | |
| sub OPQUAD() { return 3; } | |
| sub OPOBJ() { return 4; } | |
| sub OPFUNC() { return 5; } | |
| sub OPPROP() { return 6; } | |
| sub OPRET() { return 7; } | |
| sub OPLABEL() { return 8; } | |
| sub OPDSTR() { return 9; } | |
| sub OPBIF() { return 10; } | |
| sub OPSSTR() { return 11; } | |
| sub OPLIST() { return 12; } | |
| sub OPSWITCH() { return 13; } # Switch table | |
| sub OPLINE() { return 14; } # Debugger line record | |
| sub OPFRAME() { return 15; } # Local variable frame record | |
| # Print an operand, returning the number of bytes it occupied | |
| sub printOperand($$$) { | |
| my $block = shift; | |
| my $pos = shift; | |
| my $type = shift; | |
| my $lenleft = length($block) - $pos; | |
| if ($type == OPBYTE) { | |
| return sigend($lenleft) if ($lenleft < 1); | |
| print ord1($block, $pos); | |
| return 1; | |
| } | |
| elsif ($type == OPWORD || $type == OPRET) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| print ord2s($block, $pos); | |
| return 2; | |
| } | |
| elsif ($type == OPQUAD) { | |
| return printValue($block, $pos, DAT_NUMBER); | |
| } | |
| elsif ($type == OPOBJ || $type == OPFUNC) { | |
| return printValue($block, $pos, DAT_OBJECT); | |
| } | |
| elsif ($type == OPPROP) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| printProp(ord2($block, $pos)); | |
| return 2; | |
| } | |
| elsif ($type == OPLABEL || $type == OPSWITCH) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| print $pos + ord2s($block, $pos); | |
| return 2; | |
| } | |
| elsif ($type == OPDSTR) { | |
| return printValue($block, $pos, DAT_DSTRING); | |
| } | |
| elsif ($type == OPSSTR) { | |
| return printValue($block, $pos, DAT_SSTRING); | |
| } | |
| elsif ($type == OPBIF) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| printBuiltin(ord2($block, $pos)); | |
| return 2; | |
| } | |
| elsif ($type == OPLIST) { | |
| return printValue($block, $pos, DAT_LIST); | |
| } | |
| elsif ($type == OPLINE) { | |
| return sigend($lenleft) if ($lenleft < 1); | |
| my $linelen = ord1($block, $pos); | |
| return sigend($lenleft) if ($linelen > $lenleft || $linelen < 1); | |
| print "line record (", $linelen, " bytes)"; | |
| return $linelen; | |
| } | |
| elsif ($type == OPFRAME) { | |
| return sigend($lenleft) if ($lenleft < 2); | |
| my $framelen = ord2($block, $pos); | |
| return sigend($lenleft) if ($framelen > $lenleft || $framelen < 2); | |
| print "frame record (", $framelen, " bytes)"; | |
| return $framelen; | |
| } | |
| else { | |
| print "This shouldn't happen: unknown operand type ", $type, "\n"; | |
| die; | |
| } | |
| } | |
| @props = (undef, "doAction", "verb", "noun", "adjective", "preposition", | |
| "article", "plural", "sdesc", "thedesc", "doDefault", | |
| "ioDefault", "ioAction", "location", "value", | |
| "roomAction", "actorAction", "contents", "tpl", | |
| "prepDefault", "verActor", "validDo", "validIo", | |
| "lookAround", "roomCheck", "statusLine", "locationOK", | |
| "isVisible", "cantReach", "isHim", "isHer", "action", | |
| "validDoList", "validIoList", "iobjGen", "dobjGen", | |
| "nilPrep", "rejectMultiDobj", "moveInto", "construct", | |
| "destruct", "validActor", "preferredActor", "isEquivalent", | |
| "adesc", "multisdesc", "tpl2", "anyvalue", | |
| "newNumbered", "unknown", "parseUnknownDobj", | |
| "parseUnknownIobj", "dobjCheck", "iobjCheck", "verbAction", | |
| "disambigDobj", "disambigIobj", "prefixdesc", "isThem"); | |
| sub printProp($) { | |
| my $n = shift; | |
| if ($n == 0) { | |
| print "noprop"; | |
| } | |
| elsif ($n > 0 && defined $props[$n]) { | |
| print $props[$n]; | |
| } | |
| else { | |
| print "prop", $n; | |
| } | |
| } | |
| @objs = (); | |
| sub printObj($) { | |
| my $n = shift; | |
| if ($n == 65535) { | |
| print "nullobj"; | |
| } | |
| elsif (defined $objs[$n]) { | |
| print $objs[$n]; | |
| } | |
| else { | |
| print "obj", $n; | |
| } | |
| } | |
| sub printBuiltin($) { | |
| my $n = shift; | |
| my @builtins = ("say", "car", "cdr", "length", "randomize", "rand", | |
| "substr", "cvtstr", "cvtnum", "upper", "lower", | |
| "caps", "find", "getarg", "datatype", "setdaemon", | |
| "setfuse", "setversion", "notify", "unnotify", | |
| "yorn", "remfuse", "remdaemon", "incturn", "quit", | |
| "save", "restore", "logging", "input", "setit", | |
| "askfile", "setscore", "firstobj", "nextobj", | |
| "isclass", "restart", "debugTrace", "undo", "defined", | |
| "proptype", "outhide", "runfuses", "rundaemons", | |
| "gettime", "getfuse", "intersect", "inputkey", | |
| "objwords", "addword", "delword", "getwords", | |
| "nocaps", "skipturn", "clearscreen", "firstsc", | |
| "verbinfo", "fopen", "fclose", "fwrite", "fread", | |
| "fseek", "fseekeof", "ftell", "outcapture", | |
| "systemInfo", "morePrompt", "parserSetMe", | |
| "parserGetMe", "reSearch", "reGetGroup", "inputevent", | |
| "timeDelay", "setOutputFilter", "execCommand", | |
| "parserGetObj", "parseNounList", "parserTokenize", | |
| "parserGetTokTypes", "parserDictLookup", | |
| "parserResolveObjects", "parserReplaceCommand", | |
| "exitobj", "inputdialog", "resourceExists"); | |
| if ($n < 0 || $n > $#builtins) { | |
| print "invalid builtin $n"; | |
| } | |
| else { | |
| print $builtins[$n]; | |
| } | |
| } | |
| @opcodes = | |
| ([], | |
| ["pushnum", OPQUAD], | |
| ["pushobj", OPOBJ], | |
| ["neg"], | |
| ["not"], | |
| ["add"], | |
| ["sub"], | |
| ["mul"], | |
| ["div"], | |
| ["and"], | |
| ["or"], | |
| ["eq"], | |
| ["ne"], | |
| ["gt"], | |
| ["ge"], | |
| ["lt"], | |
| ["le"], | |
| ["call", OPBYTE, OPFUNC], | |
| ["getp", OPBYTE, OPPROP], | |
| ["getpdata", OPBYTE, OPPROP], | |
| ["getlcl", OPWORD], | |
| ["ptrgetpdata", OPBYTE], | |
| ["return", OPRET], | |
| ["retval", OPRET], | |
| ["enter", OPWORD], | |
| ["discard"], | |
| ["jmp", OPLABEL], | |
| ["jf", OPLABEL], | |
| ["pushself"], | |
| ["say", OPDSTR], | |
| ["builtin", OPBYTE, OPBIF], | |
| ["pushstr", OPSSTR], | |
| ["pushlst", OPLIST], | |
| ["pushnil"], | |
| ["pushtrue"], | |
| ["pushfn", OPFUNC], | |
| ["getpselfdata", OPBYTE, OPPROP], | |
| [], | |
| ["ptrcall", OPBYTE], | |
| ["ptrinh", OPBYTE], | |
| ["ptrgetp", OPBYTE], | |
| ["pass", OPPROP], | |
| ["exit"], | |
| ["abort"], | |
| ["askdo"], | |
| ["askio", OPOBJ], | |
| ["expinh", OPBYTE, OPPROP, OPOBJ], | |
| ["expinhptr", OPBYTE, OPOBJ], | |
| ["calld", OPBYTE, OPFUNC], | |
| ["getpd", OPBYTE, OPPROP], | |
| ["builtind", OPBYTE, OPBIF], | |
| ["je", OPLABEL], | |
| ["jne", OPLABEL], | |
| ["jgt", OPLABEL], | |
| ["jge", OPLABEL], | |
| ["jlt", OPLABEL], | |
| ["jle", OPLABEL], | |
| ["jnand", OPLABEL], | |
| ["jnor", OPLABEL], | |
| ["jt", OPLABEL], | |
| ["getpself", OPBYTE, OPPROP], | |
| ["getpslfd", OPBYTE, OPPROP], | |
| ["getpobj", OPBYTE, OPOBJ, OPPROP], | |
| ["getpobjd", OPBYTE, OPOBJ, OPPROP], | |
| ["index"], | |
| [], | |
| [], | |
| ["pushpn", OPPROP], | |
| ["jst", OPLABEL], | |
| ["jsf", OPLABEL], | |
| ["jmpd", OPLABEL], | |
| ["inherit", OPBYTE, OPPROP], | |
| ["callext", OPBYTE, OPWORD], | |
| ["dbgret"], | |
| ["cons", OPBYTE], | |
| ["switch", OPSWITCH], | |
| ["argc"], | |
| ["chkargc", OPBYTE], | |
| ["line", OPLINE], | |
| ["frame", OPFRAME], | |
| ["bp", OPLINE], | |
| ["getdblcl", OPWORD, OPWORD, OPWORD], | |
| ["getpptrself", OPBYTE], | |
| ["mod"], | |
| ["band"], | |
| ["bor"], | |
| ["xor"], | |
| ["bnot"], | |
| ["shl"], | |
| ["shr"], | |
| ["new"], | |
| ["delete"]); | |
| sub disasm($) { | |
| my $block = shift; | |
| my @switches; | |
| my $pos = 0, $len = length($block); | |
| my $numswitches = -1; # Number of switch table entries left, | |
| # -1 means not in switch table | |
| # (It's this way so that switch tables with only a default get | |
| # disassembled correctly.) | |
| while ($pos < $len) { | |
| print $pos, "\t"; | |
| # The one case where we're not pointed at an opcode: the end of | |
| # a switch table | |
| if ($numswitches == 0) { | |
| print "default\t-> "; | |
| $pos += printOperand($block, $pos, OPLABEL); | |
| $numswitches = -1; | |
| print "\n", $pos, "\t"; | |
| } | |
| if ($#switches >= 0 && $pos == $switches[0]) { | |
| print "Switch table\n"; | |
| $numswitches = ord2($block, $pos); | |
| $pos += 2; | |
| shift @switches; | |
| } | |
| else { | |
| my $opc = ord1($block, $pos); | |
| $pos++; | |
| if ($opc >= 1 && $opc <= $#opcodes) { | |
| my @opcdata = @{$opcodes[$opc]}; | |
| if ($#opcdata < 0) { | |
| print "Invalid opcode $opc\n"; | |
| last; | |
| } | |
| print $opcdata[0], "\t"; | |
| for (my $i = 1; $i <= $#opcdata; $i++) { | |
| # Handle operand to switch | |
| if ($opcdata[$i] == OPSWITCH) { | |
| @switches = sort(@switches, $pos + ord2s($block, $pos)); | |
| } | |
| $pos += printOperand($block, $pos, $opcdata[$i]); | |
| print ", " if ($i < $#opcdata); | |
| } | |
| } | |
| elsif (($opc & 0xc0) == 0xc0) { # Assignment | |
| print "assign\t"; | |
| my $ext = 0; | |
| if (($opc & 0x1c) == 0x1c) { | |
| $ext = ord1($block, $pos); | |
| $pos++; | |
| } | |
| if (($opc & 3) == 0) { | |
| print "local ", ord2($block, $pos); | |
| $pos += 2; | |
| } elsif (($opc & 3) == 1) { | |
| print "property "; | |
| printProp(ord2($block, $pos)); | |
| $pos += 2; | |
| } elsif (($opc & 3) == 2) { | |
| print "list"; | |
| } else { | |
| print "property pointer"; | |
| } | |
| print " :=" if (($opc & 0x1c) == 0x00); | |
| print " +=" if (($opc & 0x1c) == 0x04); | |
| print " -=" if (($opc & 0x1c) == 0x08); | |
| print " *=" if (($opc & 0x1c) == 0x0c); | |
| print " /=" if (($opc & 0x1c) == 0x10); | |
| print " ++" if (($opc & 0x1c) == 0x14); | |
| print " --" if (($opc & 0x1c) == 0x18); | |
| print " %=" if ($ext == 1); | |
| print " &=" if ($ext == 2); | |
| print " |=" if ($ext == 3); | |
| print " ^=" if ($ext == 4); | |
| print " <<=" if ($ext == 5); | |
| print " >>=" if ($ext == 6); | |
| if (($opc & 0x1c) == 0x14 || ($opc & 0x1c) == 0x18) { | |
| if ($opc & 0x20) { | |
| print " pre"; | |
| } | |
| else { | |
| print " post"; | |
| } | |
| } | |
| else { | |
| print " and discard" if ($opc & 0x20); | |
| } | |
| } | |
| else { | |
| print "Invalid opcode ", $opc, "\n"; | |
| last; | |
| } | |
| if ($numswitches >= 0) { | |
| print "\t-> "; | |
| $pos += printOperand($block, $pos, OPLABEL); | |
| $numswitches--; | |
| } | |
| print "\n"; | |
| } | |
| } | |
| } | |
| sub processXSI(*$) { | |
| my $FH = shift; | |
| $xorseed = read1($FH); | |
| $xorinc = read1($FH); | |
| print " xorseed = ", $xorseed, ", xorinc = ", $xorinc, "\n"; | |
| } | |
| sub processOBJ(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| while ($len > 0) { | |
| my $type = read1($FH); | |
| my $n = read2($FH); | |
| my $size = read2($FH); | |
| my $use = read2($FH); | |
| print " Object "; | |
| printObj($n); | |
| print ":\n Type $type"; | |
| print " (function)" if ($type == 1); | |
| print " (object)" if ($type == 2); | |
| print " (extern)" if ($type == 10); | |
| print "\n Size $size\n"; | |
| print " Size used $use\n"; | |
| my $block; | |
| read($FH, $block, $use); | |
| $block = decode($block); | |
| if ($type == 1) { | |
| disasm($block); | |
| } | |
| else { | |
| print " Workspace ", ord2($block, 0), "\n"; | |
| print " Flags ", ord2($block, 2), "\n"; | |
| print " Free ", ord2($block, 8), "\n"; | |
| print " Reset ", ord2($block, 10), "\n"; | |
| print " Static ", ord2($block, 12), "\n"; | |
| print " Superclasses:"; | |
| my $n = ord2($block, 4); | |
| for (my $i = 0; $i < $n; $i++) { | |
| print " "; | |
| printObj(ord2($block, 14 + 2 * $i)); | |
| } | |
| print "\n"; | |
| my $flags = ord2($block, 2); | |
| my $nprop = ord2($block, 6); | |
| my $pos = 14 + 2 * $n; | |
| if ($flags & 2) { | |
| # Skip the index table if present | |
| $pos += 2 * $nprop; | |
| } | |
| for (my $i = 0; $i < $nprop; $i++) { | |
| my $num = ord2($block, $pos); | |
| my $type = ord1($block, $pos + 2); | |
| my $size = ord2($block, $pos + 3); | |
| print " Property "; | |
| printProp($num); | |
| print ":\n"; | |
| print " Datatype ", $type, "\n"; | |
| print " Size ", $size, "\n"; | |
| print " Flags ", ord1($block, $pos + 5), "\n"; | |
| if ($type == DAT_CODE) { # code | |
| disasm(substr($block, $pos + 6, $size)); | |
| } | |
| elsif ($type == DAT_DEMAND) { | |
| print " implicit contents list\n"; | |
| } | |
| elsif ($type == DAT_SYN) { # property synonym | |
| print " synonym to property "; | |
| printProp(ord2($block, $pos + 6)); | |
| print "\n"; | |
| } | |
| elsif ($type == DAT_REDIR) { # redirection to another object | |
| print " redirection to object "; | |
| printObj(ord2($block, $pos + 6)); | |
| print "\n"; | |
| } | |
| elsif ($type == DAT_TPL2) { # tpl2 | |
| my $i = ord1($block, $pos + 6); | |
| my $intpos = $pos + 7; | |
| for (my $j = 0; $j < $i; $j++) { | |
| print " preposition "; | |
| printObj(ord2($block, $intpos)); | |
| print ":\n verIoVerb "; | |
| printProp(ord2($block, $intpos + 2)); | |
| print "\n ioVerb "; | |
| printProp(ord2($block, $intpos + 4)); | |
| print "\n verDoVerb "; | |
| printProp(ord2($block, $intpos + 6)); | |
| print "\n doVerb "; | |
| printProp(ord2($block, $intpos + 8)); | |
| print "\n flags ", ord1($block, $intpos + 10), "\n"; | |
| $intpos += 16; | |
| } | |
| } | |
| else { | |
| print " "; | |
| printValue($block, $pos + 6, $type); | |
| print "\n"; | |
| } | |
| $pos += 6 + $size; | |
| } | |
| } | |
| $len -= $use + 7; | |
| } | |
| } | |
| sub processFST(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| while ($len > 0) { | |
| my $type = read1($FH); | |
| my $n = read2($FH); | |
| my $size = read2($FH); | |
| my $use = read2($FH); | |
| my $ofs = read4($FH); | |
| print " Object "; | |
| printObj($n); | |
| print ":\n Type $type"; | |
| print " (function)" if ($type == 1); | |
| print " (object)" if ($type == 2); | |
| print " (extern)" if ($type == 10); | |
| print "\n Size $size\n"; | |
| print " Size used $use\n"; | |
| print " Offset $ofs\n"; | |
| $len -= 11; | |
| } | |
| } | |
| sub processINH(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| while ($len > 0) { | |
| my $flag = read1($FH); | |
| my $n = read2($FH); | |
| my $loc = read2($FH); | |
| my $ilc = read2($FH); | |
| my $i = read2($FH); | |
| print " Object "; | |
| printObj($n); | |
| print ":\n Flags $flag\n"; | |
| print " Loc "; | |
| printObj($loc); | |
| print "\n Ilc "; | |
| printObj($ilc); | |
| print "\n Superclasses:"; | |
| for (my $j = 0; $j < $i; $j++) { | |
| print " "; | |
| printObj(read2($FH)); | |
| } | |
| print "\n"; | |
| $len -= (9 + 2 * $i); | |
| } | |
| } | |
| sub processREQ(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| my @reqnames = ("Me", "takeVerb", "strObj", "numObj", "pardon", | |
| "againVerb", "init", "preparse", "parseError", | |
| "cmdPrompt", "parseDisambig", "parseError2", | |
| "parseDefault", "parseAskobj", "preparseCmd", | |
| "parseAskobjActor", "parseErrorParam", "commandAfterRead", | |
| "initRestore", "parseUnknownVerb", "parseNounPhrase", | |
| "postAction", "endCommand", "preCommand", | |
| "parseAskobjIndirect"); | |
| foreach $name(@reqnames) { | |
| return if ($len <= 0); | |
| print " $name: "; printObj(read2($FH)); print "\n"; | |
| $len -= 2; | |
| } | |
| } | |
| sub processFMTSTR(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| my $block; | |
| $len = read2($FH); | |
| read($FH, $block, $len); | |
| $block = decode($block); | |
| my $pos = 0; | |
| while ($pos < $len) { | |
| my $strlen = ord2($block, $pos + 2); | |
| print " ", substr($block, $pos + 4, $strlen - 2), " -> "; | |
| printProp(ord2($block, $pos)); | |
| print "\n"; | |
| $pos += $strlen + 2; | |
| } | |
| } | |
| sub processCMPD(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| my $block; | |
| $len = read2($FH); | |
| read($FH, $block, $len); | |
| $block = decode($block); | |
| my $pos = 0, $i = 0; | |
| while ($pos < $len) { | |
| my $thislen = ord2($block, $pos); | |
| print " " if ($i % 3 == 0); | |
| print substr($block, $pos + 2, $thislen - 2); | |
| print " " if ($i % 3 == 0); | |
| print " => " if ($i % 3 == 1); | |
| print "\n" if ($i % 3 == 2); | |
| $i++; | |
| $pos += $thislen; | |
| } | |
| print "\n" unless ($i % 3 == 0); | |
| } | |
| sub processSPECWORD(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| my $block; | |
| $len = read2($FH); | |
| read($FH, $block, $len); | |
| $block = decode($block); | |
| my $pos = 0; | |
| while ($pos < $len) { | |
| $flags = ord1($block, $pos); | |
| $strlen = ord1($block, $pos + 1); | |
| print " ", substr($block, $pos + 2, $strlen), | |
| ", flags ", $flags, "\n"; | |
| $pos += $strlen + 2; | |
| } | |
| } | |
| sub processVOC(*$) { | |
| my $FH = shift; | |
| my $len = shift; | |
| my $pos = 0; | |
| while ($pos < $len) { | |
| my $len1 = read2($FH); | |
| my $len2 = read2($FH); | |
| my $prpnum = read2($FH); | |
| my $objnum = read2($FH); | |
| my $classflg = read2($FH); | |
| my $block; | |
| read($FH, $block, $len1 + $len2); | |
| $block = decode($block); | |
| print " ", substr($block, 0, $len1); | |
| print " ", substr($block, $len1, $len2) if ($len2 != 0); | |
| print "\n Prpnum: "; | |
| printProp($prpnum); | |
| print "\n Object: "; | |
| printObj($objnum); | |
| print "\n Class flags: $classflg\n"; | |
| $pos += 10 + $len1 + $len2; | |
| } | |
| } | |
| if ($#ARGV >= 1 && $ARGV[0] eq '-s') { | |
| unless ($return = do $ARGV[1]) { | |
| warn "couldn't parse file: $@" if $@; | |
| warn "couldn't do $ARGV[1]: $!" unless defined ($return); | |
| warn "couldn't run $ARGV[1]" unless $return; | |
| } | |
| splice(@ARGV, 0, 2); | |
| } | |
| if ($#ARGV != 0) { | |
| die "Usage: untads [-s symbols] file.gam\n"; | |
| } | |
| open(GAM, $ARGV[0]) || die("Couldn't find file $ARGV[0]"); | |
| # Assume for now that it is a TADS game; skip to the header flags | |
| seek(GAM, 20, SEEK_SET); | |
| $flags = read1(GAM); | |
| $crypt = $flags & 8; | |
| seek(GAM, 48, SEEK_SET); | |
| for (;;) { | |
| $namelen = read1(GAM); | |
| read(GAM, $name, $namelen); | |
| $nextofs = read4(GAM); | |
| $curofs = tell(GAM); | |
| $sectlen = $nextofs - $curofs; | |
| if ($name eq '$EOF') { | |
| print "\$EOF marker\n"; | |
| close GAM; | |
| exit(0); | |
| } | |
| print "Section $name: $sectlen bytes ($curofs to $nextofs)\n"; | |
| processXSI(GAM, $sectlen) if ($name eq 'XSI'); | |
| processOBJ(GAM, $sectlen) if ($name eq 'OBJ'); | |
| processFST(GAM, $sectlen) if ($name eq 'FST'); | |
| processINH(GAM, $sectlen) if ($name eq 'INH'); | |
| processREQ(GAM, $sectlen) if ($name eq 'REQ'); | |
| processFMTSTR(GAM, $sectlen) if ($name eq 'FMTSTR'); | |
| processCMPD(GAM, $sectlen) if ($name eq 'CMPD'); | |
| processSPECWORD(GAM, $sectlen) if ($name eq 'SPECWORD'); | |
| processVOC(GAM, $sectlen) if ($name eq 'VOC'); | |
| seek(GAM, $nextofs, SEEK_SET); | |
| } | |
Xet Storage Details
- Size:
- 21 kB
- Xet hash:
- a8b09d0cd0194725d79ad5660c56fd823500a792358f8844f82775fa68779b17
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.