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 }