That last ticket is scary. It deletes all but 20 of QFG4's saved games whenever it autosaves (disabling autosave prevents it, btw).
I guess I'll diagnose that here with the CD edition, and folks can chime in or watch me learn...
I filled my saved game dir with more than 20 files, loaded up a saved game, enabled autosave. I knew if I walked to another screen, my files would be deleted.
I summoned the debugger and ran the
functions command (list kernel functions). Bunch of stuff came up. "FileIO" looked relevant.
Code: Select all
) bp_kernel FileIO
No kernel functions match FileIO.
Running
bp_kernel without an arg gave its help, which suggested using an asterisk wildcard.
Code: Select all
) bp_kernel FileIO* break
) bp_kernel GetSaveFiles break
) bp_kernel MakeSaveFileName break
I set some breakpoints and triggered an autosave. The game paused and dropped down the debugger whenever it called those kernel functions. I dismissed the debugger by hitting escape until I found something interesting. Along the way, it was telling me the function names it found. To cut down on noise, I deleted the wildcard breakpoint, and added specific functions.
Code: Select all
) bp_del 0
) bp_kernel FileIOOpen break
) bp_kernel FileIOClose break
) bp_kernel FileIOUnlink break
.
.
At a breakpoint, the
backtrace command prints breadcrumbs of where you are and what sent you here: script number, class, method.
.
.
Here's a suspicious bit, decompiled with SCI Companion (Script 0, Glory::save()).
Code: Select all
(while
(and
(> temp9 0)
(or
(not (CheckFreeSpace (global29 data?)))
(>= temp9 20)
)
)
(localproc_21c5 (- temp9 1) newStr temp9 temp1)
(= temp9
(GetSaveFiles
(global1 name?)
(newStr data?)
(temp1 data?)
)
)
)
localproc_21c5() is deleting individual files (by calling FileIOUnlink). And it's in a loop with a >= 20 condition.
.
.
Next, I disassembled that method ("scummvm.log" captured the result) and looked for landmarks.
This is an excerpt from the (or ...) block above.
Code: Select all
0001:1fb1: 43 3f 02 00 callk CheckFreeSpace[3f], 0002
0001:1fb5: 18 not
0001:1fb6: 2f 05 bt 05 [1fbd]
0001:1fb8: 8d 09 lst 09
0001:1fba: 35 14 ldi 14 ; 20
0001:1fbc: 20 ge?
0001:1fbd: 30 38 00 bnt 0038 [1ff8]
Location: opcode_value arg [arg...] // opcode_name args_description
Comments in
vm.cpp help explain what each opcode does.
- Call CheckFreeSpace().
- If it's true that there's inadequate space for a new save, branch to skip the rest of the OR condition and get to the loop content (deleting a file to free more space). Otherwise, keep going.
- Push a variable (temp9) onto the stack.
- Load a constant value into the accumulator register (0x14 hex, 20d decimal).
- Pop temp9 off the stack and compare to accumulator: is it greater-or-equal?
- If not true - and we got this far because the first condition wasn't true either - branch to the end of the loop. Done.
.
.
That's weird... My opcodes (e.g., bt = 0x2f, ldi = 0x35) didn't match the enums in "
vm.h" !?
Code: Select all
op_bnot = 0x00, // 000
op_add = 0x01, // 001
op_sub = 0x02, // 002
op_mul = 0x03, // 003
op_div = 0x04, // 004
op_mod = 0x05, // 005
op_shr = 0x06, // 006
op_shl = 0x07, // 007
op_xor = 0x08, // 008
op_and = 0x09, // 009
op_or = 0x0a, // 010
op_neg = 0x0b, // 011
op_not = 0x0c, // 012
op_eq_ = 0x0d, // 013
op_ne_ = 0x0e, // 014
op_gt_ = 0x0f, // 015
op_ge_ = 0x10, // 016
op_lt_ = 0x11, // 017
op_le_ = 0x12, // 018
op_ugt_ = 0x13, // 019
op_uge_ = 0x14, // 020
op_ult_ = 0x15, // 021
op_ule_ = 0x16, // 022
op_bt = 0x17, // 023
op_bnt = 0x18, // 024
op_jmp = 0x19, // 025
op_ldi = 0x1a, // 026
op_push = 0x1b, // 027
op_pushi = 0x1c, // 028
op_toss = 0x1d, // 029
op_dup = 0x1e, // 030
op_link = 0x1f, // 031
op_call = 0x20, // 032
op_callk = 0x21, // 033
op_callb = 0x22, // 034
op_calle = 0x23, // 035
op_ret = 0x24, // 036
op_send = 0x25, // 037
op_info = 0x26, // 038
op_superP = 0x27, // 039
op_class = 0x28, // 040
// dummy 0x29, // 041
op_self = 0x2a, // 042
op_super = 0x2b, // 043
op_rest = 0x2c, // 044
op_lea = 0x2d, // 045
op_selfID = 0x2e, // 046
// dummy 0x2f // 047
op_pprev = 0x30, // 048
op_pToa = 0x31, // 049
op_aTop = 0x32, // 050
op_pTos = 0x33, // 051
op_sTop = 0x34, // 052
op_ipToa = 0x35, // 053
op_dpToa = 0x36, // 054
op_ipTos = 0x37, // 055
op_dpTos = 0x38, // 056
op_lofsa = 0x39, // 057
op_lofss = 0x3a, // 058
op_push0 = 0x3b, // 059
op_push1 = 0x3c, // 060
op_push2 = 0x3d, // 061
op_pushSelf = 0x3e, // 062
op_line = 0x3f, // 063
//
op_lag = 0x40, // 064
op_lal = 0x41, // 065
op_lat = 0x42, // 066
op_lap = 0x43, // 067
op_lsg = 0x44, // 068
op_lsl = 0x45, // 069
op_lst = 0x46, // 070
op_lsp = 0x47, // 071
op_lagi = 0x48, // 072
op_lali = 0x49, // 073
op_lati = 0x4a, // 074
op_lapi = 0x4b, // 075
op_lsgi = 0x4c, // 076
op_lsli = 0x4d, // 077
op_lsti = 0x4e, // 078
op_lspi = 0x4f, // 079
//
op_sag = 0x50, // 080
op_sal = 0x51, // 081
op_sat = 0x52, // 082
op_sap = 0x53, // 083
op_ssg = 0x54, // 084
op_ssl = 0x55, // 085
op_sst = 0x56, // 086
op_ssp = 0x57, // 087
op_sagi = 0x58, // 088
op_sali = 0x59, // 089
op_sati = 0x5a, // 090
op_sapi = 0x5b, // 091
op_ssgi = 0x5c, // 092
op_ssli = 0x5d, // 093
op_ssti = 0x5e, // 094
op_sspi = 0x5f, // 095
//
op_plusag = 0x60, // 096
op_plusal = 0x61, // 097
op_plusat = 0x62, // 098
op_plusap = 0x63, // 099
op_plussg = 0x64, // 100
op_plussl = 0x65, // 101
op_plusst = 0x66, // 102
op_plussp = 0x67, // 103
op_plusagi = 0x68, // 104
op_plusali = 0x69, // 105
op_plusati = 0x6a, // 106
op_plusapi = 0x6b, // 107
op_plussgi = 0x6c, // 108
op_plussli = 0x6d, // 109
op_plussti = 0x6e, // 110
op_plusspi = 0x6f, // 111
//
op_minusag = 0x70, // 112
op_minusal = 0x71, // 113
op_minusat = 0x72, // 114
op_minusap = 0x73, // 115
op_minussg = 0x74, // 116
op_minussl = 0x75, // 117
op_minusst = 0x76, // 118
op_minussp = 0x77, // 119
op_minusagi = 0x78, // 120
op_minusali = 0x79, // 121
op_minusati = 0x7a, // 122
op_minusapi = 0x7b, // 123
op_minussgi = 0x7c, // 124
op_minussli = 0x7d, // 125
op_minussti = 0x7e, // 126
op_minusspi = 0x7f // 127
.
.
My opcodes did, however, match what everyone else was writing in "
script_patches.cpp". I scraped a list from comments there, incomplete but known to work.
Code: Select all
0x00 neg; One example, typo?
0x02 add
0x04 sub
0x06 mul
0x08 div
0x0c shr
0x12 and
0x14 or
0x18 not
0x1a eq?
0x1c ne?
0x1e gt?
0x20 ge?
0x22 lt?
0x24 le?
0x28 uge?
0x2c ult?
0x2e bt (UINT16)
0x2f bt
0x30 bnt (UINT16)
0x31 bnt
0x32 jmp (UINT16)
0x33 jmp
0x34 ldi (UINT16)
0x35 ldi
0x36 push
0x38 pushi (UINT16)
0x39 pushi
0x3a toss
0x3c dup
0x3e link (UINT16)
0x3f link
0x40 call (??? 2 bytes)
0x41 call (??? 3 bytes)
0x43 callk
0x45 callb
0x47 calle
0x48 ret
0x4a send (UINT16)
0x4b send
0x51 class
0x54 self
0x57 super
0x59 rest
0x5a lea (UINT16) (UINT16)
0x5b lea
0x62 pToa (UINT16)
0x63 pToa
0x64 aTop (UINT16)
0x65 aTop
0x66 pTos (UINT16)
0x67 pTos
0x72 lofsa
0x74 lofss
0x76 push0
0x78 push1
0x7a push2
0x7c pushSelf
0x7e line
0x80 lag (UINT16); One comment said lsg, typo?
0x81 lag; One comment said lsg, typo?
0x82 lal (UINT16)
0x83 lal
0x85 lat
0x87 lap
0x89 lsg
0x8b lsl
0x8d lst
0x8f lsp
0x90 lagi
0x93 lali
0x99 lsgi
0x9a lsli (UINT16)
0x9b lsli
0xa0 sag (UINT16)
0xa1 sag
0xa3 sal
0xa5 sat
0xa8 ssg (UINT16)
0xa9 ssg
0xab ssl
0xb0 sagi
0xb3 sali
0xba ssli
0xc1 +ag
0xe1 -ag
Okay, the
real opcode values are double the enums, sometimes plus 1. "vm.h" had condensed the list of repeated names: wide/narrow arg variants of each op.
.
.
To be continued...