ucdump.c

Go to the documentation of this file.
00001 /*
00002  Ultima 7 usecode dump/disassembly utility
00003  Distributed under GPL
00004 
00005  Note:
00006   - the source code comments below are possibly SPOILERS. You have beed warned.
00007 
00008  Maintainer:
00009   Maxim S. Shatskih aka Moscow Dragon (maxim__s@mtu-net.ru)
00010  
00011  History:
00012  - originally written June 99 by Maxim S. Shatskih aka Moscow Dragon (maxim__s@mtu-net.ru)
00013  Thanks to Keldon Jones (keldon@umr.edu)
00014   and Wouter Dijklslag aka Wody Dragon (wody@wody.demon.nl) for their help
00015  11-Oct-99
00016   - added "function search by opcode & intrinsic" feature
00017   - added "unknown opcode & intrinsic counting" feature
00018  12-Oct-99
00019   - the good deal of intrinsic functions is now known
00020  6-May-00
00021   - the current version
00022  
00023  The source must be buildable on any C compiler and runnable on any OS
00024   (tested on Win32 and Linux).
00025  See source file comments for the description of usecode opcodes & intrinsic functions
00026   (the latter one differs between BG & SI)
00027  
00028  Some general usecode ideas:
00029  - usecode functions 0x0-0x3ff are shape handlers - called on double-clicks & other
00030   event with appropriate shapes
00031  - usecode functions 0x401-0x4ff are NPC handlers - called on double-clicks & other
00032   event with appropriate NPCs - NPCID + 0x400 (0x401 for Iolo, 0x417 for LB in BG,
00033   0x41f for Rotoluncia in SI etc).
00034  - usecode functions 0x500-0x5ff is for Wisps & guards (nonNPC characters able to talk)
00035   (these ranges seems to be hardcoded)
00036  - stack machine used to execute bytecodes
00037  - the machine's state is:
00038   stack
00039   local variables(forgotten on function exit, first N of them are call arguments -
00040           first pushed is 0, next are 1, 2...)
00041   game flags
00042   ItemRef (???seems to be valid only for top-level functions-event handlers
00043         or maybe is persistent till quitting usecode executuion???)
00044   EventID (???seems to be valid only for top-level functions-event handlers
00045         or maybe is persistent till quitting usecode executuion???)
00046  - game flags are bytes treated as booleans (0/1), persistent across engine shutdown/restart
00047   and stored as a simple array (??? 0 or 1 based. Don't remember. Flag 3 means
00048   - Tetrahedron is down, flag 4 means - Sphere is down) in GAMEDAT\FLAGINIT.
00049  - usecode can also manipulate items & NPCs by means of intrinsic functions
00050  - "add" opcode can sum strings (concatenation). Also it can add integer to string
00051  - any array operations can be peformed on scalar values. In fact, each scalar value is
00052   treated by the array operations as an array with a single element. Vice versa is also
00053   true - for instance, ItemsNearItem() function returns an array. Sometimes it is used in
00054   enum/next loop as an array, but sometimes it is used as an itemref.
00055  - array indices are 1-based as in VB
00056  - itemref is a unique ID of the given item. For NPCs, itemref is (-NPCID). For other items,
00057   itemrefs seems to be non-persistent (not saved to savegame & re-invented on each
00058   engine startup???) indexes into engine's item lists
00059  - there is a value called "referent" which identifies item & stored in U7IBUF
00060     Maybe Itemref is the same thing?
00061  - -356 is always an Itemref for Avatar. So, Avatar's NPC ID is possibly 356.
00062  - usecode execution starts from the event handler function. It is called by the engine
00063   without arguments (double-click) in some cases. ItemRef & EventID are set
00064   before entering usecode.
00065  - the easiest case is double-click on some item. In this case, a usecode event handler
00066   function called. Function ID usually matches the shape's Type number
00067   or is (NPCID + 0x400) for NPCs.
00068   ItemRef is set to the item double-clicked, EventID is set to 1 (double-click).
00069  - other causes for the engine to call usecode function:
00070   - events scheduled by intrinsic functions 1 & 2 have EventID 2
00071   - item is put on another item - the underlying item's handler is called with EventID 3
00072     (Penumbra's plaque)
00073   - usecode Egg - also calls a function with EventID 3
00074   - use item as a weapon (flammable oil) - EventID 4
00075   - EventID 5 and 6 - placing the item somewhere in the NPC's inventory
00076     (ring of invisibility)
00077   - NPC being beaten to death (examples: Hook in BG, Dracothaxus in FoV,
00078           Pomdirgun & Rotoluncia in SI) - 7 in SI???
00079   - Avatar & NPC approaching to some distance??? - 9 in SI
00080  - hex coords to sextant coords formula (same in BG and SI )
00081   ( x - 933 ) / 10, ( y - 1134 ) / 10
00082 */
00083 
00084 #include <stdio.h>
00085 #include <stdlib.h>
00086 #include <string.h>
00087 /*
00088  Opcode flags
00089  Just a 16bit word
00090 */
00091 #define IMMED         1
00092 /* Immediate 1 byte operand */
00093 #define IMMED_BYTE        2
00094 /* Will print a part of data string as comment */
00095 #define DATA_STRING       4
00096 /* Will add command offset before printing */
00097 #define RELATIVE_JUMP       8
00098 /* Will print third byte as decimal after comma */
00099 #define CALL          16
00100 /* Will print in square brackets */
00101 #define VARREF          32
00102 /* Will print in square brackets with "flag:" prefix */
00103 #define FLGREF          64
00104 /* Call of usecode function using extern table */
00105 #define EXTCALL         128
00106 
00107 /* Opcode descriptor */
00108 typedef struct _opcode_desc
00109 {
00110   /* Mnemonic - NULL if not known yet */
00111   const char* mnemonic;
00112   /* Number of operand bytes */
00113   int nbytes;
00114   /* Type flags */
00115   unsigned char type;
00116 } opcode_desc;
00117 
00118 /* Opcode table - common to BG & SI */
00119 static opcode_desc opcode_table[] =
00120 {
00121   { NULL, 0, 0 },           /* 00 */
00122   { NULL, 0, 0 },           /* 01 */
00123   /* Next iteration of For Each-style loop
00124     Operands are 1,2, <current>, <array variable>, <jump>
00125     <current> is set to next value from <array variable>
00126     Jumps to <jump> if array ended
00127     ??? what are 1,2? Are they used?
00128   */
00129   { "next", 10, VARREF | RELATIVE_JUMP },     /* 02 */
00130   { NULL, 0, 0 },           /* 03 */
00131   /* Asks user to select one the talk answers
00132     Jumps where specified if no answer available
00133   */
00134   { "ask", 2, RELATIVE_JUMP },        /* 04 */
00135   /* Pops a value from the top of stack, jump if false, zero or empty array */
00136   { "jne", 2, RELATIVE_JUMP },        /* 05 */
00137   /* jump */
00138   { "jmp", 2, RELATIVE_JUMP },        /* 06 */
00139   /* Pops the top-of-stack string & jumps if the string is NOT the last talk answer 
00140     ??? first operand seems to be always 1
00141   */
00142   { "jmpa", 4, IMMED | RELATIVE_JUMP},      /* 07 */
00143   { NULL, 0, 0 },           /* 08 */
00144   /* Adds two values on the stack, popping them & pushing result 
00145     Can be used to add integer to string - in this case integer is converted to string 
00146     and 2 strings are concatenated */
00147   { "add", 0, 0 },          /* 09 */
00148   /* Decrements second value of the stack by first value, popping them & pushing result */
00149   { "sub", 0, 0 },          /* 0a */
00150   /* Divides second value of the stack by first value, popping them & pushing result */
00151   { "div", 0, 0 },          /* 0b */
00152   /* Multiplies two values on the stack, popping them & pushing result */
00153   { "mul", 0, 0 },          /* 0c */
00154   /* Divides second value of the stack by first value, popping them & pushing reminder */
00155   { "mod", 0, 0 },          /* 0d */
00156   /* Boolean AND on two values on stack, popping them & pushing result
00157     Top-of-stack variable is the left one in case of string addition */
00158   { "and", 0, 0 },          /* 0e */
00159   /* Boolean OR on two values on stack, popping them & pushing result */
00160   { "or", 0, 0 },           /* 0f */
00161   /* Inverts a boolean value on top of stack */
00162   { "not", 0, 0 },          /* 10 */
00163   { NULL, 0, 0 },           /* 11 */
00164   /* Pops a stack value to given local variable */
00165   { "pop", 2, VARREF },         /* 12 */
00166   /* Pushes a TRUE boolean value on the stack */
00167   { "push\ttrue", 0, 0 },         /* 13 */
00168   /* Pushes a FALSE boolean value on the stack */
00169   { "push\tfalse", 0, 0 },        /* 14 */
00170   { NULL, 0, 0 },           /* 15 */
00171   /* Pops 2 values from the stack, pushes boolean value - 
00172     TRUE if (top-of-stack-1) value "?" then top-of-stack
00173     (where "?" can be greater, greater or equal etc...)
00174   */
00175   { "cmpgt", 0, 0 },          /* 16 */
00176   { "cmplt", 0, 0 },          /* 17 */
00177   { "cmpge", 0, 0 },          /* 18 */
00178   { "cmple", 0, 0 },          /* 19 */
00179   { "cmpne", 0, 0 },          /* 1a */
00180   { NULL, 0, 0 },           /* 1b */
00181   /* Adds a string from data segment to string register current contents */
00182   { "addsi", 2, DATA_STRING },        /* 1c */
00183   /* Pushes a string value given by 16bit string offset in data segment to stack */
00184   { "pushs", 2, DATA_STRING },        /* 1d */
00185   /* Pops specified number of values from stack, builds an array from them
00186     & pushes it on the stack 
00187     Pushes the empty array to top of the stack if operand is 0
00188   */
00189   { "arrc", 2, IMMED },         /* 1e */
00190   /* Pushes immediate 16bit integer to stack */
00191   { "pushi", 2, IMMED },          /* 1f */
00192   { NULL, 0, 0 },           /* 20 */
00193   /* Pushes a local variable on stack */
00194   { "push", 2, VARREF },          /* 21 */
00195   /* Compares 2 values on the stack, pops them & pushes TRUE if they are equal */
00196   { "cmpeq", 0, 0 },          /* 22 */
00197   { NULL, 0, 0 },           /* 23 */
00198   /* Calls a usecode function - function number is 0-based index to externs array */
00199   { "call", 2, VARREF | EXTCALL },      /* 24 */
00200   /* Return from function without result returned on the stack */
00201   { "ret", 0, 0 },          /* 25 */
00202   /* Uses the top-of-stack value to index (1-based) the array variable, 
00203     pops index & pushes result - the value from the array */
00204   { "aget", 2, VARREF },          /* 26 */
00205   { NULL, 0, 0 },           /* 27 */
00206   { NULL, 0, 0 },           /* 28 */
00207   { NULL, 0, 0 },           /* 29 */
00208   { NULL, 0, 0 },           /* 2a */
00209   { NULL, 0, 0 },           /* 2b */
00210   /* ??? Looks like to be the same as "exit"
00211     ??? Suggestion: maybe "exit" is for functions, while "exit2" is for event handlers? */
00212   { "exit2", 0, 0 },          /* 2c */
00213   /* Pop the top-of-stack value & sets it as a return value */
00214   { "popr", 0, 0 },         /* 2d */
00215   /* Opens a new For Each-style enumeration loop.
00216     Always followed by "next" opcode??? */
00217   { "enum", 0, 0 },         /* 2e */
00218   /* Adds local variable's string value to string register current contents */
00219   { "addsv", 2, VARREF },         /* 2f */
00220   /* If (top-of-stack - 1) value is in top-of-stack array, pushes true, 
00221     otherwise pushes false - after popping array & value */
00222   { "in", 0, 0 },           /* 30 */
00223   /* Something strange with 2 varrefs (???) as operands
00224     Appears in BG only - in talk to Raymundo in the Theatre - trying to sing a song
00225     I -am- the Avatar
00226   */
00227   { "???", 4, 0 },          /* 31 */
00228   /* Return with a result returned on the stack */
00229   { "retr", 0, 0 },         /* 32 */
00230   /* Displays the string register value (to current talk, sign, scroll or book),
00231     string register emptied */
00232   { "say", 0, 0 },          /* 33 */
00233   { NULL, 0, 0 },           /* 34 */
00234   { NULL, 0, 0 },           /* 35 */
00235   { NULL, 0, 0 },           /* 36 */
00236   { NULL, 0, 0 },           /* 37 */
00237   /* Calls engine's intrinsic function with specified number of parameters 
00238     (popping them). The return value remains on stack */
00239   { "callis", 3, CALL },          /* 38 */
00240   /* Calls engine's intrinsic function with specified number of parameters 
00241     (popping them). No return value  */
00242   { "calli", 3, CALL },         /* 39 */
00243   { NULL, 0, 0 },           /* 3a */
00244   { NULL, 0, 0 },           /* 3b */
00245   { NULL, 0, 0 },           /* 3c */
00246   { NULL, 0, 0 },           /* 3d */
00247   /* Pushes identifier of the item ( for which the usecode event handler is called ) 
00248     on the stack */
00249   { "push\titemref", 0, 0 },        /* 3e */
00250   /* Aborts the function & all usecode execution, returning to the engine */
00251   { "exit", 0, 0 },         /* 3f */
00252   /* Removes all answers from the current talk */
00253   { "cla", 0, 0 },          /* 40 */
00254   { NULL, 0, 0 },           /* 41 */
00255   /* Pushes game flag's value (boolean) on the stack */
00256   { "pushf", 2, FLGREF },         /* 42 */
00257   /* Pops the stack value to the game flag (boolean) */
00258   { "popf", 2, FLGREF },          /* 43 */
00259   /* Pushes an immediate byte to the stack */
00260   { "pushbi", 1, IMMED_BYTE },        /* 44 */
00261   { NULL, 0, 0 },           /* 45 */
00262   /* Uses the top-of-stack value to index (1-based) the array variable, (top-of-stack - 1)  as
00263     the new value, and updates a value in the array slot (local variable specified
00264     in the operand) */
00265   { "aput", 2, VARREF },          /* 46 */
00266   /* Call of usecode function - function # is the operand
00267     ???Suggestion: functions are divided into event handlers (called by external code
00268     and the usecode) and functions (called by usecode only). "calle" is used to
00269     call the event handler from the usecode, while "call" is used to call a function
00270   */
00271   { "calle", 2, EXTCALL },        /* 47 */
00272   /* Pushes the cause of usecode event handler call on the stack */
00273   /*  (double-click on item is be 1, NPC death seems to be 7 in SI and 2 in BG ) */
00274   { "push\teventid", 0, 0 },        /* 48 */
00275   { NULL, 0, 0 },           /* 49 */
00276   /* Pops the value from the stack and adds it to array on the top of stack */
00277   /*  or pops both values from the stack, builds an array from them */
00278   /*  & pushes it on the stack if neither of them are arrays */
00279   { "arra", 0, 0 },         /* 4a */
00280   /* Pops the top-of-stack value & sets the cause of usecode event handler to it
00281     (double-click on item is be 1, NPC death seems to be 7 in SI and 2 in BG )
00282     Used to call event handlers from the usecode
00283   */
00284   { "pop\teventid", 0, 0 },       /* 4b */
00285 };
00286 
00287 /* Embedded function table for Black Gate */
00288 static const char* bg_func_table[] = 
00289 {
00290   /* Random1(Hi) - Returns a random integer or from 1 to Hi inclusively
00291     Used to select a random party member for remarks
00292   */
00293   "Random1",            /* 0 */
00294   /* ExecuteMacro(itemref, array).
00295     Executes a macro for this itemref.
00296     Macro is an array of opcodes (bytes) and operands (words)
00297     Macro opcodes:      
00298       either 0x35 or 0x45 - deletes an item (fire caused by the flaming oil)
00299       0x46 <integer> - sets item's frame number to integer (rotating crossbeam
00300             while opening the portcullis gate)
00301       0x52 <string> - the same as ItemSay() function
00302       0x55 <usecode function number> - call a usecode event for the item
00303       0x56 <integer> - Guardian's phrase with given number
00304     All spells use this function - if current weather is 3 - 606 is called
00305     instead of the second part of the spell engine (magic negation field at Ambrosia,
00306       Armageddon is blocked after shouting 1st mantra & before shouting second).
00307     Macro execution is asynchronous to the commands following ExecuteMacro -
00308       ??? macro opcodes are executed one per redraw frame counted???
00309     Macro execution seems to be interruptable by opening inventory.
00310     The initial red moongate uses this to grow in height and then call another
00311     usecode function.
00312     This initial sequence is fired from Iolo's usecode hanlder.
00313   */
00314   "ExecuteMacro",           /* 1 */
00315   /* ScheduleMacro(itemref, array, delay)
00316     The same as above - but schedules a macro for execution later
00317     Delay seems to be in frame counter units???
00318     (use a serpent venom on somebody - first raises stats, then - after some time
00319       - lower them)
00320     Can also shout exclamations - "Call it... Heads... Its tails..."
00321       - double-click on money.
00322   */
00323   "ScheduleMacro",          /* 2 */
00324   /* SwitchTalkTo(itemref, face_frame) - switches conversation to other NPC,
00325     selecting one of possible face pictures
00326     Shows the face in the bottom of the screen if the face on top was not hidden yet
00327     Switching talk to -277 means - huge reg Guardian semi-transparent face
00328   */
00329   "SwitchTalkTo",           /* 3 */
00330   /* HideNPC(itemref) - hides NPC face away from the conversation */
00331   "HideNPC",            /* 4 */
00332   /* AddAnswer(str_or_array) - adds a answer or set of answers to */
00333   /*  the set of talk threads suggested */
00334   "AddAnswer",            /* 5 */
00335   /* RemoveAnswer(str) - removes a given string from set of talk threads suggested */
00336   "RemoveAnswer",           /* 6 */
00337   /* SaveTalkStartNew() - saves the conversation state & starts */
00338   /*  new blank set of talk answers suggested */
00339   "SaveTalkStartNew",         /* 7 */
00340   /* RestoreTalk() - restores set of answers suggested */
00341   /*  as was saved in SaveTalkStartNew() */
00342   "RestoreTalk",            /* 8 */
00343   /* EmptyTalk() - removes all talk answers
00344     Looks like the same as "cla" opcode
00345   */
00346   "EmptyTalk",            /* 9 */
00347   /* GetAnswer() - asks user to select one of the talk threads & returns it
00348     Practically the same functionality as "ask" opcode
00349   */
00350   "GetAnswer",            /* a */
00351   /* GetAnswerIndex() - asks user to select one of the talk threads
00352     & returns the 1-based index of it instead the text itself
00353   */
00354   "GetAnswerIndex",         /* b */
00355   /* AskNumber(min, max, step, default) - asks user a number by showing a slider */
00356   /*  with given parameters */
00357   "AskNumber",            /* c */
00358   /* SetItemType(itemref, type) - changes a type of given item to the new value */
00359   "SetItemType",            /* d */
00360   /* AreItemsNearNPC(NPCID, type, distance) - returns boolean whether there are 
00361     items of such Type at the given distance from the given NPC.
00362     Distance -1 means - screen borders
00363   */
00364   "AreItemsNearNPC",          /* e */
00365   /* PlaySoundEffect(sound_num) - plays a given effect soundtrack
00366   */
00367   "PlaySoundEffect",          /* f */
00368   /* Random2(lo, hi) - returns a random integer from lo to hi inclusively */
00369   "Random2",            /* 10 */
00370   /* GetItemType(itemref) - returns a "type" value of the item */
00371   "GetItemType",            /* 11 */
00372   /* GetItemFrame(itemref) - returns a "frame" value of the item */
00373   "GetItemFrame",           /* 12 */
00374   /* SetItemFrame(itemref, frame) - sets a "frame" value for the item */
00375   "SetItemFrame",           /* 13 */
00376   /* GetItemQuality(itemref) - returns a "quality" value of the item */
00377   "GetItemQuality",         /* 14 */
00378   /* SetItemQuality(itemref, quality) - sets a "quality" value for the item
00379     (Penumbra's plaque)
00380     Returns old quality???
00381   */
00382   "SetItemQuality",         /* 15 */
00383   NULL,             /* 16 */
00384   NULL,             /* 17 */
00385   /* GetItemCoords(itemref) - parameter is itemref as returned by
00386       ItemSelectModal(), returns array
00387     The array is 2 item's coordinates - array[1], array[2]
00388   */
00389   "GetItemCoords",          /* 18 */
00390   NULL,             /* 19 */
00391   NULL,             /* 1a */
00392   /* GetNPCID(itemref) - returns NPCID from Itemref - possibly does-nothing function???
00393     Accepts values like -2 for Spark or -356 for Avatar on input
00394   */
00395   "GetNPCID",           /* 1b */
00396   /* GetNPCClass(NPCID) - returns a number specifying the NPC class -
00397     fighter/sage/shopkeeper etc. Used in exclamations like "Oh my aching back!"
00398     10 seems to be Noble (Finnegan & his aching back :-) )
00399     Or maybe I'm wrong and this is GetNPCActivity???
00400   */
00401   "GetNPCClass",            /* 1c */
00402   /* SetNPCActivity(NPCID, activity) - sets an NPC activity
00403     11 - Loiter (leave with "go home")
00404     15 - Wait (leave with "wait here")
00405     ??? in cheat menu order?
00406   */
00407   "SetNPCActivity",         /* 1d */
00408   /* JoinNPC(itemref) - joins NPC to the party
00409     This seems to only set Party flag on - the same effect as doing this in cheat mode
00410     ???in BG, unusual companions (joined by cheat) will not be shown in Inventory/Ztats
00411       gumps. Where this information (whether the NPC is a usual companion) is kept?
00412     In SI, they will (I saw Rotoluncia's Ztats - though no 2 keys + a wand usually taken
00413       from her when dead in inventory. Trying to display Vasculio's Ztats hung
00414       the game) - but headless :-)
00415     Once more - it is kept somewhere...
00416   */
00417   "JoinNPC",            /* 1e */
00418   /* DismissNPC(itemref) - dismisses NPC from the party */
00419   "DismissNPC",           /* 1f */
00420   /* GetNPCStat(NPCID, property_id) -
00421     returns NPC's statistic value (9 is food level, 0 is Strength, 3 is Hits, )
00422     (1 & 4 seems to be Training & Combat???)
00423     Vas Mani (Restoration) spell is a good example
00424   */
00425   "GetNPCStat",           /* 20 */
00426   /* ChangeNPCStat(itemref, property_id, delta) - 
00427     increments (or decrements for negative delta) NPC's statistic value.
00428   */
00429   "ChangeNPCStat",          /* 21 */
00430   /* AvatarNPCID() - returns Avatar's NPC ID */
00431   "AvatarNPCID",            /* 22 */
00432   /* GetPartyMembers() - returns the array of NPCs which are party members currently */
00433   /*  ??? NPC IDs returned??? */
00434   "GetPartyMembers",          /* 23 */
00435   /* CreateItem(Type) - creates an item of given Type, returns itemref
00436     Must be followed by SetItemFrame()???
00437   */
00438   "CreateItem",           /* 24 */
00439   NULL,             /* 25 */
00440   /* InsertNewItem(Coords) Parameter is an array of c
00441   */
00442   "InsertNewItem",          /* 26 */
00443   /* GetNPCName(npc_id_or_array) - returns an NPC name by NPC ID
00444     Returns an array of names if array was specified
00445   */
00446   "GetNPCName",           /* 27 */
00447   /* Counts the amount of party gold if called as Func28(-357, 644, -359, -359)
00448     644 is for sure the Type of gold heap
00449     649 is for sure the Type of serpent venom
00450     839 for hourglass
00451     Last parameter seems to be the frame number of the items - -359 means "any"
00452   */
00453   NULL,             /* 28 */
00454   /* Returns itemref of the given item in Avatar's (or party's??? - parameter 4???)
00455       posessions
00456     0 if no such item
00457     Func28(-357, 761, -359, -359) - spellbook.
00458     Func28(-357, 839, -359, 0) - Nicodemus's hourglass
00459     (known as Hourglass Of Fate in SI).
00460     Third parameter is quality
00461   */
00462   NULL,             /* 29 */
00463   /* GetContainerItems(container_itemref, type, quality, ???) - returns an array */
00464   /*  ??? of itemrefs */
00465   "GetContainerItems",          /* 2a */
00466   /* Reduces the amount of party gold if called Func2b(delta, 644, -359, -359, true) 
00467     Reduces the amount of party eggs if called Func2b(delta, 377, -359, 24, true) 
00468   */
00469   NULL,             /* 2b */
00470   /* Increases the amount of party gold if called Func2c(delta, 644, -359, -359, true) 
00471     Returns false if party is overloaded & cannot accept this
00472   */
00473   NULL,             /* 2c */
00474   /* Not used at all */
00475   NULL,             /* 2d */
00476   /* PlayMusic(song#, itemref) - plays a MIDI music track. */
00477   /*  Song# = 255 means - mute the MIDI channel. */
00478   "PlayMusic",            /* 2e */
00479   /* IsNPCInParty(npc_id) - returns true if given NPC is in party, */
00480   /*  otherwise returns false */
00481   "IsNPCInParty",           /* 2f */
00482   /* Takes a Type(???), returns an array of all visible items/NPCs of this type */
00483   NULL,             /* 30 */
00484   /* IsNPCxxx(itemref) - returns true if given NPC is ????? */
00485   NULL,             /* 31 */
00486   /* DisplaySign(gump#, array_of_strings) - displays a sign/plaque */
00487   /*  of given gump number & given text (array of strings or single string). */
00488   "DisplaySign",          /* 32 */
00489   /* ItemSelectModal() - switches the engine to cross-cursor "use" mode.
00490     Returns an itemref of the item selected by user by single-click. 
00491     Does not return till user will single-click on something.
00492     The entity returned can be treated as array with coords at array[2], array[3]
00493   */
00494   "ItemSelectModal",          /* 33 */
00495   /* Not used at all */
00496   NULL,             /* 34 */
00497   /* ItemsNearItem(itemref, type, distance, ???) - returns an array of items
00498       of given Type which are closer then Distance to the item specified by Itemref
00499       Parameter 4 is possibly frame number to compare???
00500       Type of -1 means - all NPC??? (Fear spell)
00501     (Powder keg & cannon ball near the cannon)
00502   */
00503   "ItemsNearItem",          /* 35 */
00504   /* Takes an NPCID as a single parameter, seems to return the amount of free
00505     space in its inventory
00506   */
00507   NULL,             /* 36 */
00508   NULL,             /* 37 */
00509   /* GetTimeHour() - returns hour part of current game time - 0-23 */
00510   "GetTimeHour",            /* 38 */
00511   /* GetTimeMinute() - returns minute part of current game time - 0-59 */
00512   "GetTimeMinute",          /* 39 */
00513   /* GetItemRef(NPCID) - returns Itemref from NPCID - possibly does-nothing function???
00514   */
00515   "GetItemRef",           /* 3a */
00516   NULL,             /* 3b */
00517   /* GetNPCxxx(NPCID) - returns the same??? NPC mode as set in function 3d */
00518   NULL,             /* 3c */
00519   /* SetNPCxxx(NPCID, mode) - ???sets some NPC mode
00520     often called with mode=2 before entering combat
00521     ???alignment
00522   */
00523   NULL,             /* 3d */
00524   /* TeleportItem(itemref, array) - teleports an NPC
00525     array[1] - X coord
00526     array[2] - Y coord
00527     array[3] - height
00528     Help spell calls this with (3a8, 47a, 0) array - LB throne room
00529     Recall spell calls this with (5aa, 500, 0) array (House of the Dead) - in some cases.
00530     Also interesting location - (217, 489, 0) - back from the Sphere.
00531     Function 6cf teleports there is Sphere is not destroyed & party does not
00532       posesses item 0x347
00533       (the hourglass, maybe parameter 4 in 0x28 checks the Frame number to
00534         check whether the hourglass is enchanted???
00535       BTW - there is a known bug in BG that sometimes you can enter the Sphere
00536         with non-enchanted hourglass - cause???)
00537     What are these locations?
00538   */
00539   "TeleportItem",           /* 3e */
00540   /* VanishNPC(NPCID) - only parameter is NPCID, no return value
00541     Called in eventid 2 for the plaque - never mind what - and NPC -23 (LB)
00542       - plaque falling on LB's head
00543     Called on Weston (-69) when LB frees him
00544     Called on Batlin (GetNPCID(-26)) when he vanishes, feeling the Cube at Avatar's
00545       posession
00546     Called on -214 in Vesper - when Yvella want to tell Catherine's father & Mara
00547       something....
00548     Called on Kreg/Kraig(-245) after he laughs when invis. potion is given to him
00549     Called on Addom (-164) after Brion attaches to crystal & completes the orrery
00550       viewer
00551     Called on -156 (Balayna???) when Rankin says he did not see Balayna for some time
00552     Called on all Scara Brae ghosts in For Each loop when Forsythe jumps into the Well
00553     The exact logic is not clear - what appears with the vanished NPC?
00554     Where it is teleported???
00555   */
00556   "VanishNPC",              /* 3f */
00557   /* ItemSay(itemref, str) - displays a string on the screen near the specified item */
00558   "ItemSay",            /* 40 */
00559   NULL,             /* 41 */
00560   /* GetItemZCoord(itemref) - returns Z coordinate of the item */
00561   "GetItemZCoord",          /* 42 */
00562   /* SetItemZCoord(itemref, coord) - sets a Z coordinate for the item */
00563   "SetItemZCoord",          /* 43 */
00564   /* GetWeather() - returns current weather - 0-3
00565     3 seems to negate magic
00566   */
00567   "GetWeather",           /* 44 */
00568   /* SetWeather(weather) - sets new current weather - 0-3 */
00569   "SetWeather",           /* 45 */
00570   /* Sits down NPC??? */
00571   NULL,             /* 46 */
00572   /* SummonCreature(Type, boolean) - engine under Kal Bet Xen (Swarm), Kal Xen (???)
00573     an Kal Vas Xen (Summon).
00574     Summons a creature of given Type. Second parameter is true only for Kal Vas Xen
00575     Return value???
00576   */
00577   "SummonCreature",         /* 47 */
00578   /* Shows a map of Britannia. Double-click on the map, Vas Wis (Peer) spell or reading
00579     the Brommer's Britannia book
00580   */
00581   "ShowMap",            /* 48 */
00582   /* KillNPC(itemref) - kills the given NPC. Owen's suicide is this function alone.
00583     Plaque falling on LB's head - KillNPC, then VanishNPC.
00584     Balayna takes the vial from you & dies - this function alone
00585     Death bolt spell uses it too.
00586     These are all occurences.
00587   */
00588   "KillNPC",            /* 49 */
00589   /* Some kind of comparing 2 numbers (2 params) - returns boolean
00590     Used to compare somebody' Strength to item's Quality
00591   */
00592   "???",              /* 4a */
00593   /* SetNPCAttackMode(NPCID, mode) - sets a combat mode for the NPC
00594     Mode 7 is Fleeing - others are in men
00595     Fear spell, using dirty diapers on people
00596       (it is the same as Fear spell) - mode 7
00597     LB attacking in an LB cheat room - mode 0
00598   */
00599   "SetNPCAttackMode",         /* 4b */
00600   /* SetTargetNPCToAttack(attacker_NPCID, target_NPCID) - sets target NPC to attack
00601     ??? how it is called in a cheat menu?
00602     Usually called with Avatar as target NPC
00603   */
00604   "SetTargetNPCToAttack",     /* 4c */
00605   /* CloneNPC(NPCID) - clones an NPC - Clone spell
00606     Returns something - return value never used.
00607   */
00608   "CloneNPC",           /* 4d */
00609   /* Not used at all */
00610   NULL,             /* 4e */
00611   /* ShowCrystalBall(coords_array) - shows a crystal ball view of a given world point
00612   */
00613   "ShowCrystalBall",          /* 4f */
00614   /* ShowWizardEye(parm1, parm2) - shows a view for telescope or Wizard Eye spell
00615     For telescope - ShowWizardEye(10000, 1000)
00616     For Wizard Eye spell - ShowWizardEye(45, 200)
00617   */
00618   "ShowWizardEye",          /* 50 */
00619   /* ResurrectNPC(itemref) - resurrects an NPC. Itemref is a dead body's itemref
00620     Returns false is cannot resurrect - LB in this case says "Alas..." and then
00621       about burial.
00622     ???what is the cause of such LB's behaviour? When ResurrectNPC returns false?
00623   */
00624   "ResurrectNPC",           /* 51 */
00625   /* AddSpellToBook(spellnum, 0, spellbook_itemref) - Adds a new spell to the spellbook
00626     Returns false if there was already such spell in the book
00627     Maybe calling with 1 (never called such in BG) will remove the spell from the book?
00628   */
00629   "AddSpellToBook",         /* 52 */
00630   /* ExecuteSprite(sprite, coordx, coordy, speedx, speedy, ???, ???)
00631     - executes an explosion-like sprite
00632     First parameter is sprite number in SPRITES.VGA
00633     speedx & speedy are non zero is the sprite floats away (smoke from smokebomb)
00634     Last parameters is usually -1 (not so for smokebomb)
00635   */
00636   "ExecuteSprite",          /* 53 */
00637   /* Powder keg burst??? */
00638   NULL,             /* 54 */
00639   /* DisplayBook(itemref) - Displays book or scroll
00640     Text will be displayed further by "say" opcode
00641   */
00642   "DisplayBook",            /* 55 */
00643   /* StopTime(???) - freezes all NPCs for some time. Parameter is duration???? 
00644     Type 6be object (Egg???) calls this with its Quality as a parameter on eventid 3
00645     Also called in Stop Time spell with the parameter 100
00646   */
00647   "StopTime",           /* 56 */
00648   /* CauseLight(duration) - Glimmer/Light/Great Light spell logic
00649     duration is 110 for Glimmer, 500 for Light, 5000 for Great Light
00650   */
00651   "CauseLight",           /* 57 */
00652   /* itemref as a parameter, returns some boolean... 
00653     ???NPC is on barge
00654     At least true return from this blocks Mark spell - even mantra is not shouted
00655   */
00656   "???",              /* 58 */
00657   /* CauseEarthquake(???) - causes an earthquake.
00658     Parameter is 40 for Armageddon spell & variable for Tremor spell
00659     Also called from func 85e (Forsythe jumping in the Well of Souls) - with parameter 15
00660   */
00661   "CauseEarthquake",          /* 59 */
00662   /* IsPlayerFemale() - returns 0 if male, 1 if female */
00663   "IsPlayerFemale",         /* 5a */
00664   /* CauseArmageddon() - all logic of Armageddon spell except
00665     shouting mantras, weather change, earthquake, & setting the game flag which
00666     affects Batlin's & LB's behaviour.
00667     Walks through the all NPC list & makes all of the Dead with Hits < 0
00668     Called also in LB's cheat room - "Busted, you thieving scoundrel bastard!"
00669   */
00670   "CauseArmageddon",          /* 5b */
00671   /* Sets NPC to some state - like VanishNPC() or KillNPC() */
00672   "???",              /* 5c */
00673   /* CauseBlackout() - darken the whole screen for some time */
00674   "CauseBlackout",          /* 5d */
00675   /* ArraySize(a) - returns number of elements in the array */
00676   "ArraySize",            /* 5e */
00677   /* Something for Mark spell??? */
00678   "???",              /* 5f */
00679   /* Called only once, something in a Recall spell
00680     Called after setting activity to 31 (Follow Avatar) to all party members
00681     and resetting flag 0x39 (what is it???)
00682     No return value, single parameter which is itemref
00683   */
00684   NULL,             /* 60 */
00685   /* Called only once, parameters are NPC strength, 0, 12, 3
00686     Return value exists but not used
00687   */
00688   NULL,             /* 61 */
00689   /* IsUnderTheRoof() - returns true if the party is under the roof.
00690     Sextant will not work under the roof.
00691     Also used in some place (???) where it causes a shout "Try it outside!"
00692   */
00693   "IsUnderTheRoof",         /* 62 */
00694   /* SetOrreryState(coords_array, status) - sets the state of the planets in Brion's orrery.
00695     Coords are usually ordinary orrery coords - array of 2 integers. (59c, b4c)
00696     status is a small integer which influences the state of the planets.
00697   */
00698   "SetOrreryState",         /* 63 */
00699   /* Not used at all */
00700   NULL,             /* 64 */
00701   /* GetTimerElapsedHours(timer_number) - returns number of hours of game
00702     time elapsed since the time set in the timer.
00703     Timer valus are persistent beyound doubt - maybe GAMETIM(A) files?
00704     Timer 0x0 - Kliftin at Jhelom making the flag of Honor
00705     Timers 0x1 - Bennie in LB's house giving free meal
00706     Timers 0x2-0x4 - Martina, Wench and Roberto at Buc. Den
00707     Timer 0x5 - called from 0x6c3 code deletes all light sources/candles,
00708       serpentine daggers, buckets, victims & bloods - Minoc murder scene.
00709       A well-known game bug! Proximity usecode Egg 0x6c3 is just at
00710       the center of the murder scene with firing distance of 16
00711       (which is exactly the sawmill entrance)
00712       The logic is: if flag 0x122 is not set, set it, and then set the timer
00713       0x5.
00714       Else - if the flag is already set and if 24 game hours elapsed
00715       from timer 0x5 - delete all murder scene objects in the distance
00716       15 aroung the Egg and the Egg iself.
00717       So - entering the sawmill second time after more a day from the first
00718       time entering it will delete the murder scene.
00719       The bug is that sometimes it deletes just at the first time and
00720       the player never sees it and cannot pick a dagger.
00721     Timer 0x6 - the same with Alagner's body (victim).
00722     Timer 0x8 - poisoning Balayna by Rankin.
00723     Timer 0xa - Jaana healing.
00724       Another well-known game bug! 
00725       If you ask Jaana to heal when she is in the party, and if flag 0x29
00726       is false, than the "period" is set to 5. Otherwise, the "period" is
00727       set to the time elapsed since timer 0xa.
00728       Then, if the period < 4, say "I am sorry..."
00729       Otherwise, Jaana heals - this sets flag 0x29 and timer 0xa.
00730       The bug is that Jaana heals only once per game - and than always
00731         says "I am sorry..."
00732     Cause of both bugs: game timers are broken beyound doubt. Seriously.
00733       Something like GetTimerElapsedHours for 0xa always
00734       returns 0 or at least value < 4 and GetTimerElapsedHours for 0x5
00735       always returns >= 24
00736       Too sad. No chances of fixing it using usecode patches.
00737     Timer 0xb - the last thievery act by the Avatar. (where is it set???)
00738     These are all used timers.
00739   */
00740   "GetTimerElapsedHours",         /* 65 */
00741   /* SetTimer(timer_number) - sets the timer value to the current game time
00742   */
00743   "SetTimer",           /* 66 */
00744   /* Is Avatar wearing a fellowship medallion???? */
00745   NULL,             /* 67 */
00746   /* IsMousePresent() - returns true if mouse is present, false otherwise
00747   */
00748   "IsMousePresent",         /* 68 */
00749   /* GetSpeechTrack() - returns a speech track number previously set by SetSpeech()
00750   */
00751   "GetSpeechTrack",         /* 69 */
00752   NULL,             /* 6a */
00753   NULL,             /* 6b */
00754   NULL,             /* 6c */
00755   NULL,             /* 6d */
00756   NULL,             /* 6e */
00757   /* DeleteItem(itemref) - deletes an item
00758   */
00759   "DeleteItem",           /* 6f */
00760   /* Called only once after initial conversation of just-arrived Avatar
00761     with Iolo
00762   */
00763   NULL,             /* 70 */
00764   NULL,             /* 71 */
00765   NULL,             /* 72 */
00766   NULL,             /* 73 */
00767   NULL,             /* 74 */
00768   /* StartEndGame(boolean) - TRUE is successful endgame (wand against the gate),
00769     FALSE is unsuccessful (passing through the gate)
00770   */
00771   "StartEndGame",           /* 75 */
00772   /* FireCannon(cannon_itemref, fire_direction, ball_type, ???,
00773             cannon_type, cannon_type)
00774     - fires a cannon in specified direction
00775   */
00776   "FireCannon",           /* 76 */
00777   NULL,             /* 77 */
00778   NULL,             /* 78 */
00779   NULL,             /* 79 */
00780   NULL,             /* 7a */
00781   NULL,             /* 7b */
00782   NULL,             /* 7c */
00783   NULL,             /* 7d */
00784   /* PlaySpeech() - plays a speech track set by SetSpeech()
00785   */
00786   "PlaySpeech",           /* 7e */
00787   NULL,             /* 7f */
00788   NULL,             /* 80 */
00789   NULL,             /* 81 */
00790   NULL,             /* 82 */
00791   NULL,             /* 83 */
00792   NULL,             /* 84 */
00793   NULL,             /* 85 */
00794   NULL,             /* 86 */
00795   NULL,             /* 87 */
00796   /* Function 0x88 - GetNPCFlag(Itemref, flagno).
00797     Flagno 1 is Slept
00798     Flagno 8 is Poisoned 
00799     Flag 25 causes people to say "Oink!"
00800     Returns boolean
00801     Or maybe not only NPC flag? - see the very first function on sails & gangplanks...
00802     ??? Suggestion. Flagno 10 is "on barge" flag which is set when the NPC is unmovable
00803     and mouse controls barge itself (sitting on ship/carpet/cart). A reasonable one.
00804     Needs checking.
00805     */
00806   "GetNPCFlag",           /* 88 */
00807   /* Function 0x89 - SetNPCFlag(itemref, flagno).
00808     Flagno 1 is Slept
00809     Flagno 8 is Poisoned
00810     Sets flag to true
00811     */
00812   "SetNPCFlag",           /* 89 */
00813   /* Function 0x8a - ResetNPCFlag(itemref, flagno).
00814     Flagno 1 is Slept
00815     Flagno 8 is Poisoned
00816     Sets flag to false
00817     */
00818   "ResetNPCFlag",           /* 8a */
00819   NULL,             /* 8b */
00820   NULL,             /* 8c */
00821   NULL,             /* 8d */
00822   NULL,             /* 8e */
00823   NULL,             /* 8f */
00824   NULL,             /* 90 */
00825   NULL,             /* 91 */
00826   NULL,             /* 92 */
00827   /* Itemref as parameter, returns an array of NPCs which are then passed to
00828     ResurrectNPC()
00829     called only once */
00830   NULL,             /* 93 */
00831   /* Called only for orrery viewer
00832     Large orrery coords as parameters,
00833     called only once and followed by ShowCrystalBall() at the orrery location
00834   */
00835   "SetupOrrery",            /* 94 */
00836   /* Some spells like An Flam */
00837   NULL,             /* 95 */
00838   /* Appears only in crazy talk after answering copy-protection incorrectly */
00839   NULL              /* 96 */
00840 };
00841 
00842 /*
00843  Embedded function table pointer
00844  TODO: set to bg_func_table or si_func_table depending on the command line
00845 */
00846 const char** func_table = bg_func_table;
00847 int func_table_size = sizeof(bg_func_table);
00848 
00849 /* Functions */
00850 
00851 /* Prints module's data segment */
00852 void process_data_seg(FILE* f, unsigned short ds)
00853 {
00854   long pos;
00855   unsigned short off = 0;
00856   unsigned char* p;
00857   unsigned char* pp;
00858   unsigned char* tempstr;
00859   /* Allocate a temporary buffer */
00860   tempstr = malloc(70 + 1);
00861   pos = ftell(f);
00862   pp = p = malloc(ds);
00863   fread(p, 1, ds, f);
00864   fseek(f, pos, SEEK_SET);
00865   /* Print all strings & their offsets */
00866   while( off < ds )
00867   {
00868     int len;
00869     unsigned short localoff = 0;
00870     /* Print all parts of the string - wrapping them around */
00871     while( (len = ( strlen(pp) > 70 )) ? 70 : strlen(pp) )
00872     {
00873       /* TODO! Escape characters if ' is part of the string */
00874       memcpy(tempstr, pp, len);
00875       tempstr[len] = '\0';
00876       if( localoff )
00877         printf("\tdb\t\'%s\'\n", tempstr);
00878       else
00879         printf("%04X\tdb\t\'%s\'\n", off, tempstr);
00880       localoff += len; 
00881       pp += len;
00882     }
00883     pp++;
00884     off += localoff + 1;
00885     printf("\tdb\t00\n");
00886   }
00887   free(p);
00888   free(tempstr);
00889 }
00890 
00891 /*
00892  Prints single opcode
00893  Return number of bytes to advance the code pointer
00894  Prints first characters of strings referenced
00895 */
00896 unsigned short print_opcode(unsigned char* ptrc, unsigned short coffset,
00897               unsigned char* pdataseg,
00898               unsigned short* pextern,
00899               unsigned short externsize,
00900               unsigned char* opcode_buf,
00901               unsigned char* intrinsic_buf,
00902               int mute, int count_all_opcodes,
00903               int count_all_intrinsic)
00904 {
00905   unsigned short nbytes;
00906   unsigned short i;
00907   opcode_desc* pdesc;
00908   if( count_all_opcodes )
00909     opcode_buf[*ptrc]++;
00910   /* Find the description */
00911   pdesc = ( *ptrc >= ( sizeof(opcode_table) / sizeof( opcode_desc ) ) ) ?
00912                         NULL : opcode_table + ( *ptrc );
00913   if( pdesc && ( pdesc->mnemonic == NULL ) )
00914     /* Unknown opcode */
00915     pdesc = NULL;
00916   if( ( pdesc == NULL ) && !count_all_opcodes )
00917     /* Unknown opcode */
00918     opcode_buf[*ptrc]++;
00919   /* Number of bytes to print */
00920   nbytes = pdesc ? ( pdesc->nbytes + 1 ) : 1;
00921   /* Print label */
00922   if( !mute )
00923     printf("%04X: ", coffset);
00924   /* Print bytes */
00925   for( i = 0; i < nbytes; i++ )
00926     if( !mute )   
00927       printf("%02X ", ptrc[i]);
00928   if( !mute )
00929   {
00930     /* Print mnemonic */
00931     if( nbytes < 4 )
00932       printf("\t");
00933     if( nbytes > 6 )
00934       printf("\n\t\t");
00935     printf("\t%s", pdesc ? pdesc->mnemonic : "???");
00936   }
00937   /* Print operands if any */
00938   if( ( nbytes == 1 ) || ( pdesc == NULL ) )
00939   {
00940     if( !mute )
00941       printf("\n");
00942     return nbytes;
00943   }
00944   switch( pdesc->type )
00945   {
00946   case IMMED:
00947     /* Print immediate operand */
00948     if( !mute )
00949       printf("\t%04XH\t\t\t; %d\n", *(unsigned short*)( ptrc + 1 ),
00950                 *(short*)( ptrc + 1 ));
00951     break;
00952   case IMMED_BYTE:
00953     /* Print immediate operand */
00954     if( !mute )
00955       printf("\t%02XH\t\t\t; %d\n", (unsigned short)(ptrc[1]),
00956                 (unsigned short)(ptrc[1]));
00957     break;
00958   case ( VARREF | RELATIVE_JUMP ):
00959     /* NEXT command */
00960     if( !mute )
00961     {
00962       /* Print variable reference */
00963       printf("\t[%04X], ", *(unsigned short*)( ptrc + 1 ));
00964       /* Print variable reference */
00965       printf("[%04X], ", *(unsigned short*)( ptrc + 3 ));
00966       /* Print variable reference */
00967       printf("[%04X], ", *(unsigned short*)( ptrc + 5 ));
00968       /* Print variable reference */
00969       printf("[%04X], ", *(unsigned short*)( ptrc + 7 ));
00970       /* Print jump desination */
00971       printf("%04X\n", *(short*)( ptrc + 9 ) + (short)coffset + nbytes);
00972     }
00973     break;
00974   case ( IMMED | RELATIVE_JUMP ):
00975     /* JMPA command */
00976     if( !mute )
00977     {
00978       /* Print immediate operand */
00979       printf("\t%04XH,\t", *(unsigned short*)( ptrc + 1 ));
00980       /* Print jump desination */
00981       printf("%04X\t", *(short*)( ptrc + 3 ) + (short)coffset + nbytes);
00982       /* Print immediate operand */
00983       printf("; %d\n", *(short*)( ptrc + 1 ));
00984     }
00985     break;
00986   case DATA_STRING:
00987     if( !mute )
00988     {
00989       unsigned char* pstr;
00990       int len;
00991       /* Print data string operand - first characters only */
00992       pstr = pdataseg + *(unsigned short*)( ptrc + 1 );
00993       len = strlen(pstr);
00994       if( len > 20 )
00995         len = 20 - 3;
00996       printf("\t%04XH\t\t\t; ", *(unsigned short*)( ptrc + 1 ));
00997       for( i = 0; i < len; i++ )
00998         printf("%c", pstr[i]);
00999       if( len < strlen(pstr) )
01000         /* String truncated */
01001         printf("...");
01002       printf("\n");
01003     }
01004     break;
01005   case RELATIVE_JUMP:
01006     /* Print jump desination */
01007     if( !mute )
01008       printf("\t%04X\n", *(short*)( ptrc + 1 ) + (short)coffset + 3);
01009     break;
01010   case CALL:
01011     {
01012       /* Print call operand */
01013       unsigned short func = *(unsigned short*)( ptrc + 1 );
01014       if( ( func < ( func_table_size / sizeof(const char *) ) ) &&
01015          func_table[func] )
01016       {
01017         /* Known function */
01018         if( !mute )
01019           printf("\t_%s@%d\t%s; %04X\n", func_table[func], ptrc[3],
01020             ( strlen(func_table[func]) > 12 ) ? "" : "\t",
01021             func);
01022         if( count_all_intrinsic )
01023           intrinsic_buf[func]++;
01024       }
01025       else
01026       {
01027         /* Unknown function */
01028         if( func >= 256 )
01029           /* TODO: error handling here */
01030           printf("Unknown intrisinc function greater then 256!\n");
01031         intrinsic_buf[func]++;
01032         if( !mute )
01033           printf("\t%04X, %d\n", func, ptrc[3]);
01034       }
01035     }
01036     break;
01037   case EXTCALL:
01038   case ( VARREF | EXTCALL ):
01039     {
01040       unsigned short externpos = *(unsigned short*)( ptrc + 1 );
01041       /* Print extern call */
01042       if( pdesc->type & VARREF )
01043       {
01044         unsigned short externpos = *(unsigned short*)( ptrc + 1 );
01045         if( externpos < externsize )
01046         {
01047           if( !mute )
01048             printf("\textern:[%04X]\t\t; %04XH\n",
01049               externpos, pextern[externpos]);
01050         }
01051         else
01052           printf("\tBad extern table!\n");
01053       }
01054       else if( !mute )
01055         printf("\textern:%04X\t\t\n", externpos);
01056     }
01057     break;
01058   case VARREF:
01059     /* Print variable reference */
01060     if( !mute )
01061       printf("\t[%04X]\n", *(unsigned short*)( ptrc + 1 ));
01062     break;
01063   case FLGREF:
01064     /* Print game flag reference */
01065     if( !mute )
01066       printf("\tflag:[%04X]\n", *(unsigned short*)( ptrc + 1 ));
01067     break;
01068   default:
01069     /* Unknown type */
01070     if( !mute )
01071       printf("\n");
01072     break;
01073   }
01074   return nbytes;
01075 }
01076 
01077 void process_code_seg(FILE* f, unsigned short ds, unsigned short s, unsigned char* opcode_buf,
01078               unsigned char* intrinsic_buf,
01079               int mute, int count_all_opcodes,
01080                 int count_all_intrinsic)
01081 {
01082   long pos;
01083   unsigned short size;
01084   unsigned short externsize;
01085   unsigned short i;
01086   unsigned short offset;
01087   unsigned short nbytes;
01088   unsigned char* p;
01089   unsigned char* pp;
01090   unsigned char* pdata;
01091   unsigned short* pextern;
01092   pos = ftell(f);
01093   size = s - ds - sizeof(unsigned short);
01094   pp = p = malloc(size);
01095   pdata = malloc(ds);
01096   fread(pdata, 1, ds, f);
01097   if( !mute )
01098     printf("Code segment at file offset %08lXH\n", ftell(f));
01099   fread(p, 1, size, f);
01100   fseek(f, pos, SEEK_SET);
01101   /* Print code segment header */
01102   if( size < 3 * sizeof(unsigned short) )
01103   {
01104     printf("Code segment bad!\n");
01105     free(p);
01106     free(pdata);
01107     return;
01108   }
01109   /* Print argument counter */
01110   if( !mute )
01111     printf("\t\t.argc %04XH\n", *(unsigned short*)pp);
01112   pp += sizeof(unsigned short);
01113   /* Print locals counter */ 
01114   if( !mute )
01115     printf("\t\t.localc %04XH\n", *(unsigned short*)pp);
01116   pp += sizeof(unsigned short);
01117   /* Print externs section */
01118   externsize = *(unsigned short*)pp;
01119   if( !mute )
01120     printf("\t\t.externsize %04XH\n", externsize);
01121   pp += sizeof(unsigned short);
01122   if( size < ( ( 3 + externsize ) * sizeof(unsigned short) ) )
01123   {
01124     printf("Code segment bad!\n");
01125     free(p);
01126     free(pdata);
01127     return;
01128   }
01129   size -= ( ( 3 + externsize ) * sizeof(unsigned short) );
01130   pextern = (unsigned short*)pp;
01131   for( i = 0; i < externsize; i++ )
01132   {
01133     if( !mute )
01134       printf("\t\t.extern %04XH\n", *(unsigned short*)pp);
01135     pp += sizeof(unsigned short);
01136   }
01137   offset = 0;
01138   /* Print opcodes */
01139   while( offset < size )
01140   {
01141     nbytes = print_opcode(pp, offset, pdata, pextern, externsize,
01142               opcode_buf, intrinsic_buf, mute,
01143               count_all_opcodes,
01144               count_all_intrinsic);
01145     pp += nbytes;
01146     offset += nbytes;
01147   }
01148   free(p);
01149   free(pdata);
01150 }
01151 
01152 void process_func(FILE* f, long func, int i, int* found, unsigned char* opcode_buf,
01153               unsigned char* intrinsic_buf,
01154               int scan_mode,
01155               unsigned long opcode,
01156               unsigned long intrinsic)
01157 {
01158   unsigned short s, ds, funcnum;  
01159   long off, bodyoff;
01160   /* Save start offset */
01161   off = ftell(f);
01162   /* Read function header */
01163   fread(&funcnum, sizeof(unsigned short), 1, f);
01164   fread(&s, sizeof(unsigned short), 1, f);
01165   /* Save body offset */
01166   bodyoff = ftell(f);
01167   fread(&ds, sizeof(unsigned short), 1, f);
01168   if( ( ( func == -1 ) || scan_mode ) && ( opcode == -1 ) && ( intrinsic == -1 ) )
01169     /* Only for general list & scan mode */
01170     printf("\tFunction #%d (%04XH), offset = %08lx, size = %04x, data = %04x\n", i,
01171                 funcnum, off, s, ds);
01172   if( ( funcnum == func ) || scan_mode || ( opcode != -1 ) || ( intrinsic != -1 ) )
01173   {
01174     /* Only for matching function or in one of the scan modes */
01175     if( funcnum == func )
01176     {
01177       *found = 1;
01178       printf("Function at file offset %08lX\n\t.funcnumber\t%04XH\n"
01179         "\t.msize\t%04XH\n\t.dsize\t%04XH\n",
01180         off, funcnum, s, ds);
01181     }
01182     /* Dump function contents */
01183     if( !scan_mode && ( opcode == -1 ) && ( intrinsic == -1 ) )
01184       process_data_seg(f, ds);
01185     if( opcode != -1 ) 
01186       memset(opcode_buf, 0, 256);
01187     if( intrinsic != -1 ) 
01188       memset(intrinsic_buf, 0, 256);
01189     process_code_seg(f, ds, s, opcode_buf, intrinsic_buf,
01190         scan_mode || ( opcode != -1 ) || ( intrinsic != -1 ),
01191         ( opcode != -1 ), ( intrinsic != -1 ));
01192     if( ( ( opcode != -1 ) && opcode_buf[opcode] > 0 ) ||
01193       ( ( intrinsic != -1 ) && intrinsic_buf[intrinsic] > 0 ) )
01194     {
01195       /* Found */
01196       *found = 1;
01197       if( intrinsic != -1 )
01198         printf("\tFound function (%04XH) - %d times\n", funcnum,
01199                 intrinsic_buf[intrinsic]);
01200       else
01201         printf("\tFound function (%04XH) - %d times\n", funcnum,
01202                   opcode_buf[opcode]);
01203     }
01204   }
01205   /* Seek back, then to next function */
01206   fseek(f, bodyoff, SEEK_SET);
01207   fseek(f, s, SEEK_CUR);
01208 }
01209 
01210 int main(int ac, char** av)
01211 {
01212   /* Preset to no match */
01213   unsigned long func = -1;
01214   const char* funcstr = NULL;
01215   unsigned char* opcode_buf = NULL;
01216   unsigned char* intrinsic_buf = NULL;
01217   long sz;
01218   int i = 0;
01219   int found = 0;
01220   int mode = 0;
01221   unsigned long opcode = -1;
01222   unsigned long intrinsic = -1;
01223   FILE* f;
01224   printf("Ultima 7 usecode disassembler v0.7\n\n");
01225   /* Parse command line */
01226   if( ac == 3 )
01227   {
01228     if( !strcmp(av[1], "-o") )
01229     {
01230       char* stopstr;
01231       /* Opcode search */
01232       opcode = strtoul(av[2], &stopstr, 16);
01233       if( stopstr - av[2] < strlen(av[2]) )
01234         opcode = -1;
01235       else
01236         /* Hex opcode OK */
01237         mode = 4;
01238     }
01239     else if( !strcmp(av[1], "-i") )
01240     {
01241       char* stopstr;
01242       /* Intrinsic function search */
01243       intrinsic = strtoul(av[2], &stopstr, 16);
01244       if( stopstr - av[2] < strlen(av[2]) )
01245         intrinsic = -1;
01246       else
01247         /* Hex opcode OK */
01248         mode = 5;
01249     }
01250   }
01251   else if( ac == 2 )
01252   {
01253     if( !strcmp(av[1], "-l") )
01254       /* List mode */
01255       mode = 2;
01256     else if( !strcmp(av[1], "-c") )
01257       /* Opcode scan mode */
01258       mode = 3;
01259     else
01260     {
01261       char* stopstr;
01262       /* Disassembly mode */
01263       funcstr = av[1];
01264       func = strtoul(funcstr, &stopstr, 16);
01265       if( stopstr - funcstr < strlen(funcstr) )
01266         /* Invalid number */
01267         func = -1;
01268       else
01269         mode = 1;
01270     }
01271   }
01272   if( mode == 0 )
01273   {
01274     printf("Usage:\n");
01275     printf("\tucdump -l - prints list of all present functions\n");
01276     printf("\tucdump -c - scans the whole usecode file for unknown opcodes\n");
01277     printf("\tucdump -o <hex number> - prints list of functions which use ");
01278     printf("the given opcode\n");
01279     printf("\tucdump -i <hex number> - prints list of functions which use ");
01280     printf("the given intrinsic function\n");
01281     printf("\tucdump <hex number> - disassembles single function to stdout\n");
01282     return -1;
01283   }
01284   /* Allocate opcode & intrinsic function buffers */
01285   if( mode != 2 )
01286   {
01287     opcode_buf = (unsigned char*)malloc(256);
01288     intrinsic_buf = (unsigned char*)malloc(256);
01289     if( ( opcode_buf == NULL ) || ( intrinsic_buf == NULL ) )
01290     {
01291       /* No memory */
01292       if( opcode_buf )
01293         free(opcode_buf);
01294       if( intrinsic_buf )
01295         free(intrinsic_buf);
01296       printf("Out of memory\n");
01297       return -2;
01298     }
01299     /* Zero them */
01300     memset(opcode_buf, 0, 256);
01301     memset(intrinsic_buf, 0, 256);
01302   }
01303   /* Open a usecode file */
01304 #ifdef _WIN32
01305   /* Microsoftism */
01306   f = fopen("usecode", "rb");
01307 #else
01308   f = fopen("usecode", "r");
01309 #endif
01310   if( f == NULL )
01311   {
01312     /* Free the buffers */
01313     if( opcode_buf )
01314       free(opcode_buf);
01315     if( intrinsic_buf )
01316       free(intrinsic_buf);
01317     printf("Failed to open usecode file\n\n");
01318     return -2;
01319   }
01320   fseek(f, 0, SEEK_END);
01321   sz = ftell(f);
01322   fseek(f, 0, SEEK_SET);
01323   if( mode == 1 )
01324     printf("Looking for function number %08lx\n\n", func);
01325   while( ftell(f) < sz )
01326   {   
01327     process_func(f, func, i, &found, opcode_buf, intrinsic_buf, ( mode == 3 ),
01328                   opcode, intrinsic);
01329     if( ( ( mode != 4 ) && ( mode !=5 ) ) || found )
01330       i++;
01331     if( ( mode == 4 ) || ( mode == 5 ) )
01332       found = 0;
01333   }
01334   if( func == -1 )
01335   {
01336     if( ftell(f) != sz )
01337       printf("Problem, tell = %ld!\n", ftell(f));
01338     printf("Functions: %d\n", i);
01339   }
01340   if( ( ( mode == 1 ) || ( mode == 4 ) ) && !found )
01341     printf("Function not found.\n");
01342   fclose(f);
01343   /* Dump unknowns */
01344   if( ( mode == 1 ) || ( mode == 3 ) )
01345   {
01346     if( opcode_buf )
01347     {
01348       int found = 0;
01349       for( i = 0; i < 255; i++ )
01350         if( opcode_buf[i] )
01351         {
01352           if( !found )
01353           {
01354             printf("Undefined opcodes found\n");
01355             found = 1;
01356           }
01357           printf("0x%02lx (%d times)\n", i, opcode_buf[i]);
01358         }
01359     }
01360     if( intrinsic_buf )
01361     {
01362       int found = 0;
01363       for( i = 0; i < 255; i++ )
01364         if( intrinsic_buf[i] )
01365         {
01366           if( !found )
01367           {
01368             printf("Undefined intrinsic functions found\n");
01369             found = 1;
01370           }
01371           printf("0x%02lx (%d times)\n", i, intrinsic_buf[i]);
01372         }
01373     }
01374   }
01375   /* Free the buffers */
01376   if( opcode_buf )
01377     free(opcode_buf);
01378   if( intrinsic_buf )
01379     free(intrinsic_buf);
01380   return 0;
01381 }

Generated on Mon Jul 9 14:42:51 2007 for ExultEngine by  doxygen 1.5.1