-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathTweaks.pas
228 lines (183 loc) · 8.18 KB
/
Tweaks.pas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
unit Tweaks;
{
DESCRIPTION: Fixed and improvements
AUTHOR: Alexander Shostak (aka Berserker aka EtherniDee aka BerSoft)
}
(***) interface (***)
uses
Windows, SysUtils, Utils, Debug, FilesEx, Concur, DataLib, ApiJack, PatchApi,
MapExt, Editor;
(***) implementation (***)
var
{O} TopLevelExceptionHandlers: DataLib.TList {OF Handler: pointer};
ExceptionsCritSection: Concur.TCritSection;
procedure DumpWinPeModuleList;
const
DEBUG_WINPE_MODULE_LIST_PATH = MapExt.DEBUG_DIR + '\pe modules.txt';
var
i: integer;
begin
{!} Debug.ModuleContext.Lock;
Debug.ModuleContext.UpdateModuleList;
with FilesEx.WriteFormattedOutput(MapExt.GameDir + '\' + DEBUG_WINPE_MODULE_LIST_PATH) do begin
Line('> Win32 executable modules');
EmptyLine;
for i := 0 to Debug.ModuleContext.ModuleList.Count - 1 do begin
Line(Debug.ModuleContext.ModuleInfo[i].ToStr);
end; // .for
end; // .with
{!} Debug.ModuleContext.Unlock;
end; // .procedure DumpWinPeModuleList
procedure DumpExceptionContext (ExcRec: Windows.PExceptionRecord; Context: Windows.PContext);
const
DEBUG_EXCEPTION_CONTEXT_PATH = MapExt.DEBUG_DIR + '\exception context.txt';
var
ExceptionText: string;
LineText: string;
Ebp: integer;
Esp: integer;
RetAddr: integer;
i: integer;
begin
{!} Debug.ModuleContext.Lock;
Debug.ModuleContext.UpdateModuleList;
with FilesEx.WriteFormattedOutput(MapExt.GameDir + '\' + DEBUG_EXCEPTION_CONTEXT_PATH) do begin
case ExcRec.ExceptionCode of
$C0000005: begin
if ExcRec.ExceptionInformation[0] <> 0 then begin
ExceptionText := 'Failed to write data at ' + Format('%x', [integer(ExcRec.ExceptionInformation[1])]);
end else begin
ExceptionText := 'Failed to read data at ' + Format('%x', [integer(ExcRec.ExceptionInformation[1])]);
end; // .else
end; // .case $C0000005
$C000008C: ExceptionText := 'Array index is out of bounds';
$80000003: ExceptionText := 'Breakpoint encountered';
$80000002: ExceptionText := 'Data access misalignment';
$C000008D: ExceptionText := 'One of the operands in a floating-point operation is denormal';
$C000008E: ExceptionText := 'Attempt to divide a floating-point value by a floating-point divisor of zero';
$C000008F: ExceptionText := 'The result of a floating-point operation cannot be represented exactly as a decimal fraction';
$C0000090: ExceptionText := 'Invalid floating-point exception';
$C0000091: ExceptionText := 'The exponent of a floating-point operation is greater than the magnitude allowed by the corresponding type';
$C0000092: ExceptionText := 'The stack overflowed or underflowed as the result of a floating-point operation';
$C0000093: ExceptionText := 'The exponent of a floating-point operation is less than the magnitude allowed by the corresponding type';
$C000001D: ExceptionText := 'Attempt to execute an illegal instruction';
$C0000006: ExceptionText := 'Attempt to access a page that was not present, and the system was unable to load the page';
$C0000094: ExceptionText := 'Attempt to divide an integer value by an integer divisor of zero';
$C0000095: ExceptionText := 'Integer arithmetic overflow';
$C0000026: ExceptionText := 'An invalid exception disposition was returned by an exception handler';
$C0000025: ExceptionText := 'Attempt to continue from an exception that isn''t continuable';
$C0000096: ExceptionText := 'Attempt to execute a privilaged instruction.';
$80000004: ExceptionText := 'Single step exception';
$C00000FD: ExceptionText := 'Stack overflow';
else ExceptionText := 'Unknown exception';
end; // .switch ExcRec.ExceptionCode
Line(ExceptionText + '.');
Line(Format('EIP: %s. Code: %x', [Debug.ModuleContext.AddrToStr(Ptr(Context.Eip)), ExcRec.ExceptionCode]));
EmptyLine;
Line('> Registers');
Line('EAX: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Eax), Debug.ANALYZE_DATA));
Line('ECX: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Ecx), Debug.ANALYZE_DATA));
Line('EDC: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Edx), Debug.ANALYZE_DATA));
Line('EBX: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Ebx), Debug.ANALYZE_DATA));
Line('ESP: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Esp), Debug.ANALYZE_DATA));
Line('EBP: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Ebp), Debug.ANALYZE_DATA));
Line('ESI: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Esi), Debug.ANALYZE_DATA));
Line('EDI: ' + Debug.ModuleContext.AddrToStr(Ptr(Context.Edi), Debug.ANALYZE_DATA));
EmptyLine;
Line('> Callstack');
Ebp := Context.Ebp;
RetAddr := 1;
try
while (Ebp <> 0) and (RetAddr <> 0) do begin
RetAddr := pinteger(Ebp + 4)^;
if RetAddr <> 0 then begin
Line(Debug.ModuleContext.AddrToStr(Ptr(RetAddr)));
Ebp := pinteger(Ebp)^;
end; // .if
end; // .while
except
// Stop processing callstack
end; // .try
EmptyLine;
Line('> Stack');
Esp := Context.Esp - sizeof(integer) * 5;
try
for i := 1 to 40 do begin
LineText := IntToHex(Esp, 8);
if Esp = integer(Context.Esp) then begin
LineText := LineText + '*';
end; // .if
LineText := LineText + ': ' + Debug.ModuleContext.AddrToStr(ppointer(Esp)^, Debug.ANALYZE_DATA);
Inc(Esp, sizeof(integer));
Line(LineText);
end; // .for
except
// Stop stack traversing
end; // .try
end; // .with
{!} Debug.ModuleContext.Unlock;
end; // .procedure DumpExceptionContext
function TopLevelExceptionHandler (const ExceptionPtrs: TExceptionPointers): integer; stdcall;
const
EXCEPTION_CONTINUE_SEARCH = 0;
begin
SysUtils.SetCurrentDir(GameDir);
DumpExceptionContext(ExceptionPtrs.ExceptionRecord, ExceptionPtrs.ContextRecord);
MapExt.FireEvent('OnGenerateDebugInfo', nil, 0);
Windows.MessageBox(0, 'Editor is crashing, sorry. Look at "' + MapExt.DEBUG_DIR + '" for debug info.', 'Something went wrong :(', Windows.MB_OK or Windows.MB_ICONEXCLAMATION);
result := EXCEPTION_CONTINUE_SEARCH;
end; // .function TopLevelExceptionHandler
function OnUnhandledException (const ExceptionPtrs: TExceptionPointers): integer; stdcall;
type
THandler = function (const ExceptionPtrs: TExceptionPointers): integer; stdcall;
const
EXCEPTION_CONTINUE_SEARCH = 0;
var
i: integer;
begin
{!} ExceptionsCritSection.Enter;
for i := 0 to TopLevelExceptionHandlers.Count - 1 do begin
THandler(TopLevelExceptionHandlers[i])(ExceptionPtrs);
end;
{!} ExceptionsCritSection.Leave;
result := EXCEPTION_CONTINUE_SEARCH;
end; // .function OnUnhandledException
function Splice_SetUnhandledExceptionFilter (OrigFunc, NewHandler: pointer): pointer; stdcall;
begin
if NewHandler <> nil then begin
{!} ExceptionsCritSection.Enter;
TopLevelExceptionHandlers.Add(NewHandler);
{!} ExceptionsCritSection.Leave;
end;
result := nil;
end;
function Hook_FixGameVersion (Context: ApiJack.PHookContext): longbool; stdcall;
begin
Editor.GameVersion^ := AB_AND_SOD;
result := true;
end;
procedure OnGenerateDebugInfo (Event: PEvent); stdcall;
begin
DumpWinPeModuleList;
end;
procedure OnInit (Event: MapExt.PEvent); stdcall;
begin
(* Install global top-level exception filter *)
Windows.SetErrorMode(SEM_NOGPFAULTERRORBOX);
Windows.SetUnhandledExceptionFilter(@OnUnhandledException);
ApiJack.StdSplice(@Windows.SetUnhandledExceptionFilter, @Splice_SetUnhandledExceptionFilter, ApiJack.CONV_STDCALL, 1);
Windows.SetUnhandledExceptionFilter(@TopLevelExceptionHandler);
(* Fix game version to allow generating random maps *)
ApiJack.Hook(Ptr($45C5FD), @Hook_FixGameVersion, nil, 5, ApiJack.HOOKTYPE_BRIDGE);
(* Fix crashing on exit is some kind of destructor *)
PatchApi.p.WriteCodePatch(Ptr($4DC080), ['C3']);
(* Use .msk files instead of .msg *)
PatchApi.p.WriteCodePatch(Ptr($54097F), ['6B']);
PatchApi.p.WriteCodePatch(Ptr($58E1EE), ['6B']);
end;
begin
ExceptionsCritSection.Init;
TopLevelExceptionHandlers := DataLib.NewList(not Utils.OWNS_ITEMS);
MapExt.RegisterHandler(OnInit, 'OnInit');
RegisterHandler(OnGenerateDebugInfo, 'OnGenerateDebugInfo');
end.