-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathnwmovies.c
More file actions
723 lines (610 loc) · 25.4 KB
/
Copy pathnwmovies.c
File metadata and controls
723 lines (610 loc) · 25.4 KB
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
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
/*
* A hack to re-enable movies in the Linux NWN client.
*
* FWIW, this is copywritten by David Holland (7/2003)
*
* Copyright 2003-2004, David Holland, zzqzzq_zzq@hotmail.com
* Copyright 2006, David Holland, david.w.holland@gmail.com
* Copyright 2008, David Holland, david.w.holland@gmail.com
*
* There is no warrenty provided with this code. Use it at your
* own risk.
*
* There are two functions in the linux client that currently return
* '1' in $eax. This modifies them to return a '0'.
*
* CClientExoApp::GetDisableMovies(void):
* CClientExoApp::GetDisableIntroMovies(void):
*
* Changing the return value of those two functions, causes a third function
* to be called when a movie is wished to be played.
*
* CClientExoApp::PlayMovie(CExoString, int)
*
* A jump-point is installed inside the third function, when the jump-point
* is triggered, the movie that was attempted to play is determined, and
* passed as a argument to a subprocess that could theoretically play the
* movie.
*
* there are two other patches required to modify a code path, otherwise
* the client will get confused (hang) while trying to play a chapter
* intro movie. (hence, patches #4, and #5)
*
* And make two modifications inside of:
* CNWCMessage::HandleServerToPlayerUpdate_WorkRemaining(void):
*
* nop() out the call to: CClientExoApp::SetSendAreaLoadedMessageAfterMovie(void)
* First call after the PlayMovie() call.
* nop() out the 'jmp' that follows it.
*
* <shrug, that's what I had in my notes, its been to long to remember the details>
*
* note: the NWN client is suspended while the movie player is playing.
*
* Becareful, as not all movie players will work.
* mplayer player playing via Xv is known not to work. (And will crash
* the X server. ) mplayer via X11 appears to be fine.
*/
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <SDL/SDL.h>
#include <SDL/SDL_types.h>
#include <SDL/SDL_syswm.h>
#include <link.h>
#include <libgen.h>
#include <errno.h>
#include <sys/mman.h>
#include <limits.h>
#include <stdarg.h>
#include "nwmovies.h"
#ifndef PAGESIZE
#define PAGESIZE 4096
#endif
/* I'm sure there are approximately 4billion better ways to do this
However, this is the one that I came up with, and most importantly
works at least most of the time */
static int _NWMovies_Current_Grab_Mode = SDL_GRAB_OFF; // Presumably the grab is off at start.
static int _NWMovies_NeedsToggle = 0;
/* External functions linked in */
static int (*sdl_wm_grabinput_ptr)(int) = NULL;
static SDL_Surface *(*sdl_getvideosurface_ptr)(void) = NULL;
static int (*sdl_wm_togglefullscreen_ptr)(SDL_Surface *) = NULL;
static int (*sdl_pollevent_ptr)(SDL_Event *event) = NULL;
static int (*sdl_wm_iconifywindow_ptr)(void) = NULL;
extern unsigned long _NWMovies_ESI; /* playmovie routine, ESI contains movie name */
unsigned long _NWM_movie_retaddr; /* modified by setup_memory(), and stored in the linkage routine */
extern void NWMovies_link_store_ESI(void); /* label that points to the ESI store code */
extern void NWMovies_link_playmovie2(void); /* label that points to the playmovie2 call */
extern void NWMovies_link_exit_jmp(void); /* label that points to the exit jump */
void NWMovies_Ungrab(void);
void NWMovies_RestoreGrab(void);
void NWMovies_setup_memory(unsigned int patch0, /* CClientExoApp::GetDisableMovies(void): modify last instruction to be 'mov $0x0, $eax' (return 0) */
unsigned int patch1, /* CClientExoApp::GetDisableIntroMovies(void): modify last instruction to be 'mov $0x0, $eax' (return 0) */
unsigned int patch2,
unsigned int patch3,
unsigned int patch4,
unsigned int patch5, /* Enable movie button */
unsigned int patch6); /* Enable movie button */
void NWMovies_printdata(char *ptr, int len);
void NWMovies_memcpy(unsigned char *dest, unsigned char *src, size_t n);
extern void NWMovies_playmovie(void);
unsigned int *NWMovies_findcookie(char *file); /* Finds the patch addresses inside the executable. */
void NWMovies_runcommand(char *title);
void NWMovies_playmovie2(void);
void NWMovies_log(const int echo, const char *fmt, ...)
{
static FILE *fp = NULL;
va_list arg_list;
if (!fp)
fp = fopen(_NWMOVIES_LOGFILE, "w");
va_start(arg_list, fmt);
if (fp) {
vfprintf(fp, fmt, arg_list);
#ifndef DEBUG
if (echo)
#endif
vfprintf(stderr, fmt, arg_list);
} else
vfprintf(stderr, fmt, arg_list);
va_end(arg_list);
fflush( fp );
}
// Initialize constructor attribute so we get called during executable startup.
void NWMovies_Initialize(void) __attribute__((constructor));
void NWMovies_Initialize(void)
{
struct stat statbuf;
Dl_info info;
char *library_name;
FILE *fp;
char string1[80];
char string2[80];
unsigned int patch0_addr, patch1_addr, patch2_addr, patch3_addr, patch4_addr;
unsigned int patch5_addr, patch6_addr;
unsigned int file_size, file_date;
unsigned int *patch_address;
void *self_handle;
void *libSDL_handle; /* Either a pointer to libSDL, or RTLD_NEXT, depending on which one "works" */
void *self_ptr;
char *self_name_ptr;
self_handle = dlopen("", RTLD_NOW | RTLD_GLOBAL);
self_ptr = dlsym(self_handle, "_init");
if( self_ptr == NULL || dladdr( self_ptr, &info ) <= 0 ) {
NWMovies_log(1, "ERROR: NWMovies: dladdr(self: _init): %s\n", dlerror());
abort();
}
/* recycle library_name */
self_name_ptr = basename((char *)info.dli_fname);
if( strncmp( self_name_ptr, "nwmain", PATH_MAX) != 0 ) {
dlclose(self_handle);
return;
}
dlclose(self_handle);
/* Spit out a version number and reset log file */
NWMovies_log(1, "NOTICE: NWMovies: Version: %s (Binary = %s)\n", _NWMOVIES_VERSION, info.dli_fname);
NWMovies_log(0, "NOTICE: NWMovies: Looking up symbols in libSDL.....\n");
/* try to lookup libSDL functions via RTLD_NEXT, and if that doesn't work,
* open libSDL directly.
*/
libSDL_handle = RTLD_NEXT;
sdl_wm_grabinput_ptr = dlsym(libSDL_handle, "SDL_WM_GrabInput");
if( sdl_wm_grabinput_ptr == NULL ) {
/* via RTLD_NEXT failed, try loading libSDL directly. */
libSDL_handle = dlopen("libSDL-1.2.so.0", RTLD_NOW | RTLD_GLOBAL);
if ( libSDL_handle == NULL ) {
NWMovies_log(1, "ERROR: NWMovies: dladdr(libSDL-1.2.so.0: _init): %s\n", dlerror());
abort();
}
sdl_wm_grabinput_ptr = dlsym(libSDL_handle, "SDL_WM_GrabInput");
if( sdl_wm_grabinput_ptr == NULL ) {
NWMovies_log(1, "ERROR: sdl_wm_grabinput_ptr == NULL: %s\n", dlerror());
abort();
}
NWMovies_log(0, "NOTICE: NWMovies: Using libSDL via direct dlopen()\n");
} else {
NWMovies_log(0, "NOTICE: NWMovies: Using libSDL via RTLD_NEXT.\n");
}
sdl_getvideosurface_ptr = dlsym(libSDL_handle, "SDL_GetVideoSurface");
if( sdl_getvideosurface_ptr == NULL ) { NWMovies_log(1, "ERROR: sdl_getvideosurface_ptr == NULL: %s\n", dlerror()); abort(); }
sdl_wm_togglefullscreen_ptr = dlsym(libSDL_handle, "SDL_WM_ToggleFullScreen");
if( sdl_wm_togglefullscreen_ptr == NULL ) { NWMovies_log(1, "ERROR: sdl_wm_togglefullscreen_ptr == NULL: %s\n", dlerror()); abort(); }
sdl_pollevent_ptr = dlsym(libSDL_handle, "SDL_PollEvent" );
if( sdl_pollevent_ptr == NULL ) { NWMovies_log(1, "ERROR: sdl_pollevent_ptr == NULL: %s\n", dlerror()); abort(); }
sdl_pollevent_ptr = dlsym(libSDL_handle, "SDL_PollEvent" );
if( sdl_pollevent_ptr == NULL ) { NWMovies_log(1, "ERROR: sdl_pollevent_ptr == NULL: %s\n", dlerror()); abort(); }
sdl_wm_iconifywindow_ptr = dlsym(libSDL_handle, "SDL_WM_IconifyWindow" );
if( sdl_wm_iconifywindow_ptr == NULL ) { NWMovies_log(1, "ERROR: sdl_wm_iconifywindow_ptr == NULL: %s\n", dlerror()); abort(); }
if( dladdr( sdl_wm_grabinput_ptr, &info ) <= 0 ) {
NWMovies_log(1, "ERROR: NWMovies: dladdr: %s\n", dlerror());
abort();
}
library_name = (char *)info.dli_fname;
NWMovies_log(0, "NOTICE: NWMovies: SDL Library determined to be: %s\n", library_name);
NWMovies_log(0, "NOTICE: NWMovies: SDL_WM_GrabInput() address: %08x\n", (unsigned int)sdl_wm_grabinput_ptr);
NWMovies_log(0, "NOTICE: NWMovies: SDL_GetVideoSurface() address: %08x\n", (unsigned int)sdl_getvideosurface_ptr);
NWMovies_log(0, "NOTICE: NWMovies: SDL_WM_ToggleFullScreen() address: %08x\n", (unsigned int)sdl_wm_togglefullscreen_ptr);
NWMovies_log(0, "NOTICE: NWMovies: SDL_PollEvent() address: %08x\n", (unsigned int)sdl_pollevent_ptr);
NWMovies_log(0, "NOTICE: NWMovies: SDL_WM_IconifyWindow() address: %08x\n", (unsigned int)sdl_wm_iconifywindow_ptr);
if( stat("nwmain", &statbuf) != 0 ) {
NWMovies_log(1, "ERROR: NWMovies: Unable to stat nwmain: %d\n", errno);
exit(-1);
}
/* ini parsing. No, this doesn't have a lot of error checking. */
fp = fopen("nwmovies.ini", "r");
if( fp == NULL ) {
NWMovies_log(1, "WARNING: NWMovies: No INI file. Creating.\n");
fp = fopen("nwmovies.ini", "w");
if( fp == NULL ) {
NWMovies_log(1, "ERROR: NWMovies: Unable to create INI file. Aborting: %d\n", errno);
exit(-1);
}
fprintf(fp, "size 0\n");
fprintf(fp, "time 0\n");
fprintf(fp, "patch0 0\n");
fprintf(fp, "patch1 0\n");
fprintf(fp, "patch2 0\n");
fprintf(fp, "patch3 0\n");
fprintf(fp, "patch4 0\n");
fprintf(fp, "patch5 0\n");
fprintf(fp, "patch6 0\n");
fprintf(fp, "NeedsToggle 0\n");
fclose(fp);
fp = fopen("nwmovies.ini", "r");
if( fp == NULL ) {
NWMovies_log(1, "ERROR: NWMovies: Unable to re-open nwmovies.ini. Aborting: %d\n", errno);
exit(-1);
}
}
while( fscanf(fp, "%79s %79s\n", string1, string2) != EOF ) {
if( strcmp(string1, "size") == 0 ) {
file_size = atoi(string2);
}
if( strcmp(string1, "time") == 0 ) {
file_date = atoi(string2);
}
if( strcmp(string1, "patch0") == 0 ) {
patch0_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "patch1") == 0 ) {
patch1_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "patch2") == 0 ) {
patch2_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "patch3") == 0 ) {
patch3_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "patch4") == 0 ) {
patch4_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "patch5") == 0 ) {
patch5_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "patch6") == 0 ) {
patch6_addr = strtol(string2, NULL, 0);
}
if( strcmp(string1, "NeedsToggle") == 0 ) {
_NWMovies_NeedsToggle = atoi(string2);
}
}
fclose(fp);
if( statbuf.st_size != file_size || statbuf.st_mtime != file_date ) {
NWMovies_log(1, "WARNING: NWMovies: INI recalculation required: %d:%d %d:%d\n",
(unsigned int)statbuf.st_size, file_size, (unsigned int)statbuf.st_mtime, file_date);
patch_address = NWMovies_findcookie( "nwmain" );
fp = fopen("nwmovies.ini", "w");
if( fp == NULL ) {
NWMovies_log(1, "ERROR: NWMovies: Unable to create INI file. Aborting: %d\n", errno);
exit(-1);
}
fprintf(fp, "%s %d\n", "size", (unsigned int)statbuf.st_size);
fprintf(fp, "%s %d\n", "time", (unsigned int)statbuf.st_mtime);
fprintf(fp, "%s 0x%08x\n", "patch0", patch_address[0]);
fprintf(fp, "%s 0x%08x\n", "patch1", patch_address[1]);
fprintf(fp, "%s 0x%08x\n", "patch2", patch_address[2]);
fprintf(fp, "%s 0x%08x\n", "patch3", patch_address[3]);
fprintf(fp, "%s 0x%08x\n", "patch4", patch_address[4]);
fprintf(fp, "%s 0x%08x\n", "patch5", patch_address[5]);
fprintf(fp, "%s 0x%08x\n", "patch6", patch_address[6]);
fprintf(fp, "%s 0\n", "NeedsToggle" );
fclose(fp);
NWMovies_log(1, "NOTICE: NWMovies: INI File written: Now exiting. This is perfectly normal!\n");
NWMovies_log(1, "NOTICE: NWMovies: Your next run of NWN should be complete, and include movies.\n");
exit(0);
}
NWMovies_log(0, "NOTICE: NWMovies: Patch 0 Address: 0x%08x\n", patch0_addr);
NWMovies_log(0, "NOTICE: NWMovies: Patch 1 Address: 0x%08x\n", patch1_addr);
NWMovies_log(0, "NOTICE: NWMovies: Patch 2 Address: 0x%08x\n", patch2_addr);
NWMovies_log(0, "NOTICE: NWMovies: Patch 3 Address: 0x%08x\n", patch3_addr);
NWMovies_log(0, "NOTICE: NWMovies: Patch 4 Address: 0x%08x\n", patch4_addr);
NWMovies_log(0, "NOTICE: NWMovies: Patch 5 Address: 0x%08x\n", patch5_addr);
NWMovies_log(0, "NOTICE: NWMovies: Patch 6 Address: 0x%08x\n", patch6_addr);
NWMovies_setup_memory(patch0_addr, patch1_addr, patch2_addr, patch3_addr, patch4_addr, patch5_addr, patch6_addr);
NWMovies_log(0, "NOTICE: NWMovies: Initialized.\n");
return;
}
SDL_GrabMode SDL_WM_GrabInput(SDL_GrabMode mode) {
/* Get/Save the current grab mode. */
NWMovies_log(0, "NOTICE: NWMovies: SDL_WM_GrabInput(");
switch( mode) {
case SDL_GRAB_QUERY:
NWMovies_log(0, "QUERY) called..\n");
_NWMovies_Current_Grab_Mode = sdl_wm_grabinput_ptr( mode );
break;
case SDL_GRAB_OFF:
NWMovies_log(0, "OFF) called..\n");
_NWMovies_Current_Grab_Mode = SDL_GRAB_OFF;
break;
case SDL_GRAB_ON:
NWMovies_log(0, "ON) called..\n");
_NWMovies_Current_Grab_Mode = SDL_GRAB_ON;
break;
default:
NWMovies_log(0, "UNKNOWN) called..\n");
break;
}
return(sdl_wm_grabinput_ptr( mode ));
}
/*
* Wrap around SDL_PollEvent() so we can handle the various unlock keys
*
* Right Alt-Enter: Toggle Fullscreen
* Left Alt-Enter: Toggle Fullscreen w/ Iconfiy (ie: Maximize Window - Odd No?)
* Left Control-G: Turn off Keyboard/Mouse Grab
* Right Control-G: Turn ON Keyboard/Mouse Grab
*/
int SDL_PollEvent(SDL_Event *event) {
SDL_Event my_event;
int eat_event;
while( sdl_pollevent_ptr(&my_event) ) {
eat_event = 0;
/* Key down, is this one of the events we're interested in? */
if( my_event.type == SDL_KEYDOWN ) {
if( my_event.key.keysym.sym == SDLK_RETURN && ( my_event.key.keysym.mod & KMOD_RALT ) ) {
sdl_wm_togglefullscreen_ptr(sdl_getvideosurface_ptr());
eat_event = 1;
}
if( my_event.key.keysym.sym == SDLK_RETURN && ( my_event.key.keysym.mod & KMOD_LALT ) ) {
sdl_wm_togglefullscreen_ptr(sdl_getvideosurface_ptr());
sdl_wm_iconifywindow_ptr();
eat_event = 1;
}
if( my_event.key.keysym.sym == SDLK_g && ( my_event.key.keysym.mod & KMOD_LCTRL )) {
SDL_WM_GrabInput(SDL_GRAB_OFF);
eat_event = 1;
}
if( my_event.key.keysym.sym == SDLK_g && ( my_event.key.keysym.mod & KMOD_RCTRL )) {
SDL_WM_GrabInput(SDL_GRAB_ON);
eat_event = 1;
}
}
if( !eat_event ) {
/* Didn't eat event, send it on up. */
memcpy( event, &my_event, sizeof(SDL_Event));
return(1);
}
} /* real SDL_PollEvent() returned 0 */
/* Copy it anyways to maintain behavior */
memcpy( event, &my_event, sizeof(SDL_Event));
return(0);
}
void NWMovies_Ungrab(void)
{
sdl_wm_grabinput_ptr(SDL_GRAB_OFF);
}
void NWMovies_RestoreGrab(void)
{
SDL_Surface *cur_surf = NULL;
if( _NWMovies_NeedsToggle ) {
cur_surf = sdl_getvideosurface_ptr();
if( (cur_surf != NULL) && (cur_surf->flags & SDL_FULLSCREEN)) {
sdl_wm_togglefullscreen_ptr(cur_surf);
sdl_wm_togglefullscreen_ptr(cur_surf);
}
}
if( _NWMovies_Current_Grab_Mode != SDL_GRAB_QUERY ) {
sdl_wm_grabinput_ptr( _NWMovies_Current_Grab_Mode );
}
}
void NWMovies_setup_memory(unsigned int patch0,
unsigned int patch1,
unsigned int patch2,
unsigned int patch3,
unsigned int patch4,
unsigned int patch5, /* Enable movie button */
unsigned int patch6) /* Enable movie button */
{
unsigned char instruction[5]; /* 5 byte instruction */
unsigned long address_offset;
int i,j;
/* 2024/10/13 -- link code patching */
void *memory_address;
void *code_address;
unsigned long memory_value;
unsigned char patch0_code[] = "\xb8\x00\x00\x00\x00\x90\x5d\xc3"; /* These must be a multiple of 4 bytes */
unsigned char patch1_code[] = "\xb8\x00\x00\x00\x00\x90\x5d\xc3";
unsigned char patch2_code[] = "\x90\x90\x90\x90\x90\x83\xec\x08";
/* 168 & earlier */
unsigned char patch3_code1[] = "\x90\x90\x83\xec";
/* 169b2 */
unsigned char patch3_code2[] = "\x90\x90\x90\x83";
unsigned char patch3_buf[4];
int patch3_flag = 0; /* Change to 1, for 1.69 */
memcpy( (void *)patch3_buf, (void *)patch3, 4);
if( patch3_buf[2] == 144 ) { patch3_flag = 1; }
NWMovies_log(0, "NOTICE: NWMovies: PrePatch0: ");
NWMovies_printdata((void *)patch0, sizeof(patch0_code)-1);
NWMovies_log(0, "\nNOTICE: NWMovies: PrePatch1: ");
NWMovies_printdata((void *)patch1, sizeof(patch1_code)-1);
NWMovies_log(0, "\nNOTICE: NWMovies: PrePatch2: ");
NWMovies_printdata((void *)patch2, sizeof(patch2_code)-1);
NWMovies_log(0, "\n");
if( patch3_flag ) {
NWMovies_log(0, "NOTICE: NWMovies: PrePatch3: 169+: ");
NWMovies_printdata((void *)patch3, sizeof(patch3_code2)-1);
} else {
NWMovies_log(0, "NOTICE: NWMovies: PrePatch3: 168-: ");
NWMovies_printdata((void *)patch3, sizeof(patch3_code1)-1);
}
NWMovies_log(0, "\n");
NWMovies_memcpy((void *)patch0, patch0_code, sizeof(patch0_code)-1);
NWMovies_memcpy((void *)patch1, patch1_code, sizeof(patch1_code)-1);
NWMovies_memcpy((void *)patch2, patch2_code, sizeof(patch2_code)-1);
if( patch3_flag ) {
NWMovies_memcpy((void *)patch3, patch3_code2, sizeof(patch3_code2)-1);
} else {
NWMovies_memcpy((void *)patch3, patch3_code1, sizeof(patch3_code1)-1);
}
NWMovies_log(0, "NOTICE: NWMovies: PostPatch0: ");
NWMovies_printdata((void *)patch0, sizeof(patch0_code)-1);
NWMovies_log(0, "\nNOTICE: NWMovies: PostPatch1: ");
NWMovies_printdata((void *)patch1, sizeof(patch1_code)-1);
NWMovies_log(0, "\nNOTICE: NWMovies: PostPatch2: ");
NWMovies_printdata((void *)patch2, sizeof(patch2_code)-1);
NWMovies_log(0, "\n");
if( patch3_flag ) {
NWMovies_log(0, "NOTICE: NWMovies: PostPatch3: 169+: ");
NWMovies_printdata((void *)patch3, sizeof(patch3_code2)-1);
} else {
NWMovies_log(0, "NOTICE: NWMovies: PostPatch3: 168-: ");
NWMovies_printdata((void *)patch3, sizeof(patch3_code1)-1);
}
NWMovies_log(0, "\n");
/* Patch 4 */
NWMovies_log(0, "NOTICE: NWMovies: PrePatch4: ");
NWMovies_printdata((void *)patch4, 5);
NWMovies_log(0, "\n");
/* Build instruction */
address_offset = (unsigned long) &NWMovies_playmovie;
address_offset = address_offset - (unsigned long) patch4 - 5; /* How many bytes should the jump be */
memcpy(instruction + 1, &address_offset, 4);
instruction[0] = '\xe9';
/* Store instruction */
NWMovies_memcpy((void *)patch4, instruction, 5); /* Put the jump in */
_NWM_movie_retaddr = (unsigned long)patch4 + 0x5; /* setup return address */
NWMovies_log(0, "NOTICE: NWMovies: PostPatch4: ");
NWMovies_printdata((void *)patch4, 5);
NWMovies_log(0, "\n");
/* 2024/10/13 -- Pretend like were the linker, since getting assembler, the linker, and ASLR all friendly seems to be beyond my ken.
2024/10/13 -- patch nwmovies_link code #1 -- ESI storage */
memory_address = &_NWMovies_ESI; /* Where do we store the ESI variable? */
code_address = &NWMovies_link_store_ESI; /* Address where the ESI variable is */
NWMovies_log(0, "NOTICE: NWMovies: PreLinkage Patch 1 (ESI): Code: 0x%08x: (New Address: 0x%08x): ", code_address, memory_address);
NWMovies_printdata((void *)code_address, 5);
NWMovies_log(0, "\n");
/* Build instruction */
memcpy(instruction + 1, &memory_address, 4);
instruction[0] = '\xb8';
/* Store instruction */
NWMovies_memcpy((void *)code_address, instruction, 5); /* Insert the new code */
NWMovies_log(0, "NOTICE: NWMovies: PostLinkage Patch 1 (ESI): Code: 0x%08x: ", code_address);
NWMovies_printdata((void *)code_address, 5);
NWMovies_log(0, "\n");
/* 2024/10/13 -- patch nwmovies_link code #2 -- our playmovie2 call. */
memory_address = &NWMovies_playmovie2; /* address of working play movie funciton */
code_address = &NWMovies_link_playmovie2; /* Address of the call. */
memory_value = memory_address - code_address - 5; /* -5 address offset for size of call */
NWMovies_log(0, "NOTICE: NWMovies: PreLinkage Patch 2 (Play Call): Code: 0x%08x: (New code called: 0x%08x, Offset: 0x%08x): ", code_address, memory_address, memory_value );
NWMovies_printdata((void *)code_address, 5);
NWMovies_log(0, "\n");
/* Build instruction */
memcpy(instruction + 1, &memory_value, 4);
instruction[0] = '\xe8';
/* Store instruction */
NWMovies_memcpy((void *)code_address, instruction, 5); /* Insert the new code */
NWMovies_log(0, "NOTICE: NWMovies: PostLinkage Patch 2 (Play Call): Code: 0x%08x: ", code_address);
NWMovies_printdata((void *)code_address, 5);
NWMovies_log(0, "\n");
/* 2024/10/13 -- patch nwmovies_link code #3 -- the jmp at exit. */
memory_address = &_NWM_movie_retaddr; /* Indirect jump through retaddr */
code_address = &NWMovies_link_exit_jmp; /* Address where the exit jump is */
NWMovies_log(0, "NOTICE: NWMovies: PreLinkage Patch 3 (exit JMP): Code: 0x%08x: (New Address: 0x%08x): ", code_address, memory_address);
NWMovies_printdata((void *)code_address, 6);
NWMovies_log(0, "\n");
/* Build instruction -- of course the indirect jump is one byte bigger than our instruction array */
/* so, carefully only store the last 5 bytes, and storage is one address greater than memory_address */
memcpy(instruction + 1, &memory_address, 4);
instruction[0] = '\x25';
/* Store instruction */
NWMovies_memcpy((void *)code_address+1, instruction, 5); /* Insert the new code */
NWMovies_log(0, "NOTICE: NWMovies: PostLinkage Patch 3 (exit JMP): Code: 0x%08x: ", code_address);
NWMovies_printdata((void *)code_address, 5);
NWMovies_log(0, "\n");
/* Enable movie buttons - maybe */
if( patch5 != 0 && patch5 != 1 && patch6 != 0 && patch6 != 1 ) {
NWMovies_log(0, "NOTICE: NWMovies: MoviesPrePatch: ");
NWMovies_printdata((void *)patch5, patch6 - patch5);
NWMovies_log(0, "\n");
for( i = patch5; i < patch6; i=i+4 ) {
memcpy( instruction, (void *)i, 4);
j = 0;
while( j + i < patch6 && j < 4 ) {
instruction[j] = 0x90;
j++;
}
NWMovies_memcpy( (void *) i, instruction, 4 );
}
NWMovies_log(0, "NOTICE: NWMovies: MoviesPostPatch: ");
NWMovies_printdata((void *)patch5, patch6 - patch5);
NWMovies_log(0, "\n");
} else {
NWMovies_log(0, "NOTICE: NWMovies: Movie Button already enabled. Skipping movie patch.\n");
}
}
void NWMovies_printdata(char *ptr, int len)
{
int i;
for(i=0; i<len; i++) {
NWMovies_log(0, "%02x ", (unsigned char) ptr[i]);
}
return;
}
void NWMovies_memcpy(unsigned char *dest, unsigned char *src, size_t n)
{
/* int i; */
unsigned char *p = dest;
/* Align to a multiple of PAGESIZE, assumed to be a power of two */
/* Do two pages, just to make certain we get a big enough chunk */
p = (unsigned char *)(((int) p + PAGESIZE-1) & ~(PAGESIZE-1));
if( mprotect(p-PAGESIZE, 2 * PAGESIZE, PROT_READ|PROT_WRITE|PROT_EXEC) != 0 ) {
NWMovies_log(1, "ERROR: NWMovies: Could not de-mprotect(%p)\n", p);
exit(-1);
}
memcpy(dest, src, n);
/* restore memory protection */
if( mprotect(p-PAGESIZE, 2 * PAGESIZE, PROT_READ|PROT_EXEC) != 0 ) {
NWMovies_log(1, "ERROR: NWMovies: Could not re-mprotect(%p)\n", p);
exit(-1);
}
}
void NWMovies_playmovie2(void)
{
char *title;
SDL_Surface *current_surface = NULL;
Uint32 current_flags;
memcpy(&title, (void *)_NWMovies_ESI, 4);
if (!title) {
NWMovies_log(1, "ERROR: NWMovies: Movie title was NULL");
return;
}
/* try to release the Xserver grab, then play, then grab again */
// We have to hack up the current video surface here, since
// we need to lie (alot) to SDL since once we're full screen
// it doesn't allow us to release the grab. (Stupid SDL)
//
// This was previously why we had to rebuild libSDL to enable
// the visibility of the SDL_WM_GrabInputRaw() function.
// Now we could be cheating.
// There's three ways to do this that I can see.
// One: Make SDL_WM_GrabInputRaw() visible, and call it directly.
// This requires custom libSDL's to be built, and that's "hard".
//
// Two: The offically supported method, call SDL_WM_ToggleFullScreen().
// This works, but causes quite a bit of screen flickering, at movie start, and end
// As the main nwn client screen toggles fullscreen
//
// Three: Twiddle the SDL_FULLSCREEN bit off the 'current_surface->flags' bits ourselves.
// This is a hack, but looks much nicer.
//
// I'm going w/ the safe option #2, as the default.
// But the recommendation is to use Option #3 via (NWMOVIES_GRAB_HACK) for visual reasons.
current_surface = sdl_getvideosurface_ptr();
if(current_surface == NULL) {
NWMovies_log(1, "ERROR: NWMovies: PlayMovie2: Unable to get current SDL Surface for Internal Grab release. Giving up!\n");
abort();
}
// Save the "real" surface flags in "current_flags"
current_flags = current_surface->flags;
if( getenv( "NWMOVIES_GRAB_HACK" ) != NULL ) {
// Option #3 (hack alert!!)
current_surface->flags &= ~SDL_FULLSCREEN;
} else {
// Option #2
// If we're full screen, toggle the fullscreen bits.
if( current_flags & SDL_FULLSCREEN ) {
sdl_wm_togglefullscreen_ptr(current_surface);
}
}
NWMovies_log(0, "NOTICE: NWMovies: Attempting to play movie \"%s\"\n", title);
NWMovies_Ungrab();
NWMovies_runcommand(title);
// And finally restore the fullscreen mode, and any grab settings.
if( getenv( "NWMOVIES_GRAB_HACK" ) != NULL ) {
// Option #3 (hack alert!!)
current_surface->flags = current_flags;
} else {
//if the "real" surface flags" were full screen, we gotta toggle back.
if( current_flags & SDL_FULLSCREEN ) {
sdl_wm_togglefullscreen_ptr(current_surface);
}
}
NWMovies_RestoreGrab();
return;
}