1 /*
2  * File: main-ros.c
3  *
4  * Abstract: Support for RISC OS versions of Angband, including support
5  * for multitasking and dynamic areas.
6  *
7  * Authors: Musus Umbra, Andrew Sidwell, Ben Harrison, and others.
8  *
9  * Licences: Angband licence.
10  */
11 
12 #ifdef __riscos
13 
14 #include "angband.h"
15 
16 /*
17  * Purpose: Support for RISC OS Angband 2.9.x onwards (and variants)
18  *
19  * NB: This code is still under continuous development - if you want to use
20  * it for your own compilation/variant, please contact me so that I can
21  * keep you up to date and give you support :)
22  *
23  * Prerequisites to compiling:
24  *
25  * DeskLib 2.30 or later  (earlier versions may be OK though)
26  *
27  * An ANSI C compiler (tested with Acorn's C/C++ and GCC, but should be OK
28  *                     with any decent compiler)
29  *
30  * My binary distribution (for the templates and other bits)
31  *
32  * Note:
33  *   The following symbols are *required* and *must* be defined properly.
34  */
35 
36 /*
37  * VARIANT & VERSION
38  *   These two get variant and version data from Angband itself; older
39  *   variants may not have these defined and will have to be altered.
40  */
41 #define VARIANT		VERSION_NAME
42 #define VERSION		VERSION_STRING
43 
44 /*
45  * PORTVERSION
46  *   This is the port version; it appears in the infobox.
47  */
48 #define PORTVERSION	"1.29-dev (2003-07-27)"
49 
50 /*
51  * RISCOS_VARIANT
52  *  This must match the entry in the !Variant Obey file, and it must only
53  *  contain characters that are valid as part of a RISC OS path variable.
54  *  [eg. "Yin-Yangband" is not okay, "EyAngband" is.]
55  */
56 #define RISCOS_VARIANT	"Zangband"
57 
58 /*
59  * AUTHORS
60  *  For the info box. [eg. "Ben Harrison"]
61  */
62 #define AUTHORS		"Zangband dev team"
63 
64 /*
65  * PORTERS
66  *  For the info box. [eg. "Musus Umbra"]
67  */
68 #define PORTERS		"A. Sidwell"
69 
70 /*
71  * ICONNAME
72  *  Iconbar icon sprite name eg. "!angband".  Note that this must be a valid
73  *  sprite name; it may need modifying for long variant names.
74  */
75 #define ICONNAME	"!"RISCOS_VARIANT
76 
77 /*
78  * PDEADCHK
79  *   This should expand to an expression that is true if the player is dead.
80  *   [eg. (p_ptr->is_dead) for Angband or (!alive || dead) for some Zangbands]
81  */
82 
83 #define PDEADCHK	(p_ptr->state.is_dead)
84 
85 /*
86  * The following symbols control the (optional) file-cache:
87  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
88  * NB: Variants that don't repeatedly read any files whilst running
89  * (eg. vanilla, sang, etc) should NOT define USE_FILECACHE, etc. as
90  * it causes a non-negligable amount of code to be compiled in.
91  *
92  * NB: The file-cache functions require that some code in files.c is modified
93  * to use the cached_* functions.  This should be utterly trivial.
94  *
95  * NB: The returned handle from cached_fopen() is almost certainly *NOT*
96  * a |FILE*| (although it may be if the cache cannot accomodate the file).
97  *
98  * Therefore, you *MUST* ensure that any file opened with cached_fopen()
99  * is only ever accessed via cached_fgets() and cached_fclose().
100  *
101  * Failure to do so will result in, ahem, unpleasantness.  Extreme
102  * unpleasantness.  "Him fall down, go boom."
103  *
104  * This /may/ change in the near future (ie. to apply caching in a
105  * transparent manner), so do keep a backup of files.c (and any other files
106  * you modify).  You always keep backups anyway, don't you?  Don't you?!
107  */
108 
109 /*
110  * USE_FILECACHE
111  *   if defined then some caching functions will be compiled for use by the
112  *   various get_rnd_line(), etc. in files.c.  This could be used in a
113  *   variety of places that read data repeatedly, but it's up to you to
114  *   implement it then.
115  */
116 
117 /* #define USE_FILECACHE */
118 
119 /*
120  * SMART_FILECACHE
121  *   This causes lines beginning with '#' (and blank lines) to be discarded
122  *   when caching files.  This should help Zangband 2.2.5+ but could cause
123  *   trouble for other variants.  If defined, then smart file caching will be
124  *   on by default.
125  */
126 
127 /* #define SMART_FILECACHE */
128 
129 /*
130  * ABBR_FILECACHE
131  *   ABBR_FILECACHE causes data read into file-cache to be compressed (using a
132  *   simple set of abbreviations) by default.  This can be overridden using a
133  *   command line option.  If this symbol is not defined then no compression
134  *   code will be compiled and the user option will be ignored/unavailable.
135  */
136 
137 /* #define ABBR_FILECACHE */
138 
139 /*
140  * Note:
141  *   The following symbols control debugging information.
142  */
143 
144 /*
145  * FE_DEBUG_INFO
146  *  If defined, some functions will be compiled to display some info. on the
147  *  state of the front-end (accessible) from the '!' user menu.
148  *
149  *  NB: For actual releases you should NOT define this symbol since it causes
150  *  a non-negligable amount of code/data to be sucked in.
151  */
152 /* #define FE_DEBUG_INFO */
153 
154 /*
155  * USE_DA
156  *  If defined, it enables the use of dynamic areas (these are still only
157  *  used when the !Variant file allows it).  It is likely that this option
158  *  will eventually be removed altogether as there is no major advantege
159  *  to using DAs over just using the Wimpslot.
160  */
161 #define USE_DA
162 
163 
164 /* Constants, etc. ---------------------------------------------------------*/
165 
166 /* Deal with any weird file-caching symbols */
167 #ifndef USE_FILECACHE
168 # undef ABBR_FILECACHE
169 # undef SMART_FILECACHE
170 #endif
171 
172 /* Maximum terminals */
173 #define MAX_TERM_DATA 8
174 
175 /* Menu entry numbers */
176 #define IBAR_MENU_INFO			0
177 #define IBAR_MENU_SAVE			1
178 #define IBAR_MENU_FULLSCREEN	2
179 #define IBAR_MENU_GAMMA			3
180 #define IBAR_MENU_SOUND			4
181 #define IBAR_MENU_WINDOWS		5
182 #define IBAR_MENU_SAVECHOICES	6
183 #define IBAR_MENU_QUIT			7
184 
185 #define TERM_MENU_INFO			0
186 #define TERM_MENU_SAVE			1
187 #define TERM_MENU_FONT			2
188 #define TERM_MENU_WINDOWS		3
189 
190 /* Icon numbers */
191 #define SND_VOL_SLIDER			0
192 #define SND_VOL_DOWN			1
193 #define SND_VOL_UP				2
194 #define SND_ENABLE				3
195 
196 #define GAMMA_ICN				0
197 #define GAMMA_DOWN				1
198 #define GAMMA_UP				2
199 
200 #define SAVE_ICON				2
201 #define SAVE_PATH				1
202 #define SAVE_OK					0
203 #define SAVE_CANCEL				3
204 
205 /* Position and size of the colours strip in the gamma window */
206 #define GC_XOFF 20
207 #define GC_YOFF -14
208 #define GC_WIDTH 512
209 #define GC_HEIGHT 72
210 
211 /* Maximum and minimum allowed volume levels */
212 #define SOUND_VOL_MIN			16
213 #define SOUND_VOL_MAX			176
214 
215 /*--------------------------------------------------------------------------*/
216 
217 
218 #undef rename
219 #undef remove
220 
221 #include "Desklib:Event.h"
222 #include "Desklib:EventMsg.h"
223 #include "Desklib:Template.h"
224 #include "Desklib:Window.h"
225 #include "Desklib:Handler.h"
226 #include "Desklib:Screen.h"
227 #include "Desklib:Menu.h"
228 #include "Desklib:Msgs.h"
229 #include "Desklib:Icon.h"
230 #include "Desklib:Resource.h"
231 #include "Desklib:SWI.h"
232 #include "Desklib:Time.h"
233 #include "Desklib:Sound.h"
234 #include "Desklib:KeyCodes.h"
235 #include "Desklib:Kbd.h"
236 #include "Desklib:GFX.h"
237 #include "Desklib:ColourTran.h"
238 #include "Desklib:Error.h"
239 #include "Desklib:Coord.h"
240 #include "Desklib:Slider.h"
241 #include "Desklib:Hourglass.h"
242 #include "Desklib:Save.h"
243 #include "Desklib:Sprite.h"
244 #include "Desklib:KernelSWIs.h"
245 
246 #include <stdlib.h>
247 #include <stdio.h>
248 #include <string.h>
249 #include <ctype.h>
250 #include <signal.h>
251 #include <stdarg.h>
252 #include <time.h>
253 #include <math.h>
254 
255 /*--------------------------------------------------------------------------*/
256 
257 /*
258  | We use the hourglass around calls to Wimp_Poll in an attempt to stop
259  | users thinking that the game has 'hung'.
260  | Kamband/Zangband and the Borg in particular can have quite long delays at
261  | times.
262  */
263 #define Start_Hourglass \
264 	{ if ( use_glass && !glass_on ) { glass_on=1; Hourglass_Start(50); } }
265 #define Stop_Hourglass \
266 	{ if ( glass_on ) { glass_on=0; Hourglass_Off(); } }
267 
268 
269 /*--------------------------------------------------------------------------*/
270 /* Types																	*/
271 /*--------------------------------------------------------------------------*/
272 
273 /*
274  | A ZapRedraw block
275  */
276 typedef struct
277 {
278 	union
279 	{
280 		unsigned int value;
281 		struct
282 		{
283 			unsigned int vdu:1;
284 			unsigned int double_height:1;
285 			unsigned int extension:1;
286 			unsigned int padding:29;
287 		}
288 		bits;
289 	}
290 	r_flags;
291 
292 	int r_minx;	/* min x of redraw in pixels from LHS, incl */
293 	int r_miny;	/* min y of redraw in pixels from top, incl */
294 	int r_maxx;	/* max x of redraw in pixels from LHS, excl */
295 	int r_maxy;	/* max y of redraw in pixels from top, excl */
296 
297 	void *r_screen;	/* DSA: address of screen to write to (0=>read) */
298 	int r_bpl;	/* DSA: bytes per raster line */
299 
300 	int r_bpp;	/* log base 2 of bits per pixel */
301 	int r_charw;	/* width of a character in pixels */
302 	int r_charh;	/* height of a character in pixels */
303 	void *r_caddr;	/* DSA: ->character cache | VDU: ->font name */
304 	int r_cbpl;	/* DSA: #bytes/character line | VDU: x OS offset */
305 	int r_cbpc;	/* DSA: #bytes/character | VDU: y OS offset */
306 
307 	int r_linesp;	/* line spacing (pixels) */
308 
309 	void *r_data;	/* -> text to display */
310 	int r_scrollx;	/* see Redraw dox */
311 	int r_scrolly;	/* see Redraw dox */
312 
313 	void *r_palette;	/* -> palette lookup table */
314 	int r_for;	/* foreground colour at start of line */
315 	int r_bac;	/* background colour at start of line */
316 
317 	void *r_workarea;	/* -> word aligned workspace */
318 
319 	int r_magx;	/* log2 x OS coords per pixel */
320 	int r_magy;	/* log2 y OS coords per pixel */
321 
322 	int r_xsize;	/* width of screen in pixels */
323 	int r_ysize;	/* height of screen in pixels */
324 
325 	int r_mode;	/* current screen mode */
326 }
327 ZapRedrawBlock;
328 
329 
330 /*
331  | We cache font data using an array of 'font handles' (since there is a
332  | known maximum no. of fonts required).
333  | This is what a font 'handle' looks like:
334  */
335 typedef struct
336 {
337 	char *name;	/* font name */
338 	int usage;	/* usage count */
339 	int w, h;	/* width, height */
340 	int f, l;	/* first and last character defined */
341 	void *bpp_1;	/* source bitmap */
342 	void *bpp_n;	/* bitmap for the current screen mode */
343 }
344 ZapFont;
345 
346 /*
347  | A struct to hold all the data relevant to a term window
348  */
349 typedef struct
350 {
351 	term t;	/* The Term itself */
352 	window_handle w;	/* Window handle */
353 	ZapFont *font;	/* Font */
354 	wimp_box changed_box;	/* Area out of date */
355 	struct
356 	{
357 	wimp_point pos;	/* Cursor position */
358 	BOOL visible;	/* visibility flag */
359 	}
360 	cursor;
361 	char name[12];	/* Name to give menus opened from the term */
362 	int def_open;	/* Open by default? */
363 	wimp_box def_pos;	/* default position */
364 	wimp_point def_scroll;	/* default scroll offset */
365 	int unopened;	/* Has this window not been opened yet? */
366 }
367 term_data;
368 
369 
370 
371 /*--------------------------------------------------------------------------*/
372 /* ZapRedraw SWI numbers													*/
373 /*--------------------------------------------------------------------------*/
374 
375 #define SWI_ZapRedraw_ 0x48480
376 #define SWI_ZapRedraw_RedrawArea (SWI_ZapRedraw_ + 0x00)
377 #define SWI_ZapRedraw_GetPaletteEntry (SWI_ZapRedraw_ + 0x01)
378 #define SWI_ZapRedraw_RedrawRaster (SWI_ZapRedraw_ + 0x02)
379 #define SWI_ZapRedraw_ConvertBitmap (SWI_ZapRedraw_ + 0x03)
380 #define SWI_ZapRedraw_PrepareDataLine (SWI_ZapRedraw_ + 0x04)
381 #define SWI_ZapRedraw_AddCursor (SWI_ZapRedraw_ + 0x05)
382 #define SWI_ZapRedraw_FindCharacter (SWI_ZapRedraw_ + 0x06)
383 #define SWI_ZapRedraw_MoveBytes (SWI_ZapRedraw_ + 0x07)
384 #define SWI_ZapRedraw_CachedCharSize (SWI_ZapRedraw_ + 0x08)
385 #define SWI_ZapRedraw_ConvBitmapChar (SWI_ZapRedraw_ + 0x09)
386 #define SWI_ZapRedraw_CreatePalette (SWI_ZapRedraw_ + 0x0a)
387 #define SWI_ZapRedraw_InsertChar (SWI_ZapRedraw_ + 0x0b)
388 #define SWI_ZapRedraw_ReadSystemChars (SWI_ZapRedraw_ + 0x0c)
389 #define SWI_ZapRedraw_ReverseBitmaps (SWI_ZapRedraw_ + 0x0d)
390 #define SWI_ZapRedraw_ReadVduVars (SWI_ZapRedraw_ + 0x0e)
391 #define SWI_ZapRedraw_GetRectangle (SWI_ZapRedraw_ + 0x0f)
392 #define SWI_ZapRedraw_AddVduBitmaps (SWI_ZapRedraw_ + 0x10)
393 #define SWI_ZapRedraw_CacheFontChars (SWI_ZapRedraw_ + 0x11)
394 #define SWI_ZapRedraw_SpriteSize (SWI_ZapRedraw_ + 0x12)
395 #define SWI_ZapRedraw_RedrawWindow (SWI_ZapRedraw_ + 0x13)
396 
397 
398 /*
399  | Other SWI numbers that aren't defined in DeskLib's SWI.h:
400  */
401 #define SWI_OS_ScreenMode 0x65
402 #define SWI_OS_DynamicArea 0x66
403 #define SWI_ColourTrans_ReturnColourNumber 0x40744
404 #define SWI_Wimp_ReportError 0x400df
405 #define SWI_PlayIt_Volume 0x4d146
406 
407 
408 
409 /*--------------------------------------------------------------------------*
410  | File scope variables													 |
411  *--------------------------------------------------------------------------*/
412 static int ftype = 0xffd;	/* hack so saved games get the right type */
413 static int filehandle[16];	/* we keep track of open files with this */
414 static int openfiles = 0;	/* how many files are currently open */
415 
416 /*
417  | Paths we use...
418  */
419 static char resource_path[260] = "";	/* Path pointng to "!Angband.Lib." */
420 static char scrap_path[260] = "";	/* Path to create scrap files on */
421 static char choices_file[3][260] =
422 { "", "", "" };	/* Choices paths (read/write, mirror, read) */
423 static char alarm_file[2][260] =
424 { "", "" };	/* Alarm choices paths (read/write, mirror, read) */
425 /*
426  | So we can use something more meaningful later...
427  | NB: Mirror is only meaningful for Choices and we don't
428  | even reserve space for alarm_file[CHFILE_MIRROR].
429  */
430 #define CHFILE_WRITE 0
431 #define CHFILE_READ 1
432 #define CHFILE_MIRROR 2
433 
434 /*
435  | Other 'globals':
436  */
437 static int initialised = 0;	/* Used to determine whether to try to save */
438 static int game_in_progress = 0;	/* if Quit (or core() is called),  etc. */
439 
440 static byte a_palette[256][4];	/* a copy of the raw Angband palette */
441 static unsigned int palette[256];	/* palette as gamma'd bbggrrxx words */
442 static unsigned int zpalette[256];	/* And our version for ZapRedraw */
443 static double gamma = 1.0;	/* assume gamma of 1.0 if unspecified */
444 
445 static int enable_sound = 0;	/* enable sound FX */
446 static int sound_volume = 127;	/* Full volume */
447 static int force_mono = 0;	/* force monochrome */
448 static int start_fullscreen = 0;	/* start up full screen (added in 1.18) */
449 static int hack_flush = 0;	/* Should TERM_XTRA_FLUSH wait for all keys to be released? */
450 static int flush_scrap = 1;	/* Should any scrapfiles (incl. filecache) be deleted at exit? */
451 static int max_file_cache_size = 64 << 10;
452 static unsigned int vfiletype;
453 static int allow_iclear_hack = 0;	/* Allow the hideously evil Iclear workaround thing */
454 static int alarm_type = 0;	/* is there an alarm set? */
455 static int alarm_h = 0, alarm_m = 0;	/* alarm time (midnight) */
456 static char alarm_message[80] = "Time for bed!";	/* the message to give */
457 static int alarm_disp = 0;	/* is the alarm being displayed? */
458 static int alarm_beep = 0;	/* should be beep? */
459 static const char *alarm_types[] =
460 { "Off", "On (one-shot)", "On (repeating)", "On (one-shot)" };
461 static unsigned int alarm_lastcheck = 0;
462 
463 
464 /* A little macro to save some typing later: */
465 #define COLOUR_CHANGED(x) \
466 	( (angband_color_table[x][1]!=a_palette[x][1]) || \
467 	  (angband_color_table[x][2]!=a_palette[x][2]) || \
468 	  (angband_color_table[x][3]!=a_palette[x][3]) )
469 
470 static int got_caret = 0;	/* Do we own the caret? */
471 static int key_pressed = 0;	/* 'Key has been pressed' Flag */
472 static int use_glass = 1;	/* use the hourglass between WimpPolls? */
473 static int glass_on = 1;	/* is the hourglass on? */
474 static int user_menu_active = FALSE;	/* set to TRUE when the user menu is active */
475 
476 /* Font system variables */
477 static ZapFont fonts[MAX_TERM_DATA + 1];	/* The +1 is for the system font */
478 
479 /* The system font is always font 0 */
480 #define SYSTEM_FONT (&(fonts[0]))
481 
482 /* Term system variables */
483 static term_data data[MAX_TERM_DATA];	/* One per term */
484 
485 #ifndef FULLSCREEN_ONLY
486 static char r_data[24 * (80 * 5 + 4) + 25 * 4];	/* buffer for ZapRedraw data */
487 
488 /* Wimp variables */
489 static icon_handle ibar_icon;	/* Iconbar icon handle */
490 static window_handle info_box;	/* handle of the info window */
491 static window_handle gamma_win;	/* gamma correction window */
492 static window_handle sound_win;	/* sound options window */
493 static window_handle save_box;	/* The savebox */
494 static menu_ptr ibar_menu;	/* Iconbar menu */
495 static menu_ptr term_menu;	/* Term window menu */
496 static menu_ptr wind_menu;	/* windows (sub) menu */
497 static menu_ptr font_menu;	/* Font (sub)menu */
498 
499 static save_saveblock *saveblk = NULL;	/* For the save box */
500 
501 /* List of Wimp messages we want to be given */
502 static int message_list[] =
503 {
504 	message_MODECHANGE,
505 	message_PALETTECHANGE,
506 
507 	/* For the savebox */
508 	message_MENUWARN,
509 	message_DATASAVEACK,
510 
511 	message_PREQUIT,
512 	0
513 };
514 
515 
516 static term_data *menu_term;	/* term the last menu was opened for */
517 
518 #endif /* FULLSCREEN_ONLY */
519 
520 static ZapRedrawBlock zrb;	/* a redraw block */
521 
522 /* Cursor colour */
523 #define CURSOR_COLOUR	255		/* Cursor's Angband colour */
524 #define CURSOR_RGB		0x00ffff00	/* if undefined, use bbggrrxx */
525 
526 static int cursor_rgb = -1;	/* colour to use for cursor */
527 
528 static int fullscreen_mode = 0;	/* screen mode in use */
529 static int old_screenmode = 0;	/* Mode we started out in */
530 static int *fullscreen_font = 0;	/* font data for fullscreen use */
531 static int *fullscreen_base = 0;	/* base address of screen */
532 static int fullscreen_height;	/* height of the fullscreen font */
533 static int fullscreen_topline;	/* raster offset of fullscreen */
534 
535 #define KEYPRESS_QUIT    0x1cc	/* F12 gets back to the desktop */
536 #define TERM_TOPLINE_HR  32		/* vertical pixel offset in mode 27 */
537 #define TERM_TOPLINE_LR  16		/* vertical pixel offset in mode 12 */
538 #define TIME_LINE        26		/* Line to display the clock on */
539 
540 /* text to display at the bottom left of the fullscreen display */
541 static const char *fs_quit_key_text = "Press f12 to return to the desktop";
542 static const char *alarm_cancel_text = "(Press ^Escape to cancel the alarm)";
543 
544 /* Debugging flags, etc. */
545 static int log_g_malloc = 0;	/* Log calls to ralloc, etc */
546 static int show_sound_alloc = 0;	/* Log sound mappings, etc */
547 
548 /* Activate file caching? */
549 #ifdef USE_FILECACHE
550 static int use_filecache = TRUE;
551 #else
552 static int use_filecache = FALSE;
553 #endif
554 
555 /* Cripple some things to save memory */
556 static int minimise_memory = 0;
557 
558 /* Forward declarations of some of the Full Screen Mode stuff */
559 static void enter_fullscreen_mode(void);
560 static void leave_fullscreen_mode(void);
561 static void set_keys(int claim);
562 
563 /* Forwards declarations of the sound stuff */
564 static void initialise_sound(void);
565 static void play_sound(int event);
566 
567 /* Forward declarations of Term hooks, etc. */
568 static void Term_init_acn(term *t);
569 static errr Term_user_acn(int n);
570 
571 #ifndef FULLSCREEN_ONLY
572 static errr Term_curs_acn(int x, int y);
573 static errr Term_text_acn(int x, int y, int n, byte a, cptr s);
574 static errr Term_xtra_acn(int n, int v);
575 static errr Term_wipe_acn(int x, int y, int n);
576 static errr Term_xtra_acn_check(void);
577 static errr Term_xtra_acn_event(void);
578 static errr Term_xtra_acn_react(void);
579 #endif /* FULLSCREEN_ONLY */
580 
581 static errr Term_curs_acnFS(int x, int y);
582 static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
583 static errr Term_xtra_acnFS(int n, int v);
584 static errr Term_wipe_acnFS(int x, int y, int n);
585 static errr Term_xtra_acn_checkFS(void);
586 static errr Term_xtra_acn_clearFS(void);
587 static errr Term_xtra_acn_eventFS(void);
588 static errr Term_xtra_acn_reactFS(int force);
589 
590 #ifdef USE_DA
591 /* Forward declarations of the memory stuff */
592 static void init_memory(int, int);
593 #endif
594 
595 /* Forward declarations of the alarm stuff */
596 static void check_alarm(void);
597 #ifndef FULLSCREEN_ONLY
598 static void trigger_alarm_desktop(void);
599 #endif /* FULLSCREEN_ONLY */
600 static void ack_alarm(void);
601 static void write_alarm_choices(void);
602 static void read_alarm_choices(void);
603 
604 
605 /* This just shows some debugging info (if enabled with FE_DEBUG_INFO) */
606 static void show_debug_info(void);
607 
608 
609 
610 /* File-caching functions (if enabled at compile time) */
611 #ifdef USE_FILECACHE
612 FILE *cached_fopen(char *name, char *mode);
613 errr cached_fclose(FILE *fch);
614 errr cached_fgets(FILE *fch, char *buffer, int max_len);
615 #endif
616 
617 /*
618  | These functions act as malloc/free, but (if possible) using memory
619  | in the 'Game' Dynamic Area created by init_memory()
620  | We attach these functions to the ralloc_aux and rnfree_aux hooks
621  | that z-virt.c provides.
622  */
623 #ifdef USE_DA
624 static vptr g_malloc(huge size);
625 static vptr g_free(vptr blk);
626 #else
627   #define g_malloc(size) malloc(size);
628   #define g_free(block) free(block);
629 #endif
630 
631 /*
632  | These functions act as malloc/free, but (if possible) using memory
633  | in the 'Fonts' Dynamic Area created by init_memory()
634  */
635 #ifdef USE_DA
636 static void* f_malloc(size_t size);
637 static void f_free(void *blk);
638 #else
639   #define f_malloc(size) malloc(size);
640   #define f_free(block) free(block);
641 #endif
642 
643 /*
644  | These two functions perpetrate great evil to stop IClear from mucking
645  | with the cursor keys in fullscreen mode.
646  */
647 static void iclear_hack(void);
648 static void remove_iclear_hack(void);
649 
650 /*
651  | We use this to locate the choices file(s)...
652  */
653 static char *find_choices(int write);
654 static char *find_choices_mirror(void);
655 static char *find_alarmfile(int write);
656 
657 
658 
659 /*
660  | This function is supplied as a wrapper to the save_player function.
661  |
662  | Its purpose is to change the filename that the game will be saved with
663  | the leafname "!!PANIC!!" so that panic saves that break the savefile
664  | won't overwrite the original savefile.
665  |
666  | To get this to work, you'll need to ammend files.c and change the call
667  | to save_player in the panic save function(s) (search for "panic save")
668  | to a call to save_player_panic_acn.  You can declare a prototype for
669  | the function if you like.
670  */
671 
save_player_panic_acn(void)672 extern int save_player_panic_acn(void)
673 {
674 	char *e, *l;
675 
676 	/* Find the final / in the savefile name */
677 	for (l = e = savefile; *e; e++)
678 		if (*e == '/')
679 		{
680 			l = e + 1;
681 		}
682 
683 	/* Write over the current leaf with the special panic one */
684 	strcpy(l, "!!PANIC!!");
685 
686 	/* save the game */
687 	return save_player();
688 }
689 
690 
691 /*--------------------------------------------------------------------------*/
692 /* Error reporting, etc.													*/
693 /*--------------------------------------------------------------------------*/
694 
695 
696 /* Tell the user something important */
plog_hook(cptr str)697 static void plog_hook(cptr str)
698 {
699 	Msgs_Report(1, "err.plog", str);
700 }
701 
702 /* Tell the user something, then quit */
quit_hook(cptr str)703 static void quit_hook(cptr str)
704 {
705 	/* str may be null */
706 	if (str) Msgs_Report(1, "err.quit", str);
707 	exit(0);
708 }
709 
710 /* Tell the user something then crash ;) */
core_hook(cptr str)711 static void core_hook(cptr str)
712 {
713 	Msgs_Report(1, "err.core", str);
714 
715 	if (game_in_progress && character_generated)
716 		save_player_panic_acn();
717 
718 	quit(NULL);
719 }
720 
debug(const char * fmt,...)721 static void debug(const char *fmt, ...)
722 {
723 	va_list ap;
724 	char buffer[260];
725 	va_start(ap, fmt);
726 	vsprintf(buffer, fmt, ap);
727 	va_end(ap);
728 	plog(buffer);
729 }
730 
731 
732 
733 /*
734 static void oserror_handler( int sig )
735 {
736 	core(_kernel_last_oserror()->errmess);
737 }
738 */
739 
740 
741 /*--------------------------------------------------------------------------*/
742 /* File handling															*/
743 /*--------------------------------------------------------------------------*/
744 
myFile_Open(const char * name,int mode)745 static int myFile_Open(const char *name, int mode)
746 {
747 	int handle;
748 	if (SWI(2, 1, SWI_OS_Find, mode, name, /**/ &handle))
749 		return 0;
750 	return handle;
751 }
752 
myFile_Size(const char * name)753 static int myFile_Size(const char *name)
754 {
755 	int size, type;
756 	if (SWI(2, 5, SWI_OS_File, 17, name, /**/ &type, 0, 0, 0, &size))
757 		return -2;
758 	return type ? size : -1;
759 }
760 
myFile_Close(const int handle)761 static os_error *myFile_Close(const int handle)
762 {
763 	return SWI(2, 0, SWI_OS_Find, 0, handle);
764 }
765 
myFile_Seek(const int handle,const int offset)766 static os_error *myFile_Seek(const int handle, const int offset)
767 {
768 	return SWI(3, 0, SWI_OS_Args, 1, handle, offset);
769 }
770 
myFile_WriteBytes(const int handle,const void * buf,const int n)771 static int myFile_WriteBytes(const int handle, const void *buf, const int n)
772 {
773 	int ntf;
774 	if (SWI(4, 4, SWI_OS_GBPB, 2, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
775 		return n;
776 	return ntf;
777 }
778 
myFile_ReadBytes(const int handle,void * buf,const int n)779 static int myFile_ReadBytes(const int handle, void *buf, const int n)
780 {
781 	int ntf;
782 	if (SWI(4, 4, SWI_OS_GBPB, 4, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
783 		return n;
784 	return ntf;
785 }
786 
myFile_SetType(const char * n,const int type)787 static os_error *myFile_SetType(const char *n, const int type)
788 {
789 	return SWI(3, 0, SWI_OS_File, 18, n, type);
790 }
791 
792 
myFile_Extent(const int handle)793 static int myFile_Extent(const int handle)
794 {
795 	int ext;
796 	if (SWI(2, 3, SWI_OS_Args, 2, handle, /**/ NULL, NULL, &ext))
797 		return -1;
798 	return ext;
799 }
800 
801 
802 /*
803  | Determine if one file is newer than another.
804  |
805  | The filenames should be specified in RISC OS style.
806  |
807  | Returns -1 if 'a' is newer than 'b'.
808  */
file_is_newer(const char * a,const char * b)809 static int file_is_newer(const char *a, const char *b)
810 {
811 	os_error *e;
812 	struct
813 	{
814 	unsigned int msw;
815 	unsigned int lsw;
816 	}
817 	a_time;
818 	struct
819 	{
820 	unsigned int msw;
821 	unsigned int lsw;
822 	}
823 	b_time;
824 	int a_type, b_type;
825 
826 	/* Get the datestamp of the 'a' file */
827 	e = SWI(2, 4, SWI_OS_File,
828 			/* In */
829 			17, (int)a,
830 			/* Out */
831 			&a_type,			/* object type */
832 			NULL, &(a_time.msw),	/* Load Addr */
833 			&(a_time.lsw)		/* Exec Addr */
834 		);
835 	if (e)
836 	{
837 		core(e->errmess);
838 	}
839 
840 
841 	/* Get the datestamp of the 'b' file */
842 	e = SWI(2, 4, SWI_OS_File,
843 			/* In */
844 			17, (int)b,
845 			/* Out */
846 			&b_type,			/* object type */
847 			NULL, &(b_time.msw),	/* Load Addr */
848 			&(b_time.lsw)		/* Exec Addr */
849 		);
850 	if (e)
851 	{
852 		core(e->errmess);
853 	}
854 
855 	/* If 'b' doesn't exist then 'b' is OOD. */
856 	if (!b_type)
857 	{
858 		return -1;
859 	}
860 	/* If 'a' doesn't exist then 'b' isn't OOD. (?) */
861 	if (!a_type)
862 	{
863 		return 0;
864 	}
865 
866 	/* Compare the timestamps (assume that the files are typed) */
867 	if ((a_time.msw & 0xff) >= (b_time.msw & 0xff))
868 		if ((a_time.lsw) > (b_time.lsw))
869 			return -1;			/* OOD */
870 	return 0;					/* Not OOD */
871 }
872 
873 
874 /*
875  | As fprintf, but outout to all files (if their handles are non zero).
876  | NB: void type.
877  */
f2printf(FILE * a,FILE * b,const char * fmt,...)878 static void f2printf(FILE *a, FILE *b, const char *fmt, ...)
879 {
880 	va_list ap;
881 	char buffer[2048];
882 	va_start(ap, fmt);
883 	vsprintf(buffer, fmt, ap);
884 	va_end(ap);
885 	if (a)
886 	{
887 		fprintf(a, buffer);
888 	}
889 	if (b)
890 	{
891 		fprintf(b, buffer);
892 	}
893 	va_end(ap);
894 }
895 
896 
897 
898 
899 /*--------------------------------------------------------------------------*/
900 /* Clean up (ie. close files, etc). 										*/
901 /*--------------------------------------------------------------------------*/
902 
final_acn(void)903 static void final_acn(void)
904 {
905 	int i;
906 
907 	for (i = 0; i < openfiles; i++)
908 		myFile_Close(filehandle[i]);
909 
910 	if (fullscreen_mode)
911 	{
912 		/* Restore the screen mode */
913 		Wimp_SetMode(old_screenmode);
914 
915 		/* Restore the various soft keys */
916 		set_keys(FALSE);
917 
918 		/*
919 		   | Hack: Early WIMP versions do the "Press SPACE" thing, or something
920 		   | odd.  It's bloody annoying, whatever it is...
921 		 */
922 		if (event_wimpversion < 300)
923 			Wimp_CommandWindow(-1);
924 	}
925 
926 	if (flush_scrap && *scrap_path)
927 	{
928 		char tmp[512];
929 		strcpy(tmp, scrap_path);
930 		tmp[strlen(tmp) - 1] = 0;	/* Remove trailing dot */
931 		SWI(4, 0, SWI_OS_FSControl, 27, tmp, 0, 1);	/* ie. "*Wipe <scrapdir> r~c~v~f" */
932 	}
933 
934 #ifdef FULLSCREEN_ONLY
935 	Wimp_CommandWindow(-1);
936 #endif /* FULLSCREEN_ONLY */
937 
938 	Stop_Hourglass;
939 }
940 
941 
942 /*--------------------------------------------------------------------------*
943  | Various UNIX-like support funtions									   |
944  *--------------------------------------------------------------------------*/
945 
946 /*
947  | Hack: determine whether filenames should be truncated to 10 chars or not.
948  |
949  | Needed since RO2 (and RO3 with Truncate configured off) will return
950  | errors instead of automatically truncating long filenames.
951  */
truncate_names(void)952 static int truncate_names(void)
953 {
954 	int r1, r2;
955 
956 	/* First, check the OS version */
957 	OS_Byte(osbyte_READOSIDENTIFIER, 0x00, 0xff, &r1, &r2);
958 
959 	/* Assume that we need to truncate if running under RO2 */
960 	if (r1 == 0xa1 || r1 == 0xa2)
961 		return TRUE;
962 
963 	/* Okay, so we've got RO3 (or later), so check the CMOS RAM */
964 	OS_Byte(osbyte_READCMOSRAM, 28, 0, &r1, &r2);
965 
966 	/* Bit 0 of byte 28 is the Truncate flag */
967 	return !(r2 & 1);
968 }
969 
970 
971 /*
972  | The PathName translation is now done by two separate functions:
973  | unixify_name() and riscosify_name().
974  |
975  | This is done because only the UNIX=>RISCOS translation should
976  | ever affect the length of the leafname (ie. by truncating it to
977  | 10 chars if necessary).
978  |
979  | Note that the two functions are identical but for the truncation
980  | check so all that's really been done is that translate_name() now
981  | takes an extra argument: 'trunc' that controls whether truncation
982  | is applied, and riscosify and unixify just call translate_name().
983  */
translate_name(const char * path,int trunc)984 static char *translate_name(const char *path, int trunc)
985 {
986 	static char buf[260];
987 	char c, *p;
988 
989 	/* Copy 'path' into 'buf', swapping dots and slashes */
990 	p = buf;					/* Output position */
991 	do
992 	{
993 		c = *path++;
994 		if (c == '/')
995 			c = '.';
996 		else if (c == '.')
997 			c = '/';
998 		*p++ = c;
999 	}
1000 	while (c);					/* Terminator /is/ copied */
1001 
1002 	/*
1003 	   | When saving a game, the old game is renamed as
1004 	   | "SavedGame.old", the new one is saved as "SavedGame.new",
1005 	   | "SavedGame.old" is deleted, "SavedGame.new" is renamed
1006 	   | as "SavedGame". This will go wrong on a Filecore based filing
1007 	   | system if the saved game has a leafname > 8 chars.
1008 	 */
1009 
1010 	if ((p = strstr(buf, "/old")) == NULL)
1011 	{
1012 		p = strstr(buf, "/new");
1013 	}
1014 	if (!p)
1015 	{
1016 		ftype = 0xffd;
1017 	}
1018 	else
1019 	{
1020 		char *q = strrchr(buf, '.');
1021 		if (q)
1022 			if (p - q > 6)
1023 			{
1024 				memmove(q + 6, p, 5);
1025 			}
1026 		ftype = vfiletype;
1027 	}
1028 
1029 	/*
1030 	   | Hack: Do we need to truncate the leafname?
1031 	 */
1032 	if (trunc)
1033 	{
1034 		if (truncate_names())
1035 		{
1036 			char *a, *b;
1037 			/*
1038 			   | Assume that only the leafname needs attention
1039 			   | (this should be true for any variant)
1040 			 */
1041 			for (a = b = buf; *a; a++)
1042 				if (*a == '.')
1043 					b = a + 1;
1044 			/*
1045 			   | Now b points to the start of the leafname.
1046 			   | If the leafname is >10 chars, write over the 10th with a
1047 			   | terminator.
1048 			 */
1049 			if (strlen(b) > 10)
1050 			{
1051 				b[10] = 0;
1052 			};
1053 		}
1054 	}
1055 
1056 	return buf;
1057 }
1058 
1059 
riscosify_name(const char * path)1060 extern char *riscosify_name(const char *path)
1061 {
1062 	return translate_name(path, TRUE);
1063 }
1064 
unixify_name(const char * path)1065 static char *unixify_name(const char *path)
1066 {
1067 	return translate_name(path, FALSE);
1068 }
1069 
1070 
1071 /*--------------------------------------------------------------------------*/
1072 
1073 
1074 /* Open a file [as fopen()] but translate the requested filename first */
1075 
my_fopen(const char * f,const char * m)1076 FILE *my_fopen(const char *f, const char *m)
1077 {
1078 	FILE *fp;
1079 	char *n = riscosify_name(f);	/* translate for RO */
1080 
1081 	/* Try to open the file */
1082 	fp = fopen(n, m);
1083 
1084 	/* If it succeded and the file was opened for binary output
1085 	   | then set the type according to the 'ftype' hack.
1086 	   | NB: This will fail on some filing systems.
1087 	 */
1088 
1089 	if (fp && strstr(m, "wb"))
1090 	{
1091 		myFile_SetType(n, ftype);
1092 	}
1093 
1094 	return fp;
1095 }
1096 
1097 
1098 
1099 
1100 /* Close a file, a la fclose() */
1101 
my_fclose(FILE * fp)1102 void my_fclose(FILE *fp)
1103 {
1104 	/* Close the file, return 1 for an error, 0 otherwise */
1105 	fclose(fp);
1106 }
1107 
1108 
1109 /* Open/Create a file */
1110 
fd_make(cptr file,int mode)1111 int fd_make(cptr file, int mode)
1112 {
1113 	char *real_path;
1114 	int handle;
1115 
1116 	/* Translate the filename into a RISCOS one */
1117 	real_path = riscosify_name(file);
1118 
1119 	/* Try to OPENOUT the file (no path, error if dir or not found) */
1120 	handle = myFile_Open(real_path, 0x8f);
1121 
1122 	/* Check for failure */
1123 	if (!handle)
1124 	{
1125 		return -1;
1126 	}
1127 
1128 	/* Try to set the filetype according to the ftype hack */
1129 	myFile_SetType(real_path, ftype);
1130 
1131 	/* We keep track of up to 16 open files at any given time */
1132 	if (openfiles < 16)
1133 		filehandle[openfiles++] = handle;
1134 
1135 	return (handle);
1136 }
1137 
1138 
1139 /* Delete a file [as remove()] */
fd_kill(cptr file)1140 errr fd_kill(cptr file)
1141 {
1142 	return remove(riscosify_name(file)) ? 1 : 0;
1143 }
1144 
1145 
1146 /* Rename a file [as rename()] */
fd_move(cptr old,cptr new)1147 errr fd_move(cptr old, cptr new)
1148 {
1149 	char new_[260];
1150 	strcpy(new_, riscosify_name(new));
1151 	return rename(riscosify_name(old), new_) ? 1 : 0;
1152 }
1153 
1154 /* Open a file */
fd_open(cptr path,int flags)1155 int fd_open(cptr path, int flags)
1156 {
1157 	int handle = 0;
1158 	char *real_path = riscosify_name(path);
1159 
1160 	switch (flags & 0x0f)
1161 	{
1162 		case O_RDONLY:			/* Read only */
1163 			handle = myFile_Open(real_path, 0x4f);
1164 			break;
1165 		case O_WRONLY:			/* Write only */
1166 		case O_RDWR:			/* Read/Write */
1167 			handle = myFile_Open(real_path, 0xcf);
1168 	}
1169 
1170 	/* Check for failure */
1171 	if (!handle)
1172 	{
1173 		return (-1);
1174 	}
1175 
1176 	/* Keep track of upto 16 open files... */
1177 	if (openfiles < 16)
1178 		filehandle[openfiles++] = handle;
1179 
1180 	return (handle);
1181 }
1182 
1183 
1184 /* Close a file opened with fd_make or fd_open */
fd_close(int handle)1185 errr fd_close(int handle)
1186 {
1187 	int i;
1188 
1189 	if (handle <= 0)
1190 	{
1191 		return -1;
1192 	}							/* Illegal handle */
1193 
1194 	/* Try to close the file */
1195 	if (myFile_Close(handle))
1196 	{
1197 		return 1;
1198 	}
1199 
1200 	/* Mark the file as closed in our array of file handles */
1201 	openfiles--;
1202 	/* Find the entry in the array (if it exists) */
1203 	for (i = 0; i < 16; i++)
1204 		if (filehandle[i] == handle)
1205 		{
1206 			break;
1207 		}
1208 	/* Shuffle the remaining entries down */
1209 	for (; i < openfiles; i++)
1210 		filehandle[i] = filehandle[i + 1];
1211 
1212 	return 0;					/* Sucess */
1213 }
1214 
1215 
1216 
1217 /* Read some bytes from a file */
fd_read(int handle,char * buf,huge nbytes)1218 errr fd_read(int handle, char *buf, huge nbytes)
1219 {
1220 	int unread;
1221 
1222 	if (handle <= 0)
1223 	{
1224 		return -1;
1225 	}							/* Illegal handle */
1226 	unread = myFile_ReadBytes(handle, buf, (int)nbytes);
1227 
1228 	return unread ? 1 : 0;
1229 }
1230 
1231 
1232 /* Write some bytes to a file */
fd_write(int handle,const char * buf,huge nbytes)1233 errr fd_write(int handle, const char *buf, huge nbytes)
1234 {
1235 	int unwritten;
1236 
1237 	if (handle <= 0)
1238 	{
1239 		return -1;
1240 	}							/* Illegal handle */
1241 	unwritten = myFile_WriteBytes(handle, (const void *)buf, (int)nbytes);
1242 
1243 	return unwritten ? 1 : 0;
1244 }
1245 
1246 
1247 /* Seek in a file */
fd_seek(int handle,huge offset)1248 errr fd_seek(int handle, huge offset)
1249 {
1250 	os_error *e;
1251 
1252 	if (handle <= 0)
1253 	{
1254 		return -1;
1255 	}							/* Illegal handle */
1256 	e = myFile_Seek(handle, (int)offset);
1257 
1258 	return e ? 1 : 0;
1259 }
1260 
1261 
1262 /* RISC OS provides no file locking facilities, so: */
fd_lock(int handle,int what)1263 errr fd_lock(int handle, int what)
1264 {
1265 	return 0;
1266 }
1267 
1268 
1269 /* Get a temporary filename */
path_temp(char * buf,size_t max)1270 errr path_temp(char *buf, size_t max)
1271 {
1272 
1273 	/*
1274 	   | New in 1.25 - use the scrap path we decided on earlier, or
1275 	   | fall back on tmpnam() if that fails for some reason.
1276 	 */
1277 	if (*scrap_path)
1278 	{
1279 		time_t t;
1280 		int m;
1281 		char tmp[512];
1282 
1283 		time(&t);
1284 		for (m = 0; m < 80; m++)
1285 		{
1286 			sprintf(tmp, "%s0x%08x", scrap_path, (int)t + m);
1287 			if (myFile_Size(tmp) == -1)
1288 			{
1289 				break;
1290 			}
1291 		}
1292 
1293 		if (m < 80)
1294 		{
1295 			strncpy(buf, unixify_name(tmp), max);
1296 			return 0;
1297 		}
1298 	}
1299 
1300 	strncpy(buf, unixify_name(tmpnam(NULL)), max);
1301 	return 0;
1302 }
1303 
1304 
1305 
1306 /*
1307  * Create a new path by appending a file (or directory) to a path
1308  *
1309  * This requires no special processing on simple machines, except
1310  * for verifying the size of the filename, but note the ability to
1311  * bypass the given "path" with certain special file-names.
1312  *
1313  * Note that the "file" may actually be a "sub-path", including
1314  * a path and a file.
1315  *
1316  * Note that this function yields a path which must be "parsed"
1317  * using the "parse" function above.
1318  */
path_build(char * buf,int max,cptr path,cptr file)1319 void path_build(char *buf, int max, cptr path, cptr file)
1320 {
1321 	/* Special file */
1322 	if (file[0] == '~')
1323 	{
1324 		/* Use the file itself */
1325 		strnfmt(buf, max, "%s", file);
1326 	}
1327 
1328 	/* Absolute file, on "normal" systems */
1329 	else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
1330 	{
1331 		/* Use the file itself */
1332 		strnfmt(buf, max, "%s", file);
1333 	}
1334 
1335 	/* No path given */
1336 	else if (!path[0])
1337 	{
1338 		/* Use the file itself */
1339 		strnfmt(buf, max, "%s", file);
1340 	}
1341 
1342 	/* Path and File */
1343 	else
1344 	{
1345 		/* Build the new path */
1346 		strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
1347 	}
1348 }
1349 
1350 
1351 
1352 /*--------------------------------------------------------------------------*/
1353 
1354 
1355 
1356 
1357 
1358 /*--------------------------------------------------------------------------*/
1359 /* Font Functions															*/
1360 /*--------------------------------------------------------------------------*/
1361 
1362 /*
1363  | Cache the system font as fonts[0]
1364  | Returns 1 for sucess or 0 for failure.
1365  | NB: The n_bpp data is *not* cached, just the 1bpp data and font info.
1366  | Also, the usage is never affected.
1367  */
cache_system_font(void)1368 static int cache_system_font(void)
1369 {
1370 	ZapFont *sys = SYSTEM_FONT;
1371 	ZapRedrawBlock zrb;
1372 	char work_area[16];
1373 	int i;
1374 
1375 	/* Cache the system font (as fonts[0]) */
1376 	if (sys->bpp_1)
1377 	{
1378 		f_free(sys->bpp_1);
1379 		sys->bpp_1 = 0;
1380 	}
1381 	sys->bpp_1 = f_malloc(8 * 256);	/* 2K */
1382 	if (!sys->bpp_1)
1383 	{
1384 		return 0;
1385 	}
1386 
1387 	/* Mung so that undefined characters show up as inverted ?s */
1388 	work_area[3] = '?';
1389 	SWI(2, 0, SWI_OS_Word, 10, work_area + 3);
1390 	for (i = 4; i < 12; i++)
1391 		work_area[i] ^= 255;	/* invert colours */
1392 	SWI(4, 0, SWI_ZapRedraw_ReverseBitmaps, 0, work_area + 4, work_area + 4, 8);
1393 	for (i = 0; i < 0x20; i++)
1394 		memcpy(((char *)sys->bpp_1) + i * 8, work_area + 4, 8);
1395 
1396 	/* Read the system font */
1397 	zrb.r_workarea = work_area;
1398 	SWI(2, 0, SWI_ZapRedraw_ReadSystemChars, sys->bpp_1, &zrb);
1399 
1400 	/* Set up some little bits of info */
1401 	sys->name = (char *) "<System>";
1402 	sys->w = sys->h = 8;
1403 	sys->f = 0;
1404 	sys->l = 255;
1405 
1406 	return 1;
1407 }
1408 
1409 
1410 
1411 /*
1412  | Prepare the font system
1413  */
initialise_fonts(void)1414 static void initialise_fonts(void)
1415 {
1416 	/* Initialise the array */
1417 	memset(fonts, 0, sizeof(fonts));	/* Clear to zeroes */
1418 
1419 	/* Cache the system font */
1420 	cache_system_font();
1421 	fonts[0].usage = 0;			/* No users */
1422 }
1423 
1424 
1425 #ifndef FULLSCREEN_ONLY
1426 /*
1427  | Find a font (by name) in the array.
1428  | Returns 0 if the font isn't loaded, or a ZapFont* for it if it is.
1429  */
find_font_by_name(char * name)1430 static ZapFont *find_font_by_name(char *name)
1431 {
1432 	int i;
1433 	for (i = 0; i <= MAX_TERM_DATA; i++)
1434 		if (fonts[i].name)
1435 			if (!strcmp(fonts[i].name, name))
1436 				return &(fonts[i]);
1437 	return NULL;
1438 }
1439 
1440 /*
1441  | Find a free slot in the fonts array
1442  */
find_free_font(void)1443 static ZapFont *find_free_font(void)
1444 {
1445 	int i;
1446 	for (i = 1; i <= MAX_TERM_DATA; i++)
1447 		if (!fonts[i].name)
1448 		{
1449 			return &(fonts[i]);
1450 		}
1451 	return NULL;
1452 }
1453 
1454 
1455 
1456 /*
1457  | Load a font from disc and set up the header info, etc.
1458  | NB: doesn't cache the nbpp data, just the 1bpp data.
1459  | (Sets usage to 1)
1460  | Returns NULL if failed.
1461  */
load_font(char * name,ZapFont * f)1462 static ZapFont *load_font(char *name, ZapFont *f)
1463 {
1464 	int handle, extent;
1465 	char path[260];
1466 	struct
1467 	{
1468 		char id[8];
1469 		int w, h, f, l, r1, r2;
1470 	}
1471 	header;
1472 	char *font_path;
1473 	char *t;
1474 	char *real_name = name;	/* need to preserve this */
1475 
1476 	/*
1477 	   | 1.10 - the first element of the name determines the path to load
1478 	   | the font from.
1479 	 */
1480 
1481 	/* The font paths start <RISCOS_VARIANT>$ */
1482 	t = path + sprintf(path, "%s$", RISCOS_VARIANT);
1483 
1484 	/* Copy the path specifier and move 'name' past it */
1485 	for (; *name != '.'; *t++ = *name++) ;
1486 
1487 	/* After this, the name now points to the font name proper */
1488 	name++;
1489 
1490 	/* Append the end of the path name */
1491 	strcpy(t, "$FontPath");
1492 
1493 	/* Get the path setting */
1494 	font_path = getenv(path);
1495 	if (!font_path || !*font_path)
1496 		strcpy(path, "null:$.");
1497 	else
1498 	{
1499 		strcpy(path, font_path);
1500 		for (t = path; *t > ' '; t++)
1501 			;
1502 		if (t[-1] != '.' && t[-1] != ':')
1503 		{
1504 			*t++ = '.';
1505 		}
1506 		*t = 0;
1507 	}
1508 	strcat(path, name);
1509 
1510 
1511 	/* Open the file */
1512 	handle = myFile_Open(path, 0x4f);
1513 	if (!handle)
1514 	{
1515 		return NULL;
1516 	}
1517 
1518 	/* Read the header */
1519 	if (myFile_ReadBytes(handle, &header, sizeof(header)))
1520 	{
1521 		myFile_Close(handle);
1522 		return NULL;
1523 	}
1524 
1525 	/* Check that it's a zapfont */
1526 	if (strncmp(header.id, "ZapFont\r", 8))
1527 	{
1528 		myFile_Close(handle);
1529 		return NULL;
1530 	}
1531 
1532 	/* Calculate the size of the 1bpp data */
1533 	extent = myFile_Extent(handle) - sizeof(header);
1534 
1535 	/* Allocate the storage for the 1bpp data */
1536 	f->bpp_1 = f_malloc(extent);
1537 	if (!f->bpp_1)
1538 	{
1539 		myFile_Close(handle);
1540 		return NULL;
1541 	}
1542 
1543 	/* Load the 1bpp data */
1544 	if (myFile_ReadBytes(handle, f->bpp_1, extent))
1545 	{
1546 		f_free(f->bpp_1);
1547 		f->bpp_1 = 0;
1548 		myFile_Close(handle);
1549 		return NULL;
1550 	}
1551 
1552 	/* Close the file and set the header, etc. */
1553 	myFile_Close(handle);
1554 	f->name = f_malloc(strlen(real_name) + 1);
1555 	if (!f->name)
1556 	{
1557 		f_free(f->bpp_1);
1558 		f->bpp_1 = 0;
1559 		return NULL;
1560 	}
1561 
1562 	strcpy(f->name, real_name);
1563 	f->w = header.w;
1564 	f->h = header.h;
1565 	f->f = header.f;
1566 	f->l = header.l;
1567 	f->usage = 1;
1568 
1569 	return f;
1570 }
1571 
1572 
1573 
1574 
1575 /*
1576  | Cache a font at a suitable number of bpp for the current mode
1577  | Returns 0 for failure, 1 for sucess.
1578  | If the call fails then the font's bpp_n entry will be NULL.
1579  */
cache_font_for_mode(ZapFont * f)1580 static int cache_font_for_mode(ZapFont *f)
1581 {
1582 	ZapRedrawBlock b;
1583 	char work_area[128];
1584 	int size;
1585 
1586 	if (!f)
1587 	{
1588 		return 0;
1589 	}
1590 	if (!f->bpp_1)
1591 	{
1592 		return 0;
1593 	}
1594 
1595 	b.r_workarea = work_area;
1596 	SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
1597 
1598 	b.r_workarea = work_area;	/* Paranoia */
1599 	b.r_charh = f->h;
1600 	b.r_charw = f->w;
1601 	SWI(4, 4, SWI_ZapRedraw_CachedCharSize, b.r_bpp, 0, f->w, f->h,
1602 		NULL, NULL, &(b.r_cbpl), &(b.r_cbpc));
1603 
1604 	size = 256 * b.r_cbpc;
1605 	if (f->bpp_n)
1606 	{
1607 		f_free(f->bpp_n);
1608 		f->bpp_n = NULL;
1609 	}
1610 	f->bpp_n = f_malloc(size);
1611 	if (!f->bpp_n)
1612 	{
1613 		return 0;
1614 	}
1615 
1616 	b.r_workarea = work_area;	/* Paranoia */
1617 	b.r_caddr = f->bpp_n;
1618 	SWI(5, 0, SWI_ZapRedraw_ConvertBitmap, 0, &b, 0, 255, f->bpp_1);
1619 
1620 	return 1;
1621 }
1622 
1623 
1624 
1625 /*
1626  | Stop using a font.
1627  | If the font's usage drops to zero then the font data is purged.
1628  */
lose_font(ZapFont * f)1629 static void lose_font(ZapFont *f)
1630 {
1631 	if (--f->usage)
1632 	{
1633 		/*debug("Losing font %s (still cached)",f->name); */
1634 		return;
1635 	}
1636 	/*debug("Losing font %s (no longer in use)",f->name); */
1637 	f_free(f->name);
1638 	f_free(f->bpp_1);
1639 	if (f->bpp_n)
1640 	{
1641 		f_free(f->bpp_n);
1642 	}
1643 	memset(f, 0, sizeof(ZapFont));
1644 }
1645 
1646 
1647 /*
1648  | Get a font.
1649  */
find_font(char * name)1650 static ZapFont *find_font(char *name)
1651 {
1652 	ZapFont *f;
1653 
1654 	/* Check to see if it's already loaded */
1655 	f = find_font_by_name(name);
1656 	if (f)
1657 	{
1658 		/*debug("Find font %s (already cached)",name); */
1659 		f->usage++;
1660 		if (f == SYSTEM_FONT)
1661 		{
1662 			if (!cache_system_font())
1663 				core("Failed to cache system font!");
1664 			if (!cache_font_for_mode(SYSTEM_FONT))
1665 				core("Failed to cache system font!");
1666 		}
1667 		return f;
1668 	}
1669 
1670 	/* Ok, now check to see if there's a free slot for it */
1671 	f = find_free_font();
1672 	if (!f)
1673 	{
1674 		return NULL;
1675 	}							/* Oh dear :( */
1676 
1677 	/* Load the font */
1678 	/*debug("Find font %s (loading)",name); */
1679 	f = load_font(name, f);
1680 	if (f)
1681 	{
1682 		if (!cache_font_for_mode(f))
1683 			return NULL;
1684 		return f;
1685 	}
1686 	return NULL;
1687 }
1688 
1689 
1690 
1691 
1692 /*
1693  | Cache the n_bpp data for all the active fonts (including system)
1694  */
cache_fonts(void)1695 static void cache_fonts(void)
1696 {
1697 	int i;
1698 	for (i = 0; i <= MAX_TERM_DATA; i++)
1699 		if (fonts[i].name)
1700 			if (!cache_font_for_mode(&(fonts[i])))
1701 				core("Failed to (re)cache font tables");
1702 }
1703 
1704 
1705 
1706 
1707 
1708 
1709 typedef struct
1710 {
1711 	int load, exec, size, attr, type;
1712 	char name[4];	/* Actual size is unknown */
1713 }
1714 osgbpb10_block;
1715 
1716 
leafname(char * path)1717 static char *leafname(char *path)
1718 {
1719 	char *s = path + strlen(path);
1720 	while (--s > path)
1721 		if (*s == '.' || *s == ':')
1722 		{
1723 			return s + 1;
1724 		}
1725 	return path;
1726 }
1727 
1728 /*
1729  | NB: This function is recursive.
1730  */
make_zfont_menu(char * dir)1731 static menu_ptr make_zfont_menu(char *dir)
1732 {
1733 	int entries, entry;
1734 	int read, offset;
1735 	unsigned int max_width;
1736 	menu_ptr m;
1737 	menu_item *mi;
1738 	char *temp;
1739 	osgbpb10_block *item_info;
1740 	char buffer[1024];	/* 1Kb buffer */
1741 
1742 	/* Count the entries in the directory */
1743 	entries = read = offset = 0;
1744 	while (offset != -1)
1745 	{
1746 		if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0,
1747 				NULL, NULL, NULL, &read, &offset))
1748 		{
1749 			offset = -1;
1750 			read = 0;
1751 		}
1752 		entries += read;
1753 	}
1754 
1755 	if (!entries)
1756 	{
1757 		return NULL;
1758 	}
1759 
1760 	/* Allocate a big enough area of storage for the number of entries */
1761 	m = f_malloc(sizeof(menu_block) + entries * sizeof(menu_item));
1762 	if (!m)
1763 	{
1764 		return NULL;
1765 	}
1766 	memset(m, 0, sizeof(menu_block) + entries * sizeof(menu_item));
1767 
1768 	/* Set up the menu header */
1769 	strncpy(m->title, leafname(dir), 12);
1770 	m->titlefore = 7;
1771 	m->titleback = 2;
1772 	m->workfore = 7;
1773 	m->workback = 0;
1774 	m->height = 44;
1775 	m->gap = 0;
1776 	mi = (menu_item *) (((int)m) + sizeof(menu_block));
1777 	max_width = strlen(m->title);
1778 
1779 	entry = 0;
1780 
1781 	/* Read the entries */
1782 	read = offset = 0;
1783 	while (offset != -1)
1784 	{
1785 		if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0,
1786 				NULL, NULL, NULL, &read, &offset))
1787 		{
1788 			offset = -1;
1789 			read = 0;
1790 			/*free(m);return NULL; */
1791 		}
1792 
1793 		item_info = (osgbpb10_block *) buffer;
1794 
1795 		/* Create a menu item for each entry read (if it fits) */
1796 		while (read-- > 0)
1797 		{
1798 			switch (item_info->type)
1799 			{
1800 				case 1:		/* File */
1801 					if ((item_info->load & 0xffffff00) == 0xfffffd00)
1802 					{
1803 						/* Data file */
1804 						mi[entry].submenu.value = -1;
1805 						mi[entry].iconflags.data.text = 1;
1806 						mi[entry].iconflags.data.filled = 1;
1807 						mi[entry].iconflags.data.foreground = 7;
1808 						mi[entry].iconflags.data.background = 0;
1809 						strncpy(mi[entry].icondata.text, item_info->name, 12);
1810 						if (strlen(mi[entry].icondata.text) > max_width)
1811 							max_width = strlen(mi[entry].icondata.text);
1812 						entry++;
1813 					}
1814 					break;
1815 				case 2:		/* Directory */
1816 				case 3:		/* Image */
1817 				{
1818 					menu_ptr sub;
1819 					char new_path[260];
1820 					if (strchr(":.", dir[strlen(dir) - 1]))
1821 						sprintf(new_path, "%s%s", dir, item_info->name);
1822 					else
1823 						sprintf(new_path, "%s.%s", dir, item_info->name);
1824 					sub = make_zfont_menu(new_path);
1825 					if (sub)
1826 					{
1827 						/* Add the submenu */
1828 						mi[entry].submenu.menu = sub;
1829 						mi[entry].iconflags.data.text = 1;
1830 						mi[entry].iconflags.data.filled = 1;
1831 						mi[entry].iconflags.data.foreground = 7;
1832 						mi[entry].iconflags.data.background = 0;
1833 						strncpy(mi[entry].icondata.text, item_info->name, 12);
1834 						if (strlen(mi[entry].icondata.text) > max_width)
1835 							max_width = strlen(mi[entry].icondata.text);
1836 						entry++;
1837 					}
1838 				}
1839 					break;
1840 			}
1841 			temp = ((char *)item_info) + 20;
1842 			while (*temp++) ;
1843 			item_info = (osgbpb10_block *) ((((int)temp) + 3) & ~3);
1844 		}
1845 	}
1846 
1847 	if (entry)
1848 	{
1849 		m->width = (max_width + 2) * 16;
1850 		mi[entry - 1].menuflags.data.last = 1;
1851 		/*
1852 		   | We could possibly realloc() the storage to fit the
1853 		   | actual no. of entries read, but this is probably more
1854 		   | trouble than it's worth.
1855 		 */
1856 	}
1857 	else
1858 	{
1859 		/*
1860 		   | No point in returning an empty menu.
1861 		 */
1862 		f_free(m);
1863 		m = NULL;
1864 	}
1865 
1866 	return m;
1867 }
1868 
1869 #endif /* FULLSCREEN_ONLY */
1870 
1871 
1872 
1873 
1874 /*--------------------------------------------------------------------------*/
1875 
1876 /*
1877  | Initialise the palette stuff
1878  */
initialise_palette(void)1879 static void initialise_palette(void)
1880 {
1881 	memset(a_palette, 0, sizeof(a_palette));
1882 	memset(palette, 0, sizeof(palette));
1883 	memset(zpalette, 0, sizeof(zpalette));
1884 }
1885 
1886 
1887 
1888 #ifndef FULLSCREEN_ONLY
1889 
1890 /*
1891  | Cache the ZapRedraw palette
1892  */
cache_palette(void)1893 static void cache_palette(void)
1894 {
1895 	static ZapRedrawBlock b;
1896 	char workspace[128];
1897 	int i;
1898 
1899 	static double old_gamma = -1.0;
1900 
1901 	/* Idiocy check: */
1902 	if (gamma < 0.01)
1903 	{
1904 		plog("Internal error: Attempt to apply zero gamma - recovering...");
1905 		gamma = 1.00;
1906 	}
1907 
1908 	if (gamma != old_gamma)
1909 	{
1910 		memset(a_palette, 0, sizeof(a_palette));
1911 		old_gamma = gamma;
1912 	}
1913 
1914 	/* Go through the palette updating any changed values */
1915 	for (i = 0; i < 256; i++)
1916 	{
1917 		if (COLOUR_CHANGED(i))
1918 		{
1919 			int r, g, b;
1920 			r = (int)(255.0 *
1921 					  pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
1922 			g = (int)(255.0 *
1923 					  pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
1924 			b = (int)(255.0 *
1925 					  pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
1926 			palette[i] = (b << 24) | (g << 16) | (r << 8);
1927 			a_palette[i][1] = angband_color_table[i][1];
1928 			a_palette[i][2] = angband_color_table[i][2];
1929 			a_palette[i][3] = angband_color_table[i][3];
1930 		}
1931 	}
1932 
1933 	cursor_rgb = palette[CURSOR_COLOUR];
1934 
1935 	/* Cache the ZapRedraw palette for it */
1936 	b.r_workarea = workspace;
1937 	if (b.r_mode != screen_mode)
1938 		SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
1939 	SWI(5, 0, SWI_ZapRedraw_CreatePalette, 2, &b, palette, zpalette, 256);
1940 }
1941 
1942 
1943 
1944 
1945 /*--------------------------------------------------------------------------*/
1946 
1947 /*
1948  | Functions for dealing with the SaveBox
1949  */
1950 
1951 /*
1952  | Create the window and claim various handlers for it
1953  */
init_save_window(void)1954 static void init_save_window(void)
1955 {
1956 	/* Create the window */
1957 	save_box = Window_Create("save", template_TITLEMIN);
1958 
1959 	/* Set the file icon */
1960 	Icon_printf(save_box, SAVE_ICON, "file_%03x", vfiletype);
1961 
1962 }
1963 #endif /* FULLSCREEN_ONLY */
1964 
1965 /*
1966  | Hack: can't use Str.h without defining HAS_STRICMP.  Rather than
1967  | require that the header files are altered we simply provide our
1968  | own strnicmp() function.
1969  */
my_strnicmp(const char * a,const char * b,int n)1970 static int my_strnicmp(const char *a, const char *b, int n)
1971 {
1972 	int i;
1973 
1974 	n--;
1975 
1976 	for (i = 0; i <= n; i++)
1977 	{
1978 		if (tolower((unsigned char)a[i]) != tolower((unsigned char)b[i]))
1979 			return tolower((unsigned char)a[i]) - tolower((unsigned char)b[i]);
1980 
1981 		if (a[i] == '\0')
1982 			break;
1983 	}
1984 
1985 	return 0;
1986 }
1987 
1988 #ifndef FULLSCREEN_ONLY
1989 /*
1990  | This is the handler called when a 'save' occurrs.
1991  | All it does is to update the game's own savefile setting and
1992  | then (if possible) save the character.
1993  */
SaveHnd_FileSave(char * filename,void * ref)1994 static BOOL SaveHnd_FileSave(char *filename, void *ref)
1995 {
1996 	char old_savefile[1024];
1997 
1998 	/* Hack: refuse to save if the character is dead */
1999 	if (PDEADCHK)
2000 	{
2001 		Msgs_Report(0, "err.cheat");
2002 		return FALSE;
2003 	}
2004 
2005 	/* Hack: disallow saves to <Wimp$Scrap>* */
2006 	if (!my_strnicmp("<wimp$scrap>", filename, 12))
2007 	{
2008 		Msgs_Report(0, "err.scrap");
2009 		return FALSE;
2010 	}
2011 
2012 	/* Preserve the old path, in case something goes wrong... */
2013 	strcpy(old_savefile, savefile);
2014 
2015 	/* Set the new path */
2016 	strcpy(savefile, unixify_name(filename));
2017 
2018 	/* Try a save (if sensible) */
2019 	if (game_in_progress && character_generated)
2020 	{
2021 		if (!save_player())
2022 		{
2023 			Msgs_Report(0, "err.save", filename);
2024 			strcpy(savefile, old_savefile);
2025 			return FALSE;		/* => failure */
2026 		}
2027 	}
2028 
2029 	/* Set the pathname icon */
2030 	Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
2031 
2032 	/* Kill the menu */
2033 	Wimp_CreateMenu((menu_block *) - 1, -1, -1);
2034 
2035 	return TRUE;				/* => Success */
2036 }
2037 
2038 
2039 
2040 /*
2041  | Init the handlers for the savebox (eg. as a result of a menuwarning
2042  | being received for the savebox)
2043  */
2044 
init_savehandlers(void)2045 static void init_savehandlers(void)
2046 {
2047 	if (saveblk)
2048 	{
2049 		Save_ReleaseSaveHandlers(saveblk);
2050 		saveblk = 0;
2051 	}
2052 
2053 	saveblk = Save_InitSaveWindowHandler(save_box,	/* Window handle */
2054 										 TRUE,	/* it's part of a menu */
2055 										 FALSE,	/* not a window */
2056 										 FALSE,	/* Don't auto release the handlers */
2057 										 SAVE_ICON,	/* The file icon */
2058 										 SAVE_OK,	/* The OK icon */
2059 										 SAVE_CANCEL,	/* The cancel icon */
2060 										 SAVE_PATH,	/* The pathname icon */
2061 										 SaveHnd_FileSave,	/* Handler to "save the file" */
2062 										 NULL,	/* No RAM transfer support */
2063 										 NULL,	/* No 'result handler' */
2064 										 100 << 10,	/* Est. size (irelevant anyway) */
2065 										 vfiletype,	/* filetype (irelevant) */
2066 										 NULL	/* ref */
2067 		);
2068 }
2069 
2070 
2071 /*
2072  | Handle a MenuWarning message for the savebox
2073  */
Hnd_SaveWarning(event_pollblock * pb,void * ref)2074 static BOOL Hnd_SaveWarning(event_pollblock * pb, void *ref)
2075 {
2076 	os_error *e;
2077 
2078 	init_savehandlers();
2079 
2080 	/* Set the pathname */
2081 	Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
2082 
2083 	/* Open the submenu */
2084 	e = Wimp_CreateSubMenu((menu_block *) save_box,
2085 						   pb->data.message.data.menuwarn.openpos.x,
2086 						   pb->data.message.data.menuwarn.openpos.y);
2087 
2088 	if (e)
2089 	{
2090 		Msgs_ReportFatal(0, "err.swi", __LINE__, e->errmess);
2091 	}
2092 
2093 	return TRUE;
2094 }
2095 
2096 
2097 /*--------------------------------------------------------------------------*/
2098 
2099 
2100 /*
2101  | Initialise the r_data array
2102  | Mainly we just set up the line offset pointers and make sure that the
2103  | lines themselves are 'safe' by writing end-of-line codes to them.
2104  */
initialise_r_data(void)2105 static void initialise_r_data(void)
2106 {
2107 	int *lo = (int *)r_data;
2108 	char *ld;
2109 	int j;
2110 	for (j = 0; j < 24; j++)
2111 	{
2112 		lo[j] = 25 * 4 + (80 * 5 + 4) * j;	/* Offset of line */
2113 		ld = r_data + lo[j];
2114 		*ld++ = 0;				/* 0,2 ==     */
2115 		*ld = 2;				/* end of line */
2116 	}
2117 	lo[j] = 0;					/* Terminate line index */
2118 }
2119 
2120 
2121 
2122 /*
2123  | Create the r_data array for a term
2124  | This is typically quite fast (1ms or so on a RPC700)
2125  | so we don't bother caching r_data for each term or using the
2126  | 'frosh' concept.
2127  */
make_r_data(term_data * t)2128 static void make_r_data(term_data *t)
2129 {
2130 	char **c = t->t.old->c;	/* char array [24][80] */
2131 	byte **a = t->t.old->a;	/* attr array [24][80] */
2132 	char *o;
2133 	int i, j, cf;
2134 
2135 /* New code: */
2136 
2137 	o = r_data + 25 * 4;		/* First byte of r_data after line index */
2138 
2139 	if (force_mono)
2140 	{
2141 		for (j = 0; j < 24; j++)
2142 		{
2143 			/* Set up the line offset entry */
2144 			((int *)r_data)[j] = o - r_data;
2145 
2146 			for (i = 0; i < 80; i++)
2147 				*o++ = a[j][i] != TERM_DARK ? c[j][i] : ' ';
2148 			/* 0,2 => end of line */
2149 			*o++ = 0;
2150 			*o++ = 2;
2151 		}
2152 	}
2153 	else
2154 	{
2155 		for (j = 0; j < 24; j++)
2156 		{
2157 			/* Set up the line offset entry */
2158 			((int *)r_data)[j] = o - r_data;
2159 
2160 			/* Each line starts in white */
2161 			cf = TERM_WHITE;
2162 
2163 			for (i = 0; i < 80; i++)
2164 			{
2165 				if (a[j][i] != cf)
2166 				{
2167 					/* 0,6 => change FG */
2168 					*o++ = 0;
2169 					*o++ = 6;
2170 					cf = *o++ = a[j][i];
2171 				}
2172 				*o++ = c[j][i];
2173 			}
2174 			/* 0,2 => end of line */
2175 			*o++ = 0;
2176 			*o++ = 2;
2177 		}
2178 	}
2179 }
2180 
2181 
2182 /*
2183  | Set up 'zrb' for the current screen mode.
2184  */
set_up_zrb_for_mode(void)2185 static void set_up_zrb_for_mode(void)
2186 {
2187 	static char work_area[4096];
2188 	zrb.r_workarea = work_area;
2189 	zrb.r_palette = zpalette;
2190 	zrb.r_linesp = 0;
2191 	zrb.r_for = TERM_WHITE;
2192 	zrb.r_bac = 0;
2193 	zrb.r_data = r_data;
2194 	SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &zrb);
2195 }
2196 
2197 
2198 
2199 /*
2200  | Set up the ZapRedrawBlock ready to redraw term 't'
2201  | (caches the r_data as part of the process)
2202  */
set_up_zrb(term_data * t)2203 static void set_up_zrb(term_data *t)
2204 {
2205 	int fw, fh;
2206 
2207 	zrb.r_flags.value = 0;
2208 
2209 	/* Set font info up */
2210 	fw = t->font->w;
2211 	fh = t->font->h;
2212 	SWI(4, 4, SWI_ZapRedraw_CachedCharSize, zrb.r_bpp, 0, fw, fh,
2213 		NULL, NULL, &(zrb.r_cbpl), &(zrb.r_cbpc));
2214 	zrb.r_caddr = (void *)(((int)t->font->bpp_n) - (t->font->f * zrb.r_cbpc));
2215 
2216 	zrb.r_charw = fw;			/* Character size in pixels */
2217 	zrb.r_charh = fh;
2218 
2219 	if (t->font == SYSTEM_FONT)
2220 		zrb.r_flags.bits.double_height = screen_eig.y == 1;
2221 	else
2222 		zrb.r_flags.bits.double_height = 0;
2223 
2224 	make_r_data(t);				/* Cache the r_data */
2225 }
2226 
2227 
2228 
2229 
2230 
RO_redraw_window(window_redrawblock * rb,BOOL * more,term_data * t)2231 static void RO_redraw_window(window_redrawblock * rb, BOOL *more, term_data *t)
2232 {
2233 	int cx, cy, cw, ch;
2234 
2235 	/* set GCOL for cursor colour */
2236 	if (t->cursor.visible)
2237 	{
2238 		cw = zrb.r_charw << screen_eig.x;
2239 		ch = -(zrb.r_charh << screen_eig.y);
2240 		if (zrb.r_flags.bits.double_height)
2241 		{
2242 			ch *= 2;
2243 		}
2244 		cx = t->cursor.pos.x * cw;
2245 		cy = t->cursor.pos.y * ch;
2246 		cx += (rb->rect.min.x - rb->scroll.x);
2247 		cy += (rb->rect.max.y - rb->scroll.y);
2248 		cw -= (1 << screen_eig.x);
2249 		ch += (1 << screen_eig.y);
2250 		cy -= (1 << screen_eig.y);
2251 	}
2252 
2253 	while (*more)
2254 	{
2255 		SWI(2, 0, SWI_ZapRedraw_GetRectangle, rb, &zrb);
2256 		SWI(2, 0, SWI_ZapRedraw_RedrawArea, NULL, &zrb);
2257 		if (t->cursor.visible)
2258 		{
2259 			ColourTrans_SetGCOL(cursor_rgb, 0, 0);
2260 			GFX_Move(cx, cy);
2261 			GFX_DrawBy(cw, 0);
2262 			GFX_DrawBy(0, ch);
2263 			GFX_DrawBy(-cw, 0);
2264 			GFX_DrawBy(0, -ch);
2265 		}
2266 		Wimp_GetRectangle(rb, more);
2267 	}
2268 }
2269 
2270 
2271 
2272 
2273 
Hnd_Redraw(event_pollblock * pb,void * ref)2274 static BOOL Hnd_Redraw(event_pollblock * pb, void *ref)
2275 {
2276 	term_data *t = (term_data *)ref;
2277 	window_redrawblock rb;
2278 	BOOL more;
2279 
2280 	rb.window = t->w;
2281 	Wimp_RedrawWindow(&rb, &more);
2282 
2283 	set_up_zrb(t);
2284 
2285 	RO_redraw_window(&rb, &more, t);
2286 
2287 	return TRUE;
2288 }
2289 
2290 
2291 
2292 
2293 
refresh_window(term_data * t)2294 static void refresh_window(term_data *t)
2295 {
2296 	window_redrawblock rb;
2297 	BOOL more;
2298 	int fw, fh;
2299 
2300 	if ((t->changed_box.min.x >= t->changed_box.max.x) ||
2301 		(t->changed_box.min.y >= t->changed_box.max.y))
2302 		return;
2303 
2304 	set_up_zrb(t);
2305 
2306 	fw = zrb.r_charw << screen_eig.x;
2307 	fh = -(zrb.r_charh << screen_eig.y);
2308 	if (zrb.r_flags.bits.double_height)
2309 	{
2310 		fh *= 2;
2311 	}
2312 
2313 	rb.window = t->w;
2314 	rb.rect.min.x = fw * t->changed_box.min.x;
2315 	rb.rect.max.x = fw * t->changed_box.max.x;
2316 
2317 	rb.rect.max.y = fh * t->changed_box.min.y;
2318 	rb.rect.min.y = fh * t->changed_box.max.y;
2319 
2320 	Wimp_UpdateWindow(&rb, &more);
2321 	RO_redraw_window(&rb, &more, t);
2322 
2323 	t->changed_box.min.x = t->changed_box.min.y = 255;
2324 	t->changed_box.max.x = t->changed_box.max.y = 0;
2325 }
2326 
2327 
2328 
refresh_windows(void)2329 static void refresh_windows(void)
2330 {
2331 	int i;
2332 	window_info info;
2333 	for (i = 0; i < MAX_TERM_DATA; i++)
2334 	{
2335 		info.window = data[i].w;
2336 		Wimp_GetWindowInfo(&info);
2337 		if (info.block.flags.data.open)
2338 			refresh_window(&(data[i]));
2339 	}
2340 }
2341 
2342 
2343 /*
2344  | Set the size of a window.
2345  | If the window grows but has no scroll bars then it is re-sized to
2346  | the new extent.  If it shrinks then it is resized regardless.
2347  |
2348  | If the window isn't open then it is opened behind the backwindow,
2349  | resized and then closed again...  I /did/ have a reason for doing this
2350  | rather than simply recreating the window at the new size, but for the
2351  | life of me I can't remember what it was...
2352  */
set_window_size(window_handle w,int width,int height)2353 static void set_window_size(window_handle w, int width, int height)
2354 {
2355 	window_state ws;
2356 	int reclose;
2357 /*
2358  *  int cw,ch;
2359  */
2360 
2361 	Wimp_GetWindowState(w, &ws);
2362 	Window_SetExtent(w, 0, -height, width, 0);
2363 
2364 	reclose = !ws.flags.data.open;
2365 	if (!(ws.flags.value & (0xf << 27)))
2366 	{
2367 		if (reclose)
2368 		{
2369 			ws.openblock.behind = -3;
2370 			Wimp_OpenWindow(&(ws.openblock));
2371 		}
2372 		/* Removed - caused "pixel creep" :)
2373 		 *  cw = ws.openblock.screenrect.max.x;
2374 		 *  ch = ws.openblock.screenrect.max.y;
2375 		 *  cw -= ws.openblock.screenrect.min.x;
2376 		 *  ch -= ws.openblock.screenrect.min.y;
2377 		 *  ws.openblock.screenrect.min.x -= ((width-cw)/2)-(1<<screen_eig.x);
2378 		 *  ws.openblock.screenrect.min.y -= ((height-ch)/2)-(1<<screen_eig.y);
2379 		 */
2380 		ws.openblock.screenrect.max.x = ws.openblock.screenrect.min.x + width;
2381 		ws.openblock.screenrect.max.y = ws.openblock.screenrect.min.y + height;
2382 		Wimp_OpenWindow(&(ws.openblock));
2383 		if (reclose)
2384 		{
2385 			Wimp_CloseWindow(w);
2386 		}
2387 	}
2388 }
2389 
2390 
2391 
2392 
2393 
2394 
2395 
2396 
2397 
2398 
2399 
2400 
2401 
2402 
2403 
2404 /*
2405  | Change the size of a window to suit the font displayed in it
2406  */
resize_term_for_font(term_data * t)2407 static void resize_term_for_font(term_data *t)
2408 {
2409 	int fw, fh;
2410 	set_up_zrb(t);
2411 
2412 	fw = zrb.r_charw << screen_eig.x;
2413 	fh = zrb.r_charh << screen_eig.y;
2414 	if (zrb.r_flags.bits.double_height)
2415 	{
2416 		fh *= 2;
2417 	}
2418 
2419 	/* Caulculate new size */
2420 	fw *= 80;
2421 	fh *= 24;
2422 
2423 	set_window_size(t->w, fw, fh);
2424 }
2425 
2426 
2427 
2428 
2429 
2430 
2431 
2432 
2433 
2434 
2435 
2436 
2437 
2438 
2439 
2440 
2441 
2442 
2443 
Hnd_Caret(event_pollblock * pb,void * ref)2444 static BOOL Hnd_Caret(event_pollblock * pb, void *ref)
2445 {
2446 	if (ref)
2447 		got_caret = 1;
2448 	else
2449 		got_caret = 0;
2450 	return TRUE;
2451 }
2452 
2453 
2454 
2455 
2456 /*
2457  | Attach a (named) font to the specified term.
2458  | If 'font' is NULL then the system font is attached.
2459  | The bpp_n data is calculated if necessary
2460  | returns:
2461  |	1 => the font was attached OK
2462  |	0 => the system font was substituted
2463  */
attach_font_to_term(term_data * t,char * font)2464 static int attach_font_to_term(term_data *t, char *font)
2465 {
2466 	if (t->font != SYSTEM_FONT)
2467 	{
2468 		lose_font(t->font);
2469 	}
2470 	if (font)
2471 	{
2472 		t->font = find_font(font);
2473 	}
2474 	if (!t->font)
2475 	{
2476 		t->font = SYSTEM_FONT;
2477 		if (font)
2478 		{
2479 			Msgs_Report(1, "err.font_l", font);
2480 		}
2481 	}
2482 	else
2483 	{
2484 		if (!t->font->bpp_n)
2485 		{
2486 			lose_font(t->font);
2487 			t->font = SYSTEM_FONT;
2488 			if (font)
2489 			{
2490 				Msgs_Report(1, "err.font_c", font);
2491 			}
2492 		}
2493 	}
2494 	resize_term_for_font(t);
2495 	return !(t->font == SYSTEM_FONT);
2496 }
2497 
2498 
2499 
2500 
2501 /*--------------------------------------------------------------------------*/
2502 
2503 
2504 
2505 /*
2506  | Create a menu of all the (probable!) fonts in the specified location
2507  | NB: Any file of type 'data' is considered a font.
2508  |
2509  | Subdirectories are recursively searched.
2510  |
2511  | 1.10 - Uses <variant>$FontPaths to get a (space separated) list of paths
2512  | to search.  For each path name, the menu text will be the name and the
2513  | path searched will be <variant>$<name>$FontPath
2514  |
2515  | Eg. (for angband):
2516  | Angband$FontPaths Zap Angband
2517  | Angband$Zap$FontPath ZapFonts:
2518  | Angband$Angband$FontPath Angband:xtra.fonts.
2519  */
make_font_menu(void)2520 static void make_font_menu(void)
2521 {
2522 	char *t;
2523 	char buffer[260];
2524 	char menu_buffer[260];
2525 	int paths;
2526 	int i;
2527 	unsigned int max_width;
2528 	const char *path[64];	/* pointers to path names */
2529 	menu_item *mi;
2530 
2531 	font_menu = NULL;
2532 
2533 	/* Get the path (ie. dir) to look under */
2534 	t = getenv(RISCOS_VARIANT "$FontPaths");
2535 
2536 	/* Hack: cope if the path isn't set */
2537 	if (!t)
2538 	{
2539 		t = "";
2540 	}
2541 
2542 	strcpy(buffer, t);
2543 
2544 	/*
2545 	   | Count how many paths there are, build an array of pointers to them
2546 	   | and terminate them in the buffer
2547 	 */
2548 	paths = 1;					/* including the system font fake path '<System>' */
2549 	for (t = buffer; *t; t++)
2550 	{
2551 		if (*t == ' ')
2552 		{
2553 			*t = 0;
2554 		}
2555 		else
2556 		{
2557 			if (t == buffer || !t[-1])
2558 			{
2559 				path[paths] = t;
2560 				paths++;
2561 			}
2562 		}
2563 	}
2564 
2565 	/*
2566 	   | Create the menu
2567 	 */
2568 	path[0] = SYSTEM_FONT->name;
2569 
2570 	font_menu = f_malloc(sizeof(menu_block) + paths * sizeof(menu_item));
2571 	if (!font_menu)
2572 	{
2573 		core("Out of memory (building font menu)");
2574 	}
2575 	memset(font_menu, 0, sizeof(menu_block) + paths * sizeof(menu_item));
2576 
2577 	strncpy(font_menu->title, "Fonts", 12);
2578 	font_menu->titlefore = 7;
2579 	font_menu->titleback = 2;
2580 	font_menu->workfore = 7;
2581 	font_menu->workback = 0;
2582 	font_menu->height = 44;
2583 	font_menu->gap = 0;
2584 	max_width = strlen(font_menu->title);
2585 
2586 	mi = (menu_item *) (font_menu + 1);
2587 
2588 	for (i = 0; i < paths; i++)
2589 	{
2590 		mi[i].submenu.value = -1;
2591 		mi[i].iconflags.data.text = 1;
2592 		mi[i].iconflags.data.filled = 1;
2593 		mi[i].iconflags.data.foreground = 7;
2594 		mi[i].iconflags.data.background = 0;
2595 		strncpy(mi[i].icondata.text, path[i], 12);
2596 		if (strlen(mi[i].icondata.text) > max_width)
2597 			max_width = strlen(mi[i].icondata.text);
2598 	}
2599 	font_menu->width = (max_width + 2) * 16;
2600 	mi[i - 1].menuflags.data.last = 1;
2601 
2602 	/*
2603 	   | Hack: add a dotted line after the system font entry if appropriate
2604 	 */
2605 	if (paths > 1) mi[0].menuflags.data.dotted = 1;
2606 
2607 	/*
2608 	   | Iterate over the paths, building the appropriate submenus
2609 	 */
2610 	for (i = 1; i < paths; i++)
2611 	{
2612 		menu_ptr sub_menu = NULL;
2613 
2614 		sprintf(menu_buffer, "%s$%s$FontPath", RISCOS_VARIANT, path[i]);
2615 		t = getenv(menu_buffer);
2616 		/* Hack: cope if the path isn't defined */
2617 		if (!t)
2618 		{
2619 			t = "";
2620 		}
2621 
2622 		/* Fudge so that the fontpath can be a path, not just a dir. */
2623 		strcpy(menu_buffer, t);
2624 		for (t = menu_buffer; *t > ' '; t++)
2625 			;
2626 		if (t[-1] == '.')
2627 		{
2628 			t--;
2629 		}
2630 		*t = 0;
2631 
2632 		/* Build the menu.  Don't bother if the path variable was empty */
2633 		if (*menu_buffer)
2634 			sub_menu = make_zfont_menu(menu_buffer);
2635 
2636 		if (!sub_menu)
2637 		{
2638 			mi[i].iconflags.data.shaded = 1;
2639 		}
2640 		else
2641 		{
2642 			mi[i].submenu.menu = sub_menu;
2643 			/* Override the title of the 'root' sub-menu */
2644 			strncpy(sub_menu->title, path[i], 12);
2645 			/* Add the submenu to the main menu */
2646 		}
2647 	}
2648 
2649 	return;
2650 }
2651 
2652 /* ----------------------------------------------- musus, xxxx-xx-xx ---
2653  * Create and set up the infobox.
2654  * --------------------------------------------------------------------- */
create_info_box(void)2655 static void create_info_box(void)
2656 {
2657 	info_box = Window_Create("info", template_TITLEMIN);
2658 	Icon_printf(info_box, 0, "%s %s", VARIANT, VERSION);
2659 	Icon_SetText(info_box, 2, AUTHORS);
2660 	Icon_SetText(info_box, 3, PORTERS);
2661 	Icon_SetText(info_box, 7, PORTVERSION);
2662 
2663 	return;
2664 }
2665 
2666 /*
2667  | Create the various menus
2668  */
init_menus(void)2669 static void init_menus(void)
2670 {
2671 	char buffer1[256];
2672 	char buffer2[32];
2673 	char *o;
2674 
2675 	create_info_box();			/* For the Info> entries */
2676 	make_font_menu();			/* Make the fonts menu */
2677 
2678 	Msgs_Lookup("menu.ibar:Info|>Save As|Full screen,Gamma correction,Sound,"
2679 				"Windows|Save choices|Quit (& save)", buffer1, 256);
2680 	ibar_menu = Menu_New(VARIANT, buffer1);
2681 	if (!ibar_menu) core("Can't create Iconbar menu!");
2682 
2683 	Msgs_Lookup("menu.term:Info|>Save As|Font,Windows", buffer1, 256);
2684 	term_menu = Menu_New(VARIANT, buffer1);
2685 	if (!term_menu) core("Can't create Term menu!");
2686 
2687 #ifndef OLD_TERM_MENU
2688 	o = buffer1;
2689 	o += sprintf(buffer1, "%s|", VARIANT);
2690 	o += sprintf(o, "%s,%s,%s|", angband_term_name[1], angband_term_name[2],
2691 				 angband_term_name[3]);
2692 	sprintf(o, "%s,%s,%s,%s", angband_term_name[4], angband_term_name[5],
2693 			angband_term_name[6], angband_term_name[7]);
2694 #else
2695 	Msgs_printf(buffer1, "menu.windows:%s|Term-1 (Mirror),Term-2 (Recall),"
2696 				"Term-3 (Choice)|Term-4,Term-5,Term-6,Term-7", VARIANT);
2697 #endif
2698 	Msgs_Lookup("menu.winT:Windows", buffer2, 32);
2699 	wind_menu = Menu_New(buffer2, buffer1);
2700 	if (!wind_menu)
2701 	{
2702 		core("Can't create Windows menu!");
2703 	}
2704 
2705 	/* Now attach the various submenus to where they belong */
2706 	Menu_AddSubMenu(ibar_menu, IBAR_MENU_INFO, (menu_ptr) info_box);
2707 	Menu_AddSubMenu(ibar_menu, IBAR_MENU_GAMMA, (menu_ptr) gamma_win);
2708 	Menu_AddSubMenu(ibar_menu, IBAR_MENU_SOUND, (menu_ptr) sound_win);
2709 	Menu_AddSubMenu(ibar_menu, IBAR_MENU_WINDOWS, (menu_ptr) wind_menu);
2710 	Menu_AddSubMenu(term_menu, TERM_MENU_INFO, (menu_ptr) info_box);
2711 	Menu_AddSubMenu(term_menu, TERM_MENU_WINDOWS, wind_menu);
2712 
2713 	/* Add the savebox */
2714 	Menu_Warn(ibar_menu, IBAR_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL);
2715 	Menu_Warn(term_menu, TERM_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL);
2716 
2717 	if (font_menu)
2718 		/* add the submenu */
2719 		Menu_AddSubMenu(term_menu, TERM_MENU_FONT, font_menu);
2720 	else
2721 		/* If the font menu is buggered, shade its entry */
2722 		/* unticked, shaded */
2723 		Menu_SetFlags(term_menu, TERM_MENU_FONT, FALSE, TRUE);
2724 }
2725 
2726 
2727 
2728 
grab_caret(void)2729 static void grab_caret(void)
2730 {
2731 	caret_block cb;
2732 	cb.window = data[0].w;
2733 	cb.icon = -1;
2734 	cb.height = 1 << 25;		/* Invisible */
2735 	Wimp_SetCaretPosition(&cb);
2736 }
2737 
2738 
2739 
2740 
2741 /*
2742  | (Recursively) clear all ticks from the specified menu
2743  */
clear_all_menu_ticks(menu_ptr mp)2744 static void clear_all_menu_ticks(menu_ptr mp)
2745 {
2746 	menu_item *mi = (menu_item *) (mp + 1);
2747 
2748 	do
2749 	{
2750 		if (mi->menuflags.data.ticked)
2751 			mi->menuflags.data.ticked = 0;
2752 		if (mi->submenu.value != -1)
2753 			clear_all_menu_ticks(mi->submenu.menu);
2754 		mi++;
2755 	}
2756 	while (mi[-1].menuflags.data.last != 1);
2757 }
2758 
2759 
2760 
2761 
2762 
2763 
2764 /*
2765  | Set the font menu's ticks to match the specifed font name.
2766  |
2767  | fm is the (sub) menu to scan down (recursing into it's submenus (if any))
2768  | fn is the font name to match
2769  | prefix is the menu text to be prepended to the menu entries due to
2770  | previous menus (eg. "08x16" will cause fn="08x16.fred" to match menu
2771  | entry "fred".
2772  |
2773  | NB: recursive.
2774  |
2775  */
2776 
set_font_menu_ticks(menu_ptr fm,char * fn,const char * prefix)2777 static void set_font_menu_ticks(menu_ptr fm, char *fn, const char *prefix)
2778 {
2779 	char buffer[260];
2780 	char *b_leaf;	/* -> menu 'leaf' text in buffer */
2781 	int pl;	/* prefix string length */
2782 	menu_item *mi = (menu_item *) (fm + 1);
2783 
2784 	strcpy(buffer, prefix);
2785 	pl = strlen(buffer);
2786 	b_leaf = buffer + pl;
2787 
2788 	do
2789 	{
2790 		/* Check for (substring) match */
2791 		strncpy(b_leaf, mi->icondata.text, 12);
2792 
2793 		/* Is it a sub-menu? */
2794 		if (mi->submenu.value == -1)
2795 		{
2796 			/* No - must be an exact match */
2797 			mi->menuflags.data.ticked = !strcmp(buffer, fn);
2798 		}
2799 		else
2800 		{
2801 			/* Yes - must be a partial match (with a dot on :) */
2802 			strcat(b_leaf, ".");
2803 			mi->menuflags.data.ticked =
2804 				!strncmp(buffer, fn, pl + strlen(b_leaf));
2805 			if (mi->menuflags.data.ticked)
2806 				set_font_menu_ticks(mi->submenu.menu, fn, buffer);
2807 			else
2808 				clear_all_menu_ticks(mi->submenu.menu);
2809 		}
2810 
2811 		/* Next item */
2812 		mi++;
2813 	}
2814 	while (mi[-1].menuflags.data.last != 1);	/* Until finished */
2815 }
2816 
2817 
2818 
2819 
2820 
2821 
2822 
2823 
2824 
2825 
2826 
2827 /*
2828  | Set ticks, etc. in the term_menu to reflect the current state of the
2829  | term 't'
2830  */
set_up_term_menu(term_data * t)2831 static void set_up_term_menu(term_data *t)
2832 {
2833 	int i;
2834 	menu_ptr mp;
2835 	menu_item *mi;
2836 	window_state ws;
2837 
2838 	/* First of all, set up menu title to be the term's title */
2839 	strncpy(term_menu->title, t->name, 12);
2840 
2841 	/* Now set the ticks in the Windows> submenu (cuz it's easy) */
2842 	mp = wind_menu;				/* Windows submenu */
2843 	mi = (menu_item *) (mp + 1);	/* First entry */
2844 	for (i = 0; i < MAX_TERM_DATA; i++)
2845 	{
2846 		Wimp_GetWindowState(data[i].w, &ws);
2847 		mi[i].menuflags.data.ticked = ws.flags.data.open;
2848 	}
2849 
2850 	/*
2851 	   | Now, the tricky bit:  find out which font is selected in this
2852 	   | term and tick it in the menu.  Untick all the rest.
2853 	 */
2854 	set_font_menu_ticks(font_menu, t->font->name, "");
2855 
2856 	/* Shade the 'Save>' entry if saving isn't possible (yet) */
2857 	if (game_in_progress && character_generated)
2858 		Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, PDEADCHK);
2859 	else
2860 		Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, TRUE);
2861 
2862 }
2863 
2864 
2865 
2866 /*
2867  | Generic 'click' handler for windows - turns a drag on the window
2868  | into a move-window style drag.
2869  */
Hnd_Click(event_pollblock * pb,void * ref)2870 static BOOL Hnd_Click(event_pollblock * pb, void *ref)
2871 {
2872 	if (pb->data.mouse.button.data.dragselect ||
2873 		pb->data.mouse.button.data.dragadjust)
2874 	{
2875 		drag_block b;
2876 		b.window = pb->data.mouse.window;
2877 		b.screenrect.min.x = b.screenrect.min.y = 0;
2878 		b.screenrect.max.x = screen_size.x;
2879 		b.screenrect.max.y = screen_size.y;
2880 		b.type = drag_MOVEWINDOW;
2881 		Wimp_DragBox(&b);
2882 	}
2883 
2884 	return TRUE;
2885 }
2886 
2887 
2888 
2889 /*
2890  | Handle a click on a Term window.
2891  */
Hnd_TermClick(event_pollblock * pb,void * ref)2892 static BOOL Hnd_TermClick(event_pollblock * pb, void *ref)
2893 {
2894 	term_data *t = (term_data *)ref;
2895 
2896 	if (pb->data.mouse.button.data.menu)
2897 	{
2898 		menu_term = t;
2899 		set_up_term_menu(t);
2900 		Menu_Show(term_menu, pb->data.mouse.pos.x - 32,
2901 				  pb->data.mouse.pos.y + 32);
2902 	}
2903 	else
2904 	{
2905 		grab_caret();
2906 		Hnd_Click(pb, ref);
2907 	}
2908 
2909 	return TRUE;
2910 }
2911 
2912 #endif /* FULLSCREEN_ONLY */
2913 
2914 
2915 
mark_ood(term_data * t,int minx,int miny,int maxx,int maxy)2916 static void mark_ood(term_data *t, int minx, int miny, int maxx, int maxy)
2917 {
2918 	if (t->changed_box.min.x > minx)
2919 		t->changed_box.min.x = minx;
2920 	if (t->changed_box.max.x < maxx)
2921 		t->changed_box.max.x = maxx;
2922 	if (t->changed_box.min.y > miny)
2923 		t->changed_box.min.y = miny;
2924 	if (t->changed_box.max.y < maxy)
2925 		t->changed_box.max.y = maxy;
2926 }
2927 
2928 
2929 #ifndef FULLSCREEN_ONLY
2930 
2931 /* Check for an event (ie. key press) */
Term_xtra_acn_check(void)2932 static errr Term_xtra_acn_check(void)
2933 {
2934 	static int last_poll = 0;
2935 	int curr_time;
2936 	int bh, bl;
2937 
2938 	/*
2939 	   | Only poll the wimp if there's something in the keyboard buffer
2940 	   | or every 10cs
2941 	 */
2942 
2943 	/* Check the kbd buffer */
2944 	SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
2945 	bl = (bl & 0xff) + (bh << 8);
2946 
2947 	/* Check how long it is since we last polled */
2948 	curr_time = Time_Monotonic();
2949 
2950 	if ((bl > 0 && got_caret) || ((curr_time - last_poll) > 9))
2951 	{
2952 		last_poll = curr_time;
2953 		Stop_Hourglass;
2954 		Event_Poll();
2955 		Start_Hourglass;
2956 	}
2957 
2958 	/*
2959 	   | This allows the user to interrupt the borg.
2960 	 */
2961 	if (key_pressed) return key_pressed = 0;
2962 
2963 	return 1;
2964 }
2965 
2966 
2967 /*
2968  | Wait for an event (ie. keypress)
2969  | Note that we idle poll once a second to allow us to implement the
2970  | alarm system.
2971  */
Term_xtra_acn_event(void)2972 static errr Term_xtra_acn_event(void)
2973 {
2974 	Stop_Hourglass;
2975 
2976 	while (!key_pressed && !fullscreen_font)
2977 	{
2978 		Event_PollIdle(100);
2979 	}
2980 	Start_Hourglass;
2981 
2982 	return key_pressed = 0;
2983 }
2984 
2985 
2986 
2987 /* React to changes (eg. palette change) */
Term_xtra_acn_react(void)2988 static errr Term_xtra_acn_react(void)
2989 {
2990 	int c;
2991 
2992 	cache_palette();
2993 
2994 	/* Mark the entirety of each window as out of date */
2995 	for (c = 0; c < MAX_TERM_DATA; c++)
2996 		mark_ood(&data[c], 0, 0, 80, 24);
2997 
2998 	/* Force a redraw of the windows */
2999 	refresh_windows();
3000 
3001 	/* Success */
3002 	return 0;
3003 }
3004 
3005 
3006 
3007 /* Do various things to a term */
Term_xtra_acn(int n,int v)3008 static errr Term_xtra_acn(int n, int v)
3009 {
3010 	term_data *t = (term_data *)Term;
3011 
3012 	switch (n)
3013 	{
3014 		case TERM_XTRA_EVENT:	/* Wait/check for an event */
3015 			if (v)
3016 				return Term_xtra_acn_event();
3017 			else
3018 				return Term_xtra_acn_check();
3019 
3020 		case TERM_XTRA_BORED:	/* Bored */
3021 			return Term_xtra_acn_check();
3022 
3023 		case TERM_XTRA_FLUSH:	/* Flush input */
3024 			if (got_caret)
3025 			{
3026 				/* 1.21 - Hack: wait until no keys are pressed */
3027 				if (hack_flush)
3028 					for (v = 0; v != 0xff;)
3029 						SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
3030 				SWI(3, 0, SWI_OS_Byte, 21, 0, 0);	/* Flush Kbd buffer */
3031 			}
3032 			return 0;
3033 
3034 		case TERM_XTRA_FRESH:	/* Flush output */
3035 			refresh_window(t);
3036 			return 0;
3037 
3038 		case TERM_XTRA_FROSH:	/* Ensure line 'v' is plotted */
3039 			/* Doesn't do anything */
3040 			return 0;
3041 
3042 		case TERM_XTRA_SHAPE:	/* Set cursor visibility */
3043 			t->cursor.visible = v ? TRUE : FALSE;
3044 			mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
3045 					 t->cursor.pos.x + 1, t->cursor.pos.y + 1);
3046 			refresh_window(t);	/* needed? */
3047 			return 0;
3048 
3049 		case TERM_XTRA_NOISE:	/* Make a beep */
3050 			Sound_SysBeep();
3051 			return 0;
3052 
3053 		case TERM_XTRA_REACT:	/* React to, eg. palette changes */
3054 			return Term_xtra_acn_react();
3055 
3056 		case TERM_XTRA_DELAY:	/* Delay for 'v' ms */
3057 			if (v > 0)
3058 			{
3059 				unsigned int start = Time_Monotonic();
3060 				v = (v + 5) / 10;	/* Round to nearest cs */
3061 				GFX_Wait();
3062 				while ((Time_Monotonic() - start) < v)
3063 					;
3064 			}
3065 			return 0;
3066 
3067 		case TERM_XTRA_SOUND:	/* Play a sound :) */
3068 			if (enable_sound)
3069 			{
3070 				play_sound(v);
3071 			}
3072 			return 0;
3073 
3074 		default:
3075 			return 1;			/* Unsupported */
3076 	}
3077 }
3078 
3079 
3080 
3081 /* Move (but don't necessarily display) the cursor */
Term_curs_acn(int x,int y)3082 static errr Term_curs_acn(int x, int y)
3083 {
3084 	term_data *t = (term_data *)Term;
3085 
3086 	if (t->cursor.visible)
3087 		mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
3088 				 t->cursor.pos.x + 1, t->cursor.pos.y + 1);
3089 
3090 	t->cursor.pos.x = x;
3091 	t->cursor.pos.y = y;
3092 
3093 	if (t->cursor.visible)
3094 		mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
3095 				 t->cursor.pos.x + 1, t->cursor.pos.y + 1);
3096 
3097 	return 0;
3098 }
3099 
3100 
3101 
3102 /*
3103  | NB: these two are very simple since we use the Term's contents
3104  | directly to generate the r_data for ZapRedraw.
3105  */
3106 
3107 /* Erase 'n' characters at (x,y) */
Term_wipe_acn(int x,int y,int n)3108 static errr Term_wipe_acn(int x, int y, int n)
3109 {
3110 	mark_ood((term_data *)Term, x, y, x + n, y + 1);
3111 	return 0;
3112 }
3113 
3114 /* Write 'n' characters from 's' with attr 'a' at (x,y) */
Term_text_acn(int x,int y,int n,byte a,cptr s)3115 static errr Term_text_acn(int x, int y, int n, byte a, cptr s)
3116 {
3117 	mark_ood((term_data *)Term, x, y, x + n, y + 1);
3118 	return 0;
3119 }
3120 
3121 #endif /* FULLSCREEN_ONLY */
3122 
3123 
3124 /* Initialise one of our terms */
Term_init_acn(term * t)3125 static void Term_init_acn(term *t)
3126 {
3127 	term_data *term = (term_data *)t;
3128 
3129 	/* Ludicrous changed box settings :) */
3130 	term->changed_box.min.x = 256;
3131 	term->changed_box.min.y = 256;
3132 	term->changed_box.max.x = 0;
3133 	term->changed_box.max.y = 0;
3134 }
3135 
3136 
3137 
term_data_link(term_data * td,int k)3138 static void term_data_link(term_data *td, int k)
3139 {
3140 	term *t = &(td->t);
3141 
3142 	/* Initialise the term */
3143 	term_init(t, 80, 24, k);
3144 
3145 	/* Set flags and hooks */
3146 	t->attr_blank = TERM_DARK;
3147 	t->char_blank = ' ';
3148 
3149 	/* Experiment (FS mode requires them) */
3150 	t->always_text = TRUE;
3151 	t->never_frosh = TRUE;
3152 	/* Experiment (FS mode requires them) */
3153 
3154 #ifdef FULLSCREEN_ONLY
3155 	t->wipe_hook  = Term_wipe_acnFS;
3156 	t->xtra_hook  = Term_xtra_acnFS;
3157 	t->curs_hook  = Term_curs_acnFS;
3158 	t->text_hook  = Term_text_acnFS;
3159 #else
3160 	t->init_hook = Term_init_acn;
3161 	t->xtra_hook = Term_xtra_acn;
3162 	t->wipe_hook = Term_wipe_acn;
3163 	t->curs_hook = Term_curs_acn;
3164 	t->text_hook = Term_text_acn;
3165 	t->user_hook = Term_user_acn;
3166 #endif /* FULLSCREEN_ONLY */
3167 
3168 	t->data = td;
3169 
3170 	Term_activate(t);
3171 }
3172 
3173 
3174 #ifndef FULLSCREEN_ONLY
3175 
3176 /* Open default windows (ie. as set in choices) at the appropriate sizes */
show_windows(void)3177 static void show_windows(void)
3178 {
3179 	int i;
3180 	for (i = MAX_TERM_DATA; i-- > 0;)
3181 	{
3182 		if (!data[i].unopened)
3183 		{
3184 			if (data[i].def_open)
3185 				Window_Show(data[i].w, open_WHEREVER);
3186 		}
3187 		else
3188 		{
3189 			if (data[i].def_open)
3190 			{
3191 				window_openblock ob;
3192 				ob.window = data[i].w;
3193 				ob.screenrect = data[i].def_pos;
3194 				ob.scroll = data[i].def_scroll;
3195 				ob.behind = -1;
3196 				Wimp_OpenWindow(&ob);
3197 				data[i].unopened = 0;
3198 			}
3199 		}
3200 	}
3201 }
3202 
3203 
3204 /*
3205  | 'ref' is used to indicate whether this close is being forced by some other
3206  | part of the code (eg. the Windows> submenu code).  This is used to modify
3207  | the 'adjust doesn't close other terms' behavior.
3208  */
3209 
Hnd_MainClose(event_pollblock * pb,void * ref)3210 static BOOL Hnd_MainClose(event_pollblock * pb, void *ref)
3211 {
3212 	int i;
3213 	window_state ws;
3214 	mouse_block mb;
3215 
3216 	/* New in 1.08: don't close other Terms if closed with adjust */
3217 	Wimp_GetPointerInfo(&mb);
3218 	if (ref || mb.button.data.adjust)
3219 	{
3220 		Wimp_CloseWindow(data[0].w);
3221 	}
3222 	else
3223 	{
3224 		/* Close all the terms, but mark the open ones as 'def_open' */
3225 		for (i = 0; i < MAX_TERM_DATA; i++)
3226 		{
3227 			Wimp_GetWindowState(data[i].w, &ws);
3228 			if (!ws.flags.data.open)
3229 			{
3230 				data[i].def_open = 0;
3231 			}
3232 			else
3233 			{
3234 				Wimp_CloseWindow(data[i].w);
3235 				data[i].def_open = 1;
3236 			}
3237 		}
3238 	}
3239 
3240 	return TRUE;
3241 }
3242 
3243 
Hnd_PaletteChange(event_pollblock * pb,void * ref)3244 static BOOL Hnd_PaletteChange(event_pollblock * pb, void *ref)
3245 {
3246 	cache_palette();
3247 	return TRUE;
3248 }
3249 
3250 
3251 
Hnd_ModeChange(event_pollblock * pb,void * ref)3252 static BOOL Hnd_ModeChange(event_pollblock * pb, void *ref)
3253 {
3254 	int i;
3255 	Screen_CacheModeInfo();
3256 	set_up_zrb_for_mode();		/* (re)set up the redraw block */
3257 	cache_palette();			/* (re)cache the palette */
3258 	cache_fonts();				/* (re)cache the fonts */
3259 	/* Enforce sizes (eg. if screen_eig.y has changed) */
3260 	for (i = 0; i < MAX_TERM_DATA; i++)
3261 		resize_term_for_font(&(data[i]));
3262 	return TRUE;
3263 }
3264 
3265 
3266 
3267 
Hnd_Keypress(event_pollblock * pb,void * ref)3268 static BOOL Hnd_Keypress(event_pollblock * pb, void *ref)
3269 {
3270 	static const char hex[] = "0123456789ABCDEF";
3271 	int c = pb->data.key.code;
3272 	/* Check whether this key was pressed in Term 0 */
3273 	if (pb->data.key.caret.window == data[0].w)
3274 	{
3275 		switch (c)
3276 		{
3277 			case keycode_F12:
3278 			case keycode_SHIFT_F12:
3279 			case keycode_CTRL_F12:
3280 			case keycode_CTRL_SHIFT_F12:
3281 				/* Never intercept these */
3282 				break;
3283 
3284 			case 27:			/* handle escape specially */
3285 				if (Kbd_KeyDown(inkey_CTRL))
3286 				{
3287 					ack_alarm();
3288 					return TRUE;
3289 				}
3290 
3291 			/* Send everything else onto the Term package */
3292 			default:
3293 				/* Take care of "special" keypresses */
3294 				switch (c)
3295 				{
3296 					case keycode_TAB:
3297 					{
3298 						c = '\t';
3299 						break;
3300 					}
3301 
3302 					case keycode_PAGEUP:
3303 					{
3304 						c = '9';
3305 						break;
3306 					}
3307 
3308 					case keycode_PAGEDOWN:
3309 					{
3310 						c = '3';
3311 						break;
3312 					}
3313 
3314 					case keycode_COPY:
3315 					{
3316 						c = '1';
3317 						break;
3318 					}
3319 
3320 					case keycode_HOME:
3321 					{
3322 						c = '7';
3323 						break;
3324 					}
3325 				}
3326 				/* Pass to the angband engine */
3327 				/* Allow shift & ctrl to modify the keypad keys */
3328 				if (c >= '0' && c <= '9')
3329 				{
3330 					kbd_modifiers m = Kbd_GetModifiers(FALSE);
3331 					if (m.shift)
3332 					{
3333 						c |= 0x800;
3334 					}
3335 					if (m.ctrl)
3336 					{
3337 						c |= 0x400;
3338 					}
3339 					/* Could maybe add ALT as 0x1000 ??? */
3340 				}
3341 
3342 				/* Keys >255 have to be send as escape sequences (31=escape) */
3343 				if (c > 255 || c == 31)
3344 				{
3345 					Term_keypress(31);
3346 					Term_keypress(hex[(c & 0xf00) >> 8]);
3347 					Term_keypress(hex[(c & 0x0f0) >> 4]);
3348 					Term_keypress(hex[(c & 0x00f)]);
3349 					c = 13;
3350 				}
3351 				Term_keypress(c);
3352 				key_pressed = 1;
3353 				/*if ( c==27 ) { escape_pressed = 1; } */
3354 				return TRUE;
3355 		}
3356 	}
3357 
3358 	Wimp_ProcessKey(c);
3359 	return TRUE;
3360 }
3361 
3362 
3363 /*--------------------------------------------------------------------------*/
3364 /* Gamma correction window stuff											*/
3365 /*--------------------------------------------------------------------------*/
3366 
redraw_gamma(window_redrawblock * rb,BOOL * more)3367 static void redraw_gamma(window_redrawblock * rb, BOOL *more)
3368 {
3369 	int i, y, x, h, w;
3370 	int bx, by;
3371 	int dither;
3372 
3373 	bx = Coord_XToScreen(GC_XOFF, (convert_block *) & (rb->rect));
3374 	by = Coord_YToScreen(GC_YOFF, (convert_block *) & (rb->rect));
3375 
3376 	h = GC_HEIGHT / 4;
3377 	w = GC_WIDTH / 16;
3378 
3379 	x = bx;
3380 
3381 	while (*more)
3382 	{
3383 		for (i = 0; i < 16; i++)
3384 		{
3385 			y = by;
3386 			for (dither = 0; dither < 2; dither++)
3387 			{
3388 				/* Solid block: */
3389 				ColourTrans_SetGCOL(palette[i], dither << 8, 0);
3390 				GFX_RectangleFill(x, y, w, -h);
3391 				y -= h;
3392 				/* Dot on black: */
3393 				ColourTrans_SetGCOL(palette[0], dither << 8, 0);
3394 				GFX_RectangleFill(x, y, w, -h);
3395 				ColourTrans_SetGCOL(palette[i], dither << 8, 0);
3396 				GFX_RectangleFill(x + (w / 2) - 2, y - (h / 2), 2, 2);
3397 				y -= h;
3398 			}
3399 			x += w;
3400 		}
3401 		Wimp_GetRectangle(rb, more);
3402 	}
3403 }
3404 
3405 
update_gamma(void)3406 static void update_gamma(void)
3407 {
3408 	window_redrawblock rb;
3409 	BOOL more;
3410 
3411 	rb.window = gamma_win;
3412 	rb.rect.min.x = GC_XOFF;
3413 	rb.rect.min.y = GC_YOFF - GC_HEIGHT;
3414 	rb.rect.max.y = GC_XOFF + GC_WIDTH + screen_delta.x;
3415 	rb.rect.max.y = GC_YOFF + screen_delta.y;
3416 
3417 	Wimp_UpdateWindow(&rb, &more);
3418 	if (more)
3419 	{
3420 		redraw_gamma(&rb, &more);
3421 	}
3422 }
3423 
3424 
3425 
Hnd_RedrawGamma(event_pollblock * pb,void * ref)3426 static BOOL Hnd_RedrawGamma(event_pollblock * pb, void *ref)
3427 {
3428 	window_redrawblock rb;
3429 	BOOL more;
3430 
3431 	rb.window = pb->data.openblock.window;
3432 	Wimp_RedrawWindow(&rb, &more);
3433 	if (more)
3434 	{
3435 		redraw_gamma(&rb, &more);
3436 	}
3437 
3438 	return TRUE;
3439 }
3440 
3441 
Hnd_GammaClick(event_pollblock * pb,void * ref)3442 static BOOL Hnd_GammaClick(event_pollblock * pb, void *ref)
3443 {
3444 	int up = (ref == 0);
3445 
3446 	if (up)
3447 	{
3448 		if (gamma < 9.0)
3449 		{
3450 			gamma += 0.05;
3451 			Icon_SetDouble(gamma_win, 0, gamma, 2);
3452 			Term_xtra_acn_react();
3453 			update_gamma();
3454 		}
3455 	}
3456 	else
3457 	{
3458 		if (gamma > 0.05)
3459 		{
3460 			gamma -= 0.05;
3461 			Icon_SetDouble(gamma_win, GAMMA_ICN, gamma, 2);
3462 			Term_xtra_acn_react();
3463 			update_gamma();
3464 		}
3465 	}
3466 
3467 	/* Hack: if the user menu is active then force it to redraw */
3468 	if (user_menu_active)
3469 	{
3470 		Term_keypress(18);
3471 		key_pressed = 1;
3472 	}
3473 
3474 	return TRUE;
3475 }
3476 
3477 /*
3478  | Reflect the current options in the gamma window
3479  */
set_gamma_window_state(void)3480 static void set_gamma_window_state(void)
3481 {
3482 	if (minimise_memory) return;
3483 
3484 	Icon_SetDouble(gamma_win, 0, gamma, 2);
3485 }
3486 
3487 
init_gamma_window(void)3488 static void init_gamma_window(void)
3489 {
3490 	if (minimise_memory) return;
3491 
3492 	Template_UseSpriteArea(resource_sprites);
3493 	gamma_win = Window_Create("gamma", template_TITLEMIN);
3494 	Template_UseSpriteArea(NULL);
3495 	Event_Claim(event_REDRAW, gamma_win, event_ANY, Hnd_RedrawGamma, 0);
3496 	Event_Claim(event_CLICK, gamma_win, GAMMA_DOWN, Hnd_GammaClick, (void *)1);
3497 	Event_Claim(event_CLICK, gamma_win, GAMMA_UP, Hnd_GammaClick, (void *)0);
3498 	set_gamma_window_state();
3499 }
3500 
3501 
3502 /*--------------------------------------------------------------------------*/
3503 /* Sound options window stuff												*/
3504 /*--------------------------------------------------------------------------*/
3505 
3506 static slider_info volume_slider;
3507 
3508 
3509 /*
3510  | Reflect the current sound config in the sound options window
3511  */
set_sound_window_state(void)3512 static void set_sound_window_state(void)
3513 {
3514 	if (minimise_memory) return;
3515 
3516 	Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
3517 	Slider_SetValue(&volume_slider, sound_volume, NULL, NULL);
3518 
3519 	if (sound_volume > 127)
3520 		volume_slider.colour.foreground = colour_RED;
3521 	else
3522 		volume_slider.colour.foreground = colour_GREEN;
3523 }
3524 
3525 
3526 
3527 /*
3528  | The sound slider has been dragged, so update the sound volume setting
3529  */
update_volume_from_slider(slider_info * si,void * ref)3530 static int update_volume_from_slider(slider_info *si, void *ref)
3531 {
3532 	sound_volume = Slider_ReadValue(si);
3533 
3534 	if (sound_volume > 127)
3535 		volume_slider.colour.foreground = colour_RED;
3536 	else
3537 		volume_slider.colour.foreground = colour_GREEN;
3538 
3539 	return 0;
3540 }
3541 
3542 
3543 /*
3544  | Handle redraw events for the sound options window
3545  */
Hnd_RedrawSnd(event_pollblock * pb,void * ref)3546 static BOOL Hnd_RedrawSnd(event_pollblock * pb, void *ref)
3547 {
3548 	window_redrawblock redraw;
3549 	BOOL more;
3550 
3551 	redraw.window = pb->data.openblock.window;
3552 	Wimp_RedrawWindow(&redraw, &more);
3553 
3554 	while (more)
3555 	{
3556 		Slider_Redraw(((slider_info *) ref), &redraw.cliprect);
3557 		Wimp_GetRectangle(&redraw, &more);
3558 	}
3559 	return (TRUE);
3560 }
3561 
3562 
3563 /*
3564  | Handle clicks on the sound options window
3565  */
Hnd_SndClick(event_pollblock * pb,void * ref)3566 static BOOL Hnd_SndClick(event_pollblock * pb, void *ref)
3567 {
3568 	int icn = pb->data.mouse.icon;
3569 	int adj = pb->data.mouse.button.data.adjust;
3570 	slider_info *si = (slider_info *) ref;
3571 
3572 	switch (icn)
3573 	{
3574 			/* Bump arrows for the slider: */
3575 		case SND_VOL_DOWN:
3576 			adj = !adj;
3577 
3578 		case SND_VOL_UP:
3579 			adj = adj ? -1 : 1;
3580 			sound_volume += adj;
3581 			if (sound_volume < SOUND_VOL_MIN)
3582 			{
3583 				sound_volume = SOUND_VOL_MIN;
3584 			}
3585 			if (sound_volume > SOUND_VOL_MAX)
3586 			{
3587 				sound_volume = SOUND_VOL_MAX;
3588 			}
3589 			set_sound_window_state();
3590 			break;
3591 
3592 			/* The slider itself */
3593 		case SND_VOL_SLIDER:
3594 			Icon_ForceRedraw(sound_win, SND_VOL_SLIDER);
3595 			Slider_Drag(si, NULL, NULL, NULL);
3596 			break;
3597 
3598 			/* The enable/disable icon */
3599 		case SND_ENABLE:
3600 			enable_sound = !enable_sound;
3601 			Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
3602 			if (enable_sound)
3603 			{
3604 				initialise_sound();
3605 			}
3606 			break;
3607 	}
3608 
3609 	/* Hack: if the user menu is active then force it to redraw */
3610 	if (user_menu_active)
3611 	{
3612 		Term_keypress(18);
3613 		key_pressed = 1;
3614 	}
3615 
3616 	return TRUE;
3617 }
3618 
3619 
3620 
3621 /*
3622  | Set the sound options window up, ready to rock :)
3623  */
init_sound_window(void)3624 static void init_sound_window(void)
3625 {
3626 	Template_UseSpriteArea(resource_sprites);
3627 	sound_win = Window_Create("sound", template_TITLEMIN);
3628 	Template_UseSpriteArea(NULL);
3629 
3630 	Event_Claim(event_REDRAW, sound_win, event_ANY, Hnd_RedrawSnd,
3631 				(void *)&volume_slider);
3632 	Event_Claim(event_CLICK, sound_win, event_ANY, Hnd_SndClick,
3633 				(void *)&volume_slider);
3634 
3635 	/* Set up the slider info */
3636 	volume_slider.window = sound_win;
3637 	volume_slider.icon = SND_VOL_SLIDER;
3638 	volume_slider.limits.min = SOUND_VOL_MIN;
3639 	volume_slider.limits.max = SOUND_VOL_MAX;
3640 	volume_slider.colour.foreground = colour_GREEN;
3641 	volume_slider.colour.background = colour_WHITE;
3642 	volume_slider.border.x = 8;
3643 	volume_slider.border.y = 8;
3644 	volume_slider.update = update_volume_from_slider;
3645 
3646 	set_sound_window_state();
3647 }
3648 
3649 
3650 
3651 
3652 
3653 /*--------------------------------------------------------------------------*/
3654 
3655 
3656 
3657 
3658 
3659 
3660 
3661 
3662 
3663 
3664 
3665 /*
3666  | A font has been selected.
3667  | At this point, menu_term is a pointer to the term for which the
3668  | menu was opened.
3669  */
handle_font_selection(int * s)3670 static void handle_font_selection(int *s)
3671 {
3672 	char name[260];
3673 	os_error *e;
3674 	char *r;
3675 	menu_ptr mp = font_menu;
3676 	int *mis;
3677 
3678 	/* Follow the >s to the entry specified */
3679 	for (mis = s; *mis != -1; mis++)
3680 		mp = ((menu_item *) (mp + 1))[*mis].submenu.menu;
3681 
3682 	/*
3683 	   | Now, check to see if we've hit a leaf entry.
3684 	   | NB: If the entry isn't a leaf entry then the first entry in its submenu
3685 	   | is used instead
3686 	 */
3687 	if (((int)mp) != -1)
3688 	{
3689 		mis[0] = 0;
3690 		mis[1] = -1;
3691 		mp = ((menu_item *) (mp + 1))[0].submenu.menu;
3692 	}
3693 
3694 	if (((int)mp) != -1)
3695 		return;
3696 
3697 	e = Wimp_DecodeMenu(font_menu, s, name);
3698 	if (e)
3699 	{
3700 		plog(e->errmess);
3701 		return;
3702 	}
3703 
3704 	/* Make sure that the string is NULL terminated */
3705 	for (r = name; *r >= ' '; r++)
3706 		;
3707 	*r = 0;
3708 
3709 	attach_font_to_term(menu_term, name);
3710 	mark_ood(menu_term, 0, 0, 80, 24);
3711 	refresh_window(menu_term);
3712 }
3713 
3714 
3715 #endif /* FULLSCREEN_ONLY */
3716 
3717 
3718 
3719 
load_choices(void)3720 static void load_choices(void)
3721 {
3722 	FILE *fp = NULL;
3723 	char *cf;
3724 	int i;
3725 	char buffer[260];
3726 
3727 	cf = find_choices(FALSE);
3728 	if (*cf)
3729 		fp = fopen(cf, "r");
3730 
3731 	/* Implement default choices */
3732 	data[0].def_open = 1;
3733 	data[0].unopened = 1;		/* ie. force def_pos */
3734 	data[0].def_pos.min.x = (screen_size.x - 1280) / 2;
3735 	data[0].def_pos.max.x = (screen_size.x + 1280) / 2;
3736 	data[0].def_pos.min.y = (screen_size.y - 768) / 2 - 32;
3737 	data[0].def_pos.max.y = (screen_size.y + 768) / 2 - 32;
3738 	data[0].def_scroll.x = data[0].def_scroll.y = 0;
3739 	for (i = 1; i < MAX_TERM_DATA; i++)
3740 	{
3741 		data[i].def_open = 0;
3742 		data[i].unopened = 1;	/* ie. force def_pos */
3743 		data[i].def_pos.min.x = (screen_size.x - 1280) / 2;
3744 		data[i].def_pos.max.x = (screen_size.x + 1280) / 2;
3745 		data[i].def_pos.min.y = (screen_size.y - 768) / 2;
3746 		data[i].def_pos.max.y = (screen_size.y + 768) / 2;
3747 		data[i].def_scroll.x = data[i].def_scroll.y = 0;
3748 	}
3749 
3750 	if (fp)
3751 	{
3752 		const char *t_;
3753 		char *o_;
3754 
3755 		if (!fgets(buffer, sizeof(buffer), fp))
3756 		{
3757 			fclose(fp);
3758 			return;
3759 		}
3760 		if (strcmp(buffer, "[Angband config, Musus' port]\n"))
3761 		{
3762 			fclose(fp);
3763 			return;
3764 		}
3765 
3766 		/* Load choices */
3767 		while (fgets(buffer, sizeof(buffer), fp))
3768 		{
3769 			t_ = strtok(buffer, " ");	/* Term number (or keyword, "Gamma", etc.) */
3770 			o_ = strtok(NULL, "\n");	/* argument string */
3771 			if (!o_)
3772 			{
3773 				o_ = "";
3774 			}					/* missing (or null) argument? */
3775 			if (t_)
3776 			{
3777 				if (!strcmp(t_, "Gamma"))
3778 					gamma = atof(o_);
3779 				else if (!strcmp(t_, "Monochrome"))
3780 					force_mono = !strcmp(o_, "on");
3781 				else if (!strcmp(t_, "Sound"))
3782 					enable_sound = !strcmp(o_, "on");
3783 				else if (!strcmp(t_, "Volume"))
3784 					sound_volume = atoi(o_);
3785 				else if (!strcmp(t_, "FullScreen"))
3786 					start_fullscreen = !strcmp(o_, "on");
3787 				else if (!strcmp(t_, "Hourglass"))
3788 					use_glass = !strcmp(o_, "on");
3789 				else if (!strcmp(t_, "HackFlush"))
3790 					hack_flush = !strcmp(o_, "on");
3791 				else if (!strcmp(t_, "AlarmTimeH"))
3792 					alarm_h = atoi(o_);
3793 				else if (!strcmp(t_, "AlarmTimeM"))
3794 					alarm_m = atoi(o_);
3795 				else if (!strcmp(t_, "AlarmText"))
3796 					strcpy(alarm_message, o_);
3797 				else if (!strcmp(t_, "AlarmBeep"))
3798 					alarm_beep = !strcmp(o_, "on");
3799 				else if (!strcmp(t_, "AlarmType"))
3800 				{
3801 					int i;
3802 					for (i = 0; i < 4; i++)
3803 						if (!strcmp(alarm_types[i], o_))
3804 							alarm_type = i;
3805 				}
3806 				else if (isdigit((unsigned char)*t_))
3807 				{
3808 					int t = atoi(t_);
3809 					if (t >= 0 && t < MAX_TERM_DATA)
3810 					{
3811 						char *f_, *x0_, *y0_, *x1_, *y1_, *sx_, *sy_;
3812 						o_ = strtok(o_, " ");	/* first word */
3813 						f_ = strtok(NULL, " ");	/* font name */
3814 						x0_ = strtok(NULL, " ");	/* x posn (min) */
3815 						y0_ = strtok(NULL, " ");	/* y posn (min) */
3816 						x1_ = strtok(NULL, " ");	/* x posn (max) */
3817 						y1_ = strtok(NULL, " ");	/* y posn (max) */
3818 						sx_ = strtok(NULL, " ");	/* x scroll offset */
3819 						sy_ = strtok(NULL, "\n");	/* y scroll offset */
3820 						data[t].def_open = (t == 0) || atoi(o_);
3821 						data[t].def_pos.min.x = atoi(x0_);
3822 						data[t].def_pos.min.y = atoi(y0_);
3823 						data[t].def_pos.max.x = atoi(x1_);
3824 						data[t].def_pos.max.y = atoi(y1_);
3825 						data[t].def_scroll.x = atoi(sx_);
3826 						data[t].def_scroll.y = atoi(sy_);
3827 						data[t].unopened = 1;	/* ie. force def_pos */
3828 #ifndef FULLSCREEN_ONLY
3829 						attach_font_to_term(&(data[t]), f_);
3830 #endif /* FULLSCREEN_ONLY */
3831 					}
3832 				}
3833 			}
3834 		}
3835 		fclose(fp);
3836 	}
3837 
3838 #ifndef FULLSCREEN_ONLY
3839 	/*
3840 	   | Fudge so that the main term is *always* fullsize
3841 	 */
3842 	{
3843 		int fw, fh;
3844 
3845 		set_up_zrb(&(data[0]));
3846 		fw = zrb.r_charw << screen_eig.x;
3847 		fh = zrb.r_charh << screen_eig.y;
3848 		if (zrb.r_flags.bits.double_height)
3849 		{
3850 			fh *= 2;
3851 		}
3852 		fw *= 80;
3853 		fh *= 24;
3854 		data[0].def_pos.max.x = data[0].def_pos.min.x + fw;
3855 		data[0].def_pos.max.y = data[0].def_pos.min.y + fh;
3856 		data[0].def_scroll.x = 0;
3857 		data[0].def_scroll.y = 0;
3858 	}
3859 #endif /* FULLSCREEN_ONLY */
3860 
3861 
3862 }
3863 
3864 
3865 
3866 
save_choices(void)3867 static void save_choices(void)
3868 {
3869 	FILE *fp = NULL;
3870 	FILE *fpm = NULL;
3871 	char *cf;
3872 	int i;
3873 
3874 	write_alarm_choices();
3875 
3876 	cf = find_choices(TRUE);
3877 	if (!*cf)
3878 	{
3879 		plog("Failed to locate writable choices file!");
3880 		return;
3881 	}
3882 
3883 	fp = fopen(cf, "w");
3884 	if (!fp)
3885 	{
3886 		plog("Can't write choices file");
3887 		return;
3888 	}
3889 
3890 	fpm = fopen(find_choices_mirror(), "w");
3891 
3892 	f2printf(fp, fpm, "[Angband config, Musus' port]\n");
3893 	f2printf(fp, fpm, "Gamma %.2lf\n", gamma);
3894 	f2printf(fp, fpm, "Monochrome %s\n", force_mono ? "on" : "off");
3895 	f2printf(fp, fpm, "Sound %s\n", enable_sound ? "on" : "off");
3896 	f2printf(fp, fpm, "Volume %d\n", sound_volume);
3897 	f2printf(fp, fpm, "FullScreen %s\n", start_fullscreen ? "on" : "off");
3898 	f2printf(fp, fpm, "Hourglass %s\n", use_glass ? "on" : "off");
3899 	f2printf(fp, fpm, "HackFlush %s\n", hack_flush ? "on" : "off");
3900 
3901 	for (i = 0; i < MAX_TERM_DATA; i++)
3902 	{
3903 		window_state ws;
3904 		Wimp_GetWindowState(data[i].w, &ws);
3905 		f2printf(fp, fpm, "%d %d %s ", i, ws.flags.data.open,
3906 				 data[i].font->name);
3907 		f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.x);
3908 		f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.y);
3909 		f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.x);
3910 		f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.y);
3911 		f2printf(fp, fpm, "%d %d\n", ws.openblock.scroll.x,
3912 				 ws.openblock.scroll.y);
3913 	}
3914 
3915 	fclose(fp);
3916 
3917 	if (fpm)
3918 	{
3919 		fclose(fpm);
3920 	}
3921 }
3922 
3923 /*
3924  | Update the Alarm choices file to reflect changed alarm settings.
3925  */
write_alarm_choices(void)3926 static void write_alarm_choices(void)
3927 {
3928 	FILE *fp;
3929 	char *cf;
3930 
3931 	/* Open the choices file for reading */
3932 	cf = find_alarmfile(TRUE);
3933 	if (!*cf)
3934 	{
3935 		plog("Can't determine Alarm file location!");
3936 		return;
3937 	}
3938 
3939 	fp = fopen(cf, "w");
3940 	if (!fp)
3941 	{
3942 		plog("Can't write Alarm file");
3943 		return;
3944 	}
3945 
3946 	/* Write the new alarm options */
3947 	fprintf(fp, "AlarmType %s\n", alarm_types[alarm_type]);
3948 	fprintf(fp, "AlarmTimeH %d\n", alarm_h);
3949 	fprintf(fp, "AlarmTimeM %d\n", alarm_m);
3950 	fprintf(fp, "AlarmText %s\n", alarm_message);
3951 	fprintf(fp, "AlarmBeep %s\n", alarm_beep ? "on" : "off");
3952 
3953 	fclose(fp);
3954 }
3955 
3956 /*
3957  | Read the Alarm choices file.
3958  */
read_alarm_choices(void)3959 static void read_alarm_choices(void)
3960 {
3961 	char buffer[260];
3962 	FILE *fp;
3963 	char *cf;
3964 
3965 	cf = find_alarmfile(FALSE);
3966 	if (!*cf)
3967 	{
3968 		return;
3969 	}
3970 
3971 	fp = fopen(cf, "r");
3972 	if (fp)
3973 	{
3974 		const char *t_, *o_;
3975 		/* Load choices */
3976 		while (fgets(buffer, sizeof(buffer), fp))
3977 		{
3978 			t_ = strtok(buffer, " ");	/* Keyword */
3979 			o_ = strtok(NULL, "\n");	/* argument string */
3980 			if (!o_)
3981 			{
3982 				o_ = "";
3983 			}					/* missing (or null) argument? */
3984 			if (t_)
3985 			{
3986 				if (!strcmp(t_, "AlarmTimeH"))
3987 					alarm_h = atoi(o_);
3988 				else if (!strcmp(t_, "AlarmTimeM"))
3989 					alarm_m = atoi(o_);
3990 				else if (!strcmp(t_, "AlarmText"))
3991 					strcpy(alarm_message, o_);
3992 				else if (!strcmp(t_, "AlarmBeep"))
3993 					alarm_beep = !strcmp(o_, "on");
3994 				else if (!strcmp(t_, "AlarmType"))
3995 				{
3996 					int i;
3997 					for (i = 0; i < 4; i++)
3998 						if (!strcmp(alarm_types[i], o_))
3999 							alarm_type = i;
4000 				}
4001 			}
4002 		}
4003 		fclose(fp);
4004 	}
4005 }
4006 
4007 
4008 
4009 #ifndef FULLSCREEN_ONLY
4010 /*
4011  | Handle selections from the term menu(s)
4012  */
Hnd_TermMenu(event_pollblock * pb,void * ref)4013 static BOOL Hnd_TermMenu(event_pollblock * pb, void *ref)
4014 {
4015 	mouse_block mb;
4016 	int i;
4017 
4018 	Wimp_GetPointerInfo(&mb);
4019 
4020 	switch (pb->data.selection[0])
4021 	{
4022 		case TERM_MENU_INFO:	/* Info> */
4023 			break;
4024 		case TERM_MENU_FONT:	/* Font> */
4025 			/* Sub item selected? */
4026 			if (pb->data.selection[1] == -1)
4027 			{
4028 				break;
4029 			}
4030 			handle_font_selection(pb->data.selection + 1);
4031 			break;
4032 		case TERM_MENU_WINDOWS:	/* Windows> */
4033 			if (pb->data.selection[1] == -1)
4034 			{
4035 				break;
4036 			}
4037 			i = pb->data.selection[1];
4038 			{
4039 				window_state ws;
4040 				Wimp_GetWindowState(data[i].w, &ws);
4041 				if (ws.flags.data.open)
4042 				{
4043 					if (!i)
4044 						Hnd_MainClose(NULL, (void *)TRUE);
4045 					else
4046 						Window_Hide(data[i].w);
4047 				}
4048 				else
4049 				{
4050 					if (!i)
4051 					{
4052 						show_windows();
4053 						grab_caret();
4054 					}
4055 					else
4056 					{
4057 						if (!data[i].unopened)
4058 						{
4059 							Window_Show(data[i].w, open_WHEREVER);
4060 						}
4061 						else
4062 						{
4063 							window_openblock ob;
4064 							ob.window = data[i].w;
4065 							ob.screenrect = data[i].def_pos;
4066 							ob.scroll = data[i].def_scroll;
4067 							ob.behind = -1;	/* could use data[0].w; ? */
4068 							Wimp_OpenWindow(&ob);
4069 							data[i].unopened = 0;
4070 						}
4071 					}
4072 				}
4073 			}
4074 			break;
4075 	}
4076 
4077 	if (mb.button.data.adjust)
4078 	{
4079 		set_up_term_menu(menu_term);
4080 		Menu_ShowLast();
4081 	}
4082 
4083 	return TRUE;
4084 }
4085 
4086 
4087 
4088 
4089 
4090 
4091 /*
4092  | Handle selections from the iconbar menu
4093  */
Hnd_IbarMenu(event_pollblock * pb,void * ref)4094 static BOOL Hnd_IbarMenu(event_pollblock * pb, void *ref)
4095 {
4096 	mouse_block mb;
4097 	Wimp_GetPointerInfo(&mb);
4098 
4099 	switch (pb->data.selection[0])
4100 	{
4101 		case IBAR_MENU_INFO:	/* Info> */
4102 			break;
4103 		case IBAR_MENU_FULLSCREEN:	/* Full screen */
4104 			/* Do Full Screen mode */
4105 			enter_fullscreen_mode();
4106 			break;
4107 		case IBAR_MENU_GAMMA:	/* Gamma correction */
4108 			break;
4109 		case IBAR_MENU_SOUND:	/* Sound */
4110 			/*
4111 			   | enable_sound = !enable_sound;
4112 			   | if ( enable_sound ) { initialise_sound(); }
4113 			   | Menu_SetFlags( ibar_menu, IBAR_MENU_SOUND, enable_sound, 0 );
4114 			   | set_sound_window_state();
4115 			 */
4116 			break;
4117 		case IBAR_MENU_WINDOWS:	/* Windows> */
4118 			/*
4119 			   | Hack: pass it off as the equivalent selection from
4120 			   | the term menu.
4121 			 */
4122 			pb->data.selection[0] = TERM_MENU_WINDOWS;
4123 			return Hnd_TermMenu(pb, ref);
4124 			break;
4125 		case IBAR_MENU_SAVECHOICES:	/* Save choices */
4126 			save_choices();
4127 			break;
4128 		case IBAR_MENU_QUIT:	/* Quit */
4129 			if (game_in_progress && character_generated)
4130 				save_player();
4131 			quit(NULL);
4132 			break;
4133 	}
4134 
4135 	if (mb.button.data.adjust)
4136 		Menu_ShowLast();
4137 
4138 	return TRUE;
4139 }
4140 
4141 
4142 
4143 
4144 
Hnd_MenuSel(event_pollblock * pb,void * ref)4145 static BOOL Hnd_MenuSel(event_pollblock * pb, void *ref)
4146 {
4147 	if (menu_currentopen == ibar_menu)
4148 		return Hnd_IbarMenu(pb, ref);
4149 	else if (menu_currentopen == term_menu)
4150 		return Hnd_TermMenu(pb, ref);
4151 	return FALSE;
4152 }
4153 
4154 
Hnd_IbarClick(event_pollblock * pb,void * ref)4155 static BOOL Hnd_IbarClick(event_pollblock * pb, void *ref)
4156 {
4157 	if (pb->data.mouse.button.data.menu)
4158 	{
4159 		set_gamma_window_state();
4160 		set_sound_window_state();
4161 
4162 		/* Hack: shade the Save> option if appropriate */
4163 		if (game_in_progress && character_generated)
4164 			Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, PDEADCHK);
4165 		else
4166 			Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, TRUE);
4167 
4168 		/*
4169 		   | Hack: set up the Term menu as if it was opened over the main
4170 		   | window (so that the Windows> submenu is set correctly)
4171 		 */
4172 		menu_term = (term_data *)&data[0];
4173 		set_up_term_menu(menu_term);
4174 
4175 		Menu_Show(ibar_menu, pb->data.mouse.pos.x, -1);
4176 		return TRUE;
4177 	}
4178 
4179 	if (pb->data.mouse.button.data.select)
4180 	{
4181 		show_windows();
4182 		grab_caret();
4183 		return TRUE;
4184 	}
4185 
4186 	if (pb->data.mouse.button.data.adjust)
4187 	{
4188 		enter_fullscreen_mode();
4189 		return TRUE;
4190 	}
4191 
4192 	return FALSE;
4193 }
4194 
4195 
4196 
4197 /*
4198  * Handler for NULL events (should this check the alarm in the desktop?
4199  */
Hnd_null(event_pollblock * event,void * ref)4200 static BOOL Hnd_null(event_pollblock *event, void *ref)
4201 {
4202 	/* Really no need to check the alarm more than twice per second. */
4203 	if (alarm_type && Time_Monotonic() > alarm_lastcheck + 200)
4204 	{
4205 		check_alarm();
4206 	}
4207 
4208 	return TRUE;
4209 }
4210 
4211 
4212 
4213 
4214 /*
4215  | Handler for PreQuit messages (eg. at shutdown).
4216  */
Hnd_PreQuit(event_pollblock * b,void * ref)4217 static BOOL Hnd_PreQuit(event_pollblock * b, void *ref)
4218 {
4219 	BOOL shutdown = (b->data.message.data.words[0] & 1) == 0;
4220 	task_handle originator = b->data.message.header.sender;
4221 	unsigned int quitref;
4222 	message_block mb;
4223 	char buffer1[64];
4224 	os_error e;
4225 	int ok;
4226 
4227 	if (!(game_in_progress && character_generated))
4228 		return TRUE;			/* ignore, we're OK to die */
4229 
4230 	/* Stop the shutdown/quit */
4231 	memcpy(&mb, &(b->data.message), 24);
4232 	quitref = mb.header.yourref;
4233 	mb.header.yourref = mb.header.myref;
4234 	Wimp_SendMessage(event_ACK, &mb, originator, 0);
4235 
4236 	/*
4237 	   | We handle this differently depending on the version of the Wimp;
4238 	   | newer versions give us much more flexibility.
4239 	 */
4240 	if (event_wimpversion < 350)
4241 	{
4242 		/*
4243 		   | Older versions - use 'OK' and 'Cancel'.
4244 		   | There is no "Save & Quit" button.
4245 		 */
4246 		Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
4247 		sprintf(buffer1, e.errmess, VARIANT);
4248 		Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
4249 					e.errmess, 260);
4250 		e.errnum = 0;
4251 		SWI(3, 2, SWI_Wimp_ReportError, &e, 3 | 16, buffer1, NULL, &ok);
4252 
4253 		if (ok != 1)
4254 			return TRUE;		/* no! Pleeeeeease don't kill leeeeddle ol' me! */
4255 	}
4256 	else
4257 	{
4258 		/*
4259 		   | Newer version: can add buttons to the dialog.
4260 		   | we add a 'Save and Quit' button to allow the shutdown to
4261 		   | continue /after/ saving.
4262 		 */
4263 		int flags;
4264 		char buttons[64];
4265 
4266 		Msgs_Lookup("err.shutbuts:Save & quit,Don't quit,Quit anyway",
4267 					buttons, 64);
4268 		Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
4269 		sprintf(buffer1, e.errmess, VARIANT);
4270 		Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
4271 					e.errmess, 260);
4272 		e.errnum = 0;
4273 
4274 		flags = 0 | 16 | 256 | (4 << 9);
4275 
4276 		SWI(6, 2, SWI_Wimp_ReportError, &e, flags, buffer1, ICONNAME, 0,
4277 			buttons, NULL, &ok);
4278 
4279 		if (ok == 4)
4280 			return TRUE;		/* no! Pleeeeeease don't kill leeeeddle ol' me! */
4281 
4282 		if (ok == 3)
4283 			save_player();		/* Save & Quit */
4284 	}
4285 
4286 
4287 	/* RO2 doesn't use the shudown flag */
4288 	if (shutdown && event_wimpversion >= 300 && mb.header.size >= 24)
4289 	{
4290 		key_block kb;
4291 		kb.code = 0x1fc;		/* restart shutdown sequence */
4292 		Wimp_SendMessage(event_KEY, (message_block *) & kb, originator, 0);
4293 	}
4294 
4295 	/* "Time... to die." */
4296 	Event_CloseDown();
4297 	exit(0);
4298 	return TRUE;				/* The one great certainty (sic) */
4299 }
4300 
4301 #endif /* FULLSCREEN_ONLY */
4302 
4303 
4304 
4305 
initialise_terms(void)4306 static void initialise_terms(void)
4307 {
4308 	char t[80];
4309 	int i;
4310 
4311 #ifndef FULLSCREEN_ONLY
4312 	if (!minimise_memory)
4313 	{
4314 		/* Create a window for each term.  Term 0 is special (no scroll bars) */
4315 		data[0].w = Window_Create("angband", template_TITLEMIN);
4316 		data[0].font = SYSTEM_FONT;
4317 		data[0].def_open = 1;
4318 		data[0].unopened = 1;
4319 		sprintf(t, "%s %s", VARIANT, VERSION);
4320 		Window_SetTitle(data[0].w, t);
4321 		strncpy(data[0].name, VARIANT, 12);
4322 		Event_Claim(event_KEY, data[0].w, event_ANY, Hnd_Keypress,
4323 					(void *)&(data[0]));
4324 		Event_Claim(event_REDRAW, data[0].w, event_ANY, Hnd_Redraw,
4325 					(void *)&(data[0]));
4326 		Event_Claim(event_CLICK, data[0].w, event_ANY, Hnd_TermClick,
4327 					(void *)&(data[0]));
4328 		Event_Claim(event_CLOSE, data[0].w, event_ANY, Hnd_MainClose, NULL);
4329 
4330 		for (i = 1; i < MAX_TERM_DATA; i++)
4331 		{
4332 			data[i].w = Window_Create("term", template_TITLEMIN);
4333 			data[i].font = SYSTEM_FONT;
4334 			data[i].def_open = 0;
4335 			data[i].unopened = 1;
4336 #ifndef OLD_TERM_MENU
4337 			sprintf(t, "%s (%s %s)", angband_term_name[i], VARIANT, VERSION);
4338 #else
4339 			sprintf(t, "Term-%d (%s %s)", i, VARIANT, VERSION);
4340 #endif
4341 			Window_SetTitle(data[i].w, t);
4342 			strncpy(data[i].name, t, 12);
4343 			Event_Claim(event_CLICK, data[i].w, event_ANY, Hnd_TermClick,
4344 						(void *)&(data[i]));
4345 			Event_Claim(event_REDRAW, data[i].w, event_ANY, Hnd_Redraw,
4346 						(void *)&(data[i]));
4347 		}
4348 	}
4349 #endif /* FULLSCREEN_ONLY */
4350 
4351 	term_data_link(&(data[0]), 256);
4352 
4353 	for (i = 1; i < MAX_TERM_DATA; i++)
4354 	{
4355 		term_data_link(&(data[i]), 16);
4356 		angband_term[i] = &(data[i].t);
4357 	}
4358 
4359 	angband_term[0] = &(data[0].t);
4360 	Term_activate(&(data[0].t));
4361 }
4362 
4363 
4364 
4365 
4366 /*
4367  | Hack(ish) - determine the version of RISC OS
4368  */
os_version(void)4369 static int os_version(void)
4370 {
4371 	int osv;
4372 	SWI(3, 2, SWI_OS_Byte, 129, 0, 255, NULL, &osv);
4373 	switch (osv)
4374 	{
4375 		case 0xa0: return 120;
4376 		case 0xa1: return 200;
4377 		case 0xa2: return 201;
4378 		case 0xa3: return 300;
4379 		case 0xa4: return 310;
4380 		case 0xa5: return 350;
4381 		case 0xa6: return 360;
4382 		case 0xa7: return 370;
4383 		case 0xa8: return 400;
4384 		default: return 370;
4385 	}
4386 	return -1;					/* -sigh- */
4387 }
4388 
4389 #ifndef FULLSCREEN_ONLY
4390 /*
4391  | Determine whether the current screen mode is "high-res"
4392  | (ie. should we use the "Sprites22" or the "Sprites" file.
4393  */
sprites22(void)4394 static int sprites22(void)
4395 {
4396 	int yeig;
4397 
4398 	OS_ReadModeVariable(-1, modevar_YEIGFACTOR, &yeig);
4399 
4400 	return yeig < 2;
4401 }
4402 
4403 /*
4404  | Determine whether we should use 2D or 3D templates.
4405  | 2D templates *must* be used under Wimp <3.00, but under RO3 we
4406  | use the CMOS settings to decide.
4407  */
templates2d(void)4408 static int templates2d(void)
4409 {
4410 	int r1, r2;
4411 
4412 	if (event_wimpversion < 300)
4413 		return TRUE;
4414 
4415 	/* The 3D bit is bit 0 of byte 140 */
4416 	OS_Byte(osbyte_READCMOSRAM, 140, 0, &r1, &r2);
4417 
4418 	return !(r2 && 1);
4419 }
4420 #endif /* FULLSCREEN_ONLY */
4421 
4422 
4423 
htoi(char * s)4424 static unsigned int htoi(char *s)
4425 {
4426 	static const char hex[] = "0123456789ABCDEF";
4427 	unsigned int v = 0;
4428 	while (*s)
4429 	{
4430 		char *m;
4431 		int d = toupper((unsigned char)*s++);
4432 		m = strchr(hex, d);
4433 		if (!m)
4434 		{
4435 			return v;
4436 		}
4437 		v = (v << 4) + (m - hex);
4438 	}
4439 	return v;
4440 }
4441 
read_unsigned(char * t)4442 static int read_unsigned(char *t)
4443 {
4444 	int r;
4445 	if (SWI(2, 3, SWI_OS_ReadUnsigned, 2, t, NULL, NULL, &r))
4446 		r = 0;
4447 	return r;
4448 }
4449 
4450 /*
4451  | Scan the string at 'n', replacing dodgy characters with underbars
4452  */
sanitise_name(char * n)4453 static void sanitise_name(char *n)
4454 {
4455 	for (; *n; n++)
4456 	{
4457 		if (strchr("\"$%^&*\\\'@#.,", *n))
4458 			*n = '_';
4459 	}
4460 }
4461 
4462 
4463 /*
4464  | Ensure that the path to a given object exists.
4465  | Ie. if |p| = "a.b.c.d" then we attempt to
4466  | create directories a, a.b and a.b.c if they don't
4467  | already exist.
4468  | Note that 'd' may be absent.
4469  */
ensure_path(char * p)4470 static int ensure_path(char *p)
4471 {
4472 	char tmp[260];
4473 	char *l = tmp;
4474 
4475 	while (*p)
4476 	{
4477 		if (*p == '.')
4478 		{
4479 			*l = 0;
4480 			if (SWI(5, 0, SWI_OS_File, 8, tmp, 0, 0, 77))
4481 				return 0;		/* Eeek! */
4482 		}
4483 		*l++ = *p++;
4484 	}
4485 
4486 	return 1;
4487 }
4488 
4489 
4490 /*
4491  * Set up the Scrap, Choices and Alarm paths, trying for
4492  * Choices:blah...,etc.  by preference, but falling back on lib/xtra
4493  * if need be.
4494  */
init_paths(void)4495 static void init_paths(void)
4496 {
4497 	char tmp[512];
4498 	char subpath[128];
4499 	char *v;
4500 	char *t;
4501 
4502 	/* Form the sub-path we use for both Choices and Scrap dirs: */
4503 	v = subpath + sprintf(subpath, "%s", VARIANT);
4504 	sanitise_name(subpath);
4505 	sprintf(v, ".%s", VERSION);
4506 	sanitise_name(v + 1);
4507 
4508 	/* Do the Scrap path first: */
4509 	*scrap_path = 0;
4510 
4511 	/* Try for Wimp$ScrapDir... */
4512 	t = getenv("Wimp$ScrapDir");
4513 	if (t && *t)
4514 	{
4515 		sprintf(tmp, "%s.AngbandEtc.%s.", t, subpath);
4516 		if (ensure_path(tmp))
4517 		{
4518 			strcpy(scrap_path, tmp);
4519 		}
4520 	}
4521 
4522 	/* Couldn't use Wimp$ScrapDir, so fall back on lib.xtra.scrap */
4523 	if (!*scrap_path)
4524 	{
4525 		sprintf(tmp, "%sxtra.scrap.", resource_path);
4526 		if (ensure_path(tmp))
4527 		{
4528 			strcpy(scrap_path, tmp);
4529 		}
4530 	}
4531 
4532 	/* Now set up the Choices and Alarm files: */
4533 
4534 	/* Read only Choices file is always lib.xtra.Choices */
4535 	sprintf(choices_file[CHFILE_READ], "%sXtra.Choices", resource_path);
4536 	/* Default writable Choices file is the same */
4537 	strcpy(choices_file[CHFILE_WRITE], choices_file[CHFILE_READ]);
4538 	/* No default mirror Choices file */
4539 	strcpy(choices_file[CHFILE_MIRROR], "");
4540 
4541 	/* Read only Alarm file is always  lib.xtra.Alarm */
4542 	sprintf(alarm_file[CHFILE_READ], "%sXtra.Alarm", resource_path);
4543 	/* Default writable Alarm file is the same */
4544 	strcpy(alarm_file[CHFILE_WRITE], alarm_file[CHFILE_READ]);
4545 
4546 	/* Try to use Choices$Path, etc. for the others... */
4547 
4548 	t = getenv("Choices$Write");	/* Ie. where choices should be written */
4549 	if (t && *t)
4550 	{
4551 		/* Choices file: */
4552 		sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
4553 		if (ensure_path(tmp))
4554 		{
4555 			/* Use for writable file: */
4556 			strcpy(choices_file[CHFILE_WRITE], tmp);
4557 			/* Form 'mirror' filename: same path but with a fixed leafname */
4558 			strcpy(v + 1, "Default");
4559 			sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
4560 			strcpy(choices_file[CHFILE_MIRROR], tmp);
4561 		}
4562 
4563 		/* Alarm file (doesn't involve subpath) */
4564 		sprintf(tmp, "%s.AngbandEtc.Global.Alarm", t);
4565 		if (ensure_path(tmp))
4566 		{
4567 			/* Use for read/writable file */
4568 			strcpy(alarm_file[CHFILE_WRITE], tmp);
4569 		}
4570 	}
4571 }
4572 
4573 
4574 
4575 
4576 
4577 
4578 
4579 /*
4580  * Return the appropriate (full) pathname.
4581  *
4582  * For write ops, the read/write file is returned.
4583  *
4584  * For read ops, either the read/write file, the mirror file,
4585  * or the read only file will be returned as appropriate.
4586  */
find_choices(int write)4587 static char *find_choices(int write)
4588 {
4589 	if (write)
4590 		return choices_file[CHFILE_WRITE];
4591 
4592 	if (myFile_Size(choices_file[CHFILE_WRITE]) > 0)
4593 		return choices_file[CHFILE_WRITE];
4594 
4595 	if (myFile_Size(choices_file[CHFILE_MIRROR]) > 0)
4596 		return choices_file[CHFILE_MIRROR];
4597 
4598 	return choices_file[CHFILE_READ];
4599 }
4600 
find_choices_mirror(void)4601 static char *find_choices_mirror(void)
4602 {
4603 	return choices_file[CHFILE_MIRROR];
4604 }
4605 
find_alarmfile(int write)4606 static char *find_alarmfile(int write)
4607 {
4608 	if (write)
4609 		return alarm_file[CHFILE_WRITE];
4610 
4611 	if (myFile_Size(alarm_file[CHFILE_WRITE]) > 0)
4612 		return alarm_file[CHFILE_WRITE];
4613 
4614 	return alarm_file[CHFILE_READ];
4615 }
4616 
4617 
4618 
4619 
main(int argc,char * argv[])4620 int main(int argc, char *argv[])
4621 {
4622 	int i, j;
4623 	int start_full = 0;
4624 	char *arg_savefile = 0;
4625 	char *t;
4626 #ifdef USE_DA
4627 	int da_font = 1, da_game = 1;
4628 #endif
4629 
4630 	atexit(final_acn);			/* "I never did care about the little things." */
4631 
4632 	Start_Hourglass;
4633 
4634 	/* Parse arguments */
4635 	for (i = 1; i < argc; i++)
4636 	{
4637 		if (argv[i][0] == '-')
4638 		{
4639 			switch (tolower((unsigned char)argv[i][1]))
4640 			{
4641 				case 'm':
4642 				{
4643 					/* Minimise Memory */
4644 					minimise_memory = 1;
4645 
4646 					/* Break */
4647 					break;
4648 				}
4649 				case 'c':		/* -c[a][s][f][<n>] */
4650 					for (j = 2; argv[i][j]; j++)
4651 					{
4652 						int on = isupper((unsigned char)argv[i][j]);
4653 
4654 						switch (tolower((unsigned char)argv[i][j]))
4655 						{
4656 #ifdef ABBR_FILECACHE
4657 							case 'a': abbr_filecache =
4658 									on;
4659 								break;
4660 							case 'f': abbr_tmpfile =
4661 									on;
4662 								break;
4663 #endif
4664 #ifdef SMART_FILECACHE
4665 							case 's': smart_filecache =
4666 									on;
4667 								break;
4668 #endif
4669 
4670 							case 'p': flush_scrap =
4671 									!on;
4672 								break;
4673 
4674 							default:
4675 								if (isdigit((unsigned char)argv[i][j]))
4676 								{
4677 									max_file_cache_size =
4678 										atoi(argv[i] + j) << 10;
4679 									while (isdigit((unsigned char)argv[i][++j]))
4680 										;
4681 									if (max_file_cache_size <= 0)
4682 									{
4683 										use_filecache = 0;
4684 									}
4685 									j--;
4686 								}
4687 								else
4688 								{
4689 									fprintf(stderr, "Unrecognised option: -c%s",
4690 											argv[i] + j);
4691 									exit(EXIT_FAILURE);
4692 								}
4693 						}
4694 					}
4695 					break;
4696 				case 'w':		/* -waitrelease */
4697 					hack_flush = 1;
4698 					break;
4699 				case 'e':		/* -Evil */
4700 					allow_iclear_hack = 1;
4701 					break;
4702 				case 's':		/* -s<savefile> */
4703 					if (argv[i][2])
4704 						arg_savefile = argv[i] + 2;
4705 					break;
4706 				case 'f':		/* -fullscreen */
4707 					start_full = 1;
4708 					break;
4709 				case 'h':		/* -hourglass */
4710 					use_glass = 1;
4711 					break;
4712 				case 't':		/* -T<filetype> */
4713 					if (argv[i][2])
4714 						vfiletype = htoi(argv[i] + 2);
4715 					break;
4716 #ifdef USE_DA
4717 				case 'd':		/* -df, -dg, -dc or -d : disable DAs */
4718 					switch (tolower((unsigned char)argv[i][2]))
4719 					{
4720 						case 0:	/* -d => disable both */
4721 							da_font = da_game = 0;
4722 							break;
4723 						case 'f':	/* -df => disable font only */
4724 							da_font = 0;
4725 							break;
4726 						case 'g':	/* -dg => disable game only */
4727 							da_game = 0;
4728 							break;
4729 					}
4730 					break;
4731 #endif
4732 				case '%':		/* -%<debug_opts> */
4733 				{
4734 					int v = read_unsigned(argv[i] + 2);
4735 					log_g_malloc = v & 1;
4736 					show_sound_alloc = v & 2;
4737 				}
4738 					break;
4739 				default:
4740 					fprintf(stderr, "Unrecognised option: %s", argv[i]);
4741 					exit(EXIT_FAILURE);
4742 			}
4743 		}
4744 	}
4745 
4746 	/* 1.27 - new handling of -minimise-memory: */
4747 #ifndef FULLSCREEN_ONLY
4748 	if (minimise_memory)
4749 #endif /* FULLSCREEN_ONLY */
4750 	{
4751 		start_full = 1;
4752 		fs_quit_key_text = "(fullscreen only mode)";
4753 	}
4754 #ifdef USE_DA
4755 	init_memory(da_font, da_game);	/* Set up dynamic areas, etc. if possible */
4756 
4757 	/* Install memory allocation hooks */
4758 	ralloc_aux = g_malloc;
4759 	rnfree_aux = g_free;
4760 #endif
4761 
4762 	/* Install replacement error reporting routines */
4763 	quit_aux = quit_hook;
4764 	plog_aux = plog_hook;
4765 	core_aux = core_hook;
4766 
4767 	/* Expand the (Angband) resource path */
4768 	t = getenv(RISCOS_VARIANT "$Path");
4769 	if (!t || !*t) Msgs_ReportFatal(0, "A resources path could not be formed.");
4770 	strcpy(resource_path, t);
4771 
4772 	/* Decide where scrap, choices and alarm files live: */
4773 	init_paths();
4774 
4775 	/* Hack: if no savefile specified, use a default */
4776 	if (!arg_savefile)
4777 	{
4778 		arg_savefile = malloc(strlen(resource_path) + 32);
4779 		if (!arg_savefile)
4780 		{
4781 			Msgs_ReportFatal(0, "err.mem");
4782 		}
4783 		sprintf(arg_savefile, "%s%s", resource_path, ">Save.Savefile");
4784 	}
4785 
4786 	/* This crap appears here so that plog() will work properly before
4787 	   init_acn() is called... */
4788 	Resource_Initialise(RISCOS_VARIANT);
4789 	Msgs_LoadFile("Messages");
4790 
4791 #ifndef FULLSCREEN_ONLY
4792 	if (!minimise_memory)
4793 	{
4794 		/* This is a hack to only call Event_Initialise3 under RO3 */
4795 		if (os_version() < 300)
4796 			Event_Initialise(RISCOS_VARIANT);
4797 		else
4798 			Event_Initialise3(RISCOS_VARIANT, 300, message_list);
4799 
4800 		EventMsg_Initialise();
4801 
4802 		/*
4803 		   | This is a possible workaround for the FP regs getting
4804 		   | bolloxed in the ! menu because the compiler sets them
4805 		   | up before a call to Wimp_Poll if CSE optimisation is on.
4806 		   | At the moment I've just turned off CSE for the function
4807 		   | affected.
4808 		   |
4809 		   | event_mask.data.keepfpregisters = 1;
4810 		 */
4811 
4812 		/* Load Templates */
4813 		Template_Initialise();
4814 		if (templates2d())
4815 			Template_LoadFile("Templates2");
4816 		else
4817 			Template_LoadFile("Templates");
4818 
4819 		/* Load Sprites */
4820 		if (sprites22())
4821 			resource_sprites =
4822 				Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites22");
4823 		else
4824 			resource_sprites = Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites");
4825 	}
4826 #endif /* FULLSCREEN_ONLY */
4827 
4828 	Screen_CacheModeInfo();
4829 
4830 	/* Initialise some ZapRedraw stuff */
4831 	initialise_palette();
4832 	initialise_fonts();			/* Set up the fonts */
4833 #ifndef FULLSCREEN_ONLY
4834 	initialise_r_data();		/* Set up the r_data buffer */
4835 
4836 	if (!minimise_memory)
4837 	{
4838 		/* Initialise some Wimp specific stuff */
4839 		init_gamma_window();
4840 		init_sound_window();
4841 		init_save_window();
4842 		init_menus();
4843 		ibar_icon = Icon_BarIcon(ICONNAME, iconbar_RIGHT);
4844 
4845 		/* Global handlers */
4846 		Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL);
4847 		Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL);
4848 		Event_Claim(event_GAINCARET, event_ANY, event_ANY, Hnd_Caret, (void *)1);
4849 		Event_Claim(event_LOSECARET, event_ANY, event_ANY, Hnd_Caret, (void *)0);
4850 		Event_Claim(event_MENU, event_ANY, event_ANY, Hnd_MenuSel, NULL);
4851 		Event_Claim(event_CLICK, window_ICONBAR, ibar_icon, Hnd_IbarClick, NULL);
4852 		Event_Claim(event_CLICK, event_ANY, event_ANY, Hnd_Click, NULL);
4853 		EventMsg_Claim(message_PALETTECHANGE, event_ANY, Hnd_PaletteChange, NULL);
4854 		EventMsg_Claim(message_MODECHANGE, event_ANY, Hnd_ModeChange, NULL);
4855 		EventMsg_Claim(message_PREQUIT, event_ANY, Hnd_PreQuit, NULL);
4856 
4857 		/* Initialise the sound stuff */
4858 		initialise_sound();
4859 	}
4860 #endif /* FULLSCREEN_ONLY */
4861 
4862 	/* Initialise some Angband stuff */
4863 	initialise_terms();
4864 	load_choices();
4865 	read_alarm_choices();
4866 	init_file_paths(unixify_name(resource_path));
4867 
4868 	Start_Hourglass;			/* Paranoia */
4869 
4870 	/* Hack - override the saved options if -F was on the command line */
4871 	start_fullscreen |= start_full;
4872 
4873 	/* hack so that the cursor is yellow if undefined */
4874 	if (palette[CURSOR_COLOUR] == palette[0])
4875 	{
4876 		angband_color_table[CURSOR_COLOUR][1] = (CURSOR_RGB & 0xff00) >> 8;
4877 		angband_color_table[CURSOR_COLOUR][2] = (CURSOR_RGB & 0xff0000) >> 16;
4878 		angband_color_table[CURSOR_COLOUR][3] = (CURSOR_RGB & 0xff000000) >> 24;
4879 	}
4880 
4881 	/* Catch nasty signals */
4882 	signals_init();
4883 	/* use pref-acn.prf */
4884 	ANGBAND_SYS = "acn";
4885 
4886 #ifndef FULLSCREEN_ONLY
4887 	if (start_fullscreen)
4888 	{
4889 #endif /* FULLSCREEN_ONLY */
4890 		enter_fullscreen_mode();
4891 #ifndef FULLSCREEN_ONLY
4892 	}
4893 	else
4894 	{
4895 		Start_Hourglass;		/* Paranoia */
4896 		Hnd_ModeChange(NULL, NULL);	/* Caches the various fonts/palettes */
4897 		show_windows();
4898 		grab_caret();
4899 
4900 		Event_Claim(event_NULL, event_ANY, event_ANY, Hnd_null, NULL);
4901 
4902 		/* Wait for a null poll so that the windows can appear */
4903 		do
4904 		{
4905 			Event_Poll();
4906 		}
4907 		while (event_lastevent.type != 0);
4908 	}
4909 #endif /* FULLSCREEN_ONLY */
4910 
4911 	/* Initialise Angband */
4912 	Start_Hourglass;			/* Paranoia */
4913 
4914 	strncpy(savefile, unixify_name(arg_savefile), sizeof(savefile));
4915 	savefile[sizeof(savefile) - 1] = '\0';
4916 
4917 	use_sound = 1;
4918 	init_angband();
4919 	initialised = 1;
4920 	game_in_progress = 1;
4921 	pause_line(23);
4922 	flush();
4923 	/* Stop_Hourglass; */
4924 	play_game(FALSE);
4925 
4926 	if (fullscreen_mode) leave_fullscreen_mode();
4927 
4928 	Stop_Hourglass;
4929 
4930 	quit(NULL);
4931 
4932 	return 0;
4933 
4934 	debug("to stop the 'unused' warning :)");
4935 }
4936 
4937 
4938 
4939 
4940 
4941 
4942 
4943 
4944 
4945 
4946 
4947 
4948 
4949 /*--------------------------------------------------------------------------*/
4950 /* Stuff below here is for the full screen display							*/
4951 /*--------------------------------------------------------------------------*/
4952 
4953 static errr Term_xtra_acn_checkFS(void);
4954 static errr Term_xtra_acn_eventFS(void);
4955 static errr Term_xtra_acn_reactFS(int force);
4956 static errr Term_curs_acnFS(int x, int y);
4957 static errr Term_xtra_acn_clearFS(void);
4958 static errr Term_xtra_acnFS(int n, int v);
4959 static errr Term_wipe_acnFS(int x, int y, int n);
4960 static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
4961 static void bored(void);
4962 static void redraw_areaFS(int x, int y, int w, int h);
4963 static void draw_cursor(int x, int y);
4964 
4965 /*
4966  | We use this to keep the mouse in the same place on return from full-screen
4967  | mode.
4968  */
4969 static unsigned char old_mouse_posn[5];
4970 
4971 /*
4972  | Take a copy of the current mode descriptor/number and return either
4973  | a pointer to it (as an int) if it's a new mode, or the mode number.
4974  | NB: A static pointer is used and the descriptor returned is only
4975  | valid until the next call to this function.
4976  |
4977  | Basically, a replacement for OS_Byte 135 / OS_ScreenMode1, IYSWIM.
4978  */
current_mode(void)4979 static int current_mode(void)
4980 {
4981 	static void *descriptor = NULL;
4982 	int mode;
4983 	int size;
4984 	int i;
4985 	int *vals;
4986 
4987 	if (descriptor)
4988 	{
4989 		free(descriptor);
4990 		descriptor = NULL;
4991 	}
4992 
4993 	SWI(1, 3, SWI_OS_Byte, 135, NULL, NULL, &mode);
4994 	if (mode < 256)
4995 	{
4996 		return mode;
4997 	}
4998 
4999 	vals = (int *)(mode + 20);
5000 	for (i = 0; vals[i] != -1; i += 2)
5001 		;
5002 
5003 	size = 24 + 8 * i;			/* Size of data */
5004 	descriptor = malloc(size);
5005 	if (!descriptor)
5006 	{
5007 		core("Out of memory!");
5008 	}
5009 	memcpy(descriptor, (void *)mode, size);
5010 
5011 	return (int)descriptor;
5012 }
5013 
5014 
5015 
5016 /*
5017  | Select the best mode we can for full screen.
5018  | Returns 12 for (low-res, ie. mode 12) or 27 for high-res,
5019  | or a pointer to a mode descriptor (as an int).
5020  */
select_fullscreen_mode(void)5021 static int select_fullscreen_mode(void)
5022 {
5023 	static struct
5024 	{
5025 		int flags, x, y, l2bpp, hz, term;
5026 	}
5027 	desc;
5028 	int mode = 0;
5029 
5030 	desc.flags = 1;				/* format 0 */
5031 	desc.x = 640;
5032 	desc.y = 480;				/* 640x480 */
5033 	desc.l2bpp = 2;				/* 16 colours */
5034 	desc.hz = -1;				/* best we can get */
5035 	desc.term = -1;	/* don't fuss about modevars */
5036 
5037 	SWI(1, 1, SWI_OS_CheckModeValid, &desc, &mode);
5038 	if (mode != (int)&desc)
5039 	{
5040 		SWI(1, 1, SWI_OS_CheckModeValid, 27, /**/ &mode);
5041 		if (mode != 27)
5042 		{
5043 			SWI(1, 1, SWI_OS_CheckModeValid, 12, /**/ &mode);
5044 			if (mode != 12)
5045 			{
5046 				mode = 0;
5047 			}
5048 		}
5049 	}
5050 
5051 	return mode;
5052 }
5053 
5054 
5055 /*
5056  | Change screen mode
5057  */
change_screenmode(int to)5058 static void change_screenmode(int to)
5059 {
5060 	if (SWI(2, 0, SWI_OS_ScreenMode, 0, to))
5061 	{
5062 		if (to < 256)
5063 		{
5064 			GFX_VDU(22);
5065 			GFX_VDU(to);
5066 		}
5067 		else
5068 		{
5069 			/* Finished with my woman / cos she couldn't help me with ... */
5070 			core("Eeek! mode isn't valid, but it /should/ be...");
5071 		}
5072 	}
5073 }
5074 
5075 
5076 /*
5077  | Constrain the mouse pointer to a point - this means that the damn
5078  | hourglass won't move around with the mouse :)
5079  */
constrain_pointer(void)5080 static void constrain_pointer(void)
5081 {
5082 	wimp_rect r;
5083 	int ys = screen_eig.y == 1 ? 32 : 64;	/* Cope with dbl height glass */
5084 
5085 	Screen_CacheModeInfo();		/* Make sure we know the screen size */
5086 	r.min.x = r.max.x = screen_size.x - 32;
5087 	r.min.y = r.max.y = screen_size.y - ys;
5088 	Pointer_RestrictToRect(r);
5089 }
5090 
5091 
5092 
5093 
5094 /*
5095  | Convert a 1bpp bitmap into a 4bpp bitmap (bit flipped)
5096  */
byte_to_word_flipped(int b)5097 static int byte_to_word_flipped(int b)
5098 {
5099 	int w;
5100 	if (b & 128)
5101 	{
5102 		w = 0xf0000000;
5103 	}
5104 	else
5105 	{
5106 		w = 0;
5107 	}
5108 	if (b & 64)
5109 	{
5110 		w |= 0x0f000000;
5111 	}
5112 	if (b & 32)
5113 	{
5114 		w |= 0x00f00000;
5115 	}
5116 	if (b & 16)
5117 	{
5118 		w |= 0x000f0000;
5119 	}
5120 	if (b & 8)
5121 	{
5122 		w |= 0x0000f000;
5123 	}
5124 	if (b & 4)
5125 	{
5126 		w |= 0x00000f00;
5127 	}
5128 	if (b & 2)
5129 	{
5130 		w |= 0x000000f0;
5131 	}
5132 	if (b & 1)
5133 	{
5134 		w |= 0x0000000f;
5135 	}
5136 	return w;
5137 }
5138 
5139 
5140 
5141 
5142 /*
5143  | try to load the fallback fullscreen font and convert it to 4bpp
5144  */
cache_zapfontHR(void)5145 static int cache_zapfontHR(void)
5146 {
5147 	int handle;
5148 	unsigned int extent;
5149 	char buffer[260];
5150 	struct
5151 	{
5152 		char id[8];
5153 		int w, h, f, l, r1, r2;
5154 	}
5155 	zfh;
5156 	int *op;
5157 	char *ip;
5158 	int l, i;
5159 
5160 	/* Try to open the file */
5161 	sprintf(buffer, "%s%s", resource_path, "xtra.FullScreen");
5162 	handle = myFile_Open(buffer, 0x4f);
5163 	if (!handle)
5164 	{
5165 		return 0;
5166 	}
5167 
5168 	/* Check file's extent */
5169 	extent = myFile_Extent(handle);
5170 	if (extent > sizeof(zfh) + 256 * 16)
5171 	{
5172 		myFile_Close(handle);
5173 		return 0;
5174 	}
5175 
5176 	/* Load the header */
5177 	if (myFile_ReadBytes(handle, &zfh, sizeof(zfh)))
5178 	{
5179 		myFile_Close(handle);
5180 		return 0;
5181 	}
5182 
5183 	/* Check font size */
5184 	if ((zfh.w != 8) || (zfh.h > 16))
5185 	{
5186 		myFile_Close(handle);
5187 		return 0;
5188 	}
5189 
5190 	/* Load the 1bpp data */
5191 	if (myFile_ReadBytes(handle, fullscreen_font, extent - sizeof(zfh)))
5192 	{
5193 		myFile_Close(handle);
5194 		return 0;
5195 	}
5196 
5197 	myFile_Close(handle);
5198 
5199 	l = zfh.l > 255 ? 255 : zfh.l;
5200 
5201 	if (zfh.h > 8)
5202 	{
5203 		op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 4);
5204 		ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
5205 					  (zfh.f * zfh.h));
5206 		while (l-- >= zfh.f)
5207 		{
5208 			for (i = 0; i < zfh.h; i++)
5209 			{
5210 				*--op = byte_to_word_flipped(*--ip);
5211 			}
5212 		}
5213 		fullscreen_height = zfh.h;
5214 	}
5215 	else
5216 	{
5217 		op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 8);
5218 		ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
5219 					  (zfh.f * zfh.h));
5220 		while (l-- >= zfh.f)
5221 		{
5222 			for (i = -zfh.h; i < zfh.h; i++)
5223 			{
5224 				int t = byte_to_word_flipped(*--ip);
5225 				*--op = t;
5226 				*--op = t;
5227 			}
5228 		}
5229 		fullscreen_height = zfh.h * 2;
5230 	}
5231 
5232 	fullscreen_topline = TERM_TOPLINE_HR;
5233 	fullscreen_topline += ((16 - fullscreen_height) * 13);
5234 
5235 	return 1;
5236 }
5237 
5238 
5239 
5240 
5241 
cache_fs_fontHR(void)5242 static int cache_fs_fontHR(void)
5243 {
5244 	ZapFont *src = SYSTEM_FONT;
5245 	int c;
5246 	int *op;
5247 	char *ip;
5248 
5249 	/* Allocate the storage for the font */
5250 	fullscreen_font = f_malloc(256 * 4 * 16);
5251 	if (!fullscreen_font)
5252 	{
5253 		return 0;
5254 	}
5255 	op = (int *)fullscreen_font;
5256 
5257 	/* Check to see if the main term's font is suitable (ie. 8x16 or 8x8) */
5258 	if ((data[0].font->w == 8) && (data[0].font->h <= 16))
5259 		src = data[0].font;
5260 
5261 	/*
5262 	   | Hack: if we're forced to use the system font, try to load the
5263 	   | 'fullscreen' font from lib.xtra.  If that fails, then I guess we're
5264 	   | stuck with the system font.
5265 	 */
5266 
5267 	if (src == SYSTEM_FONT)
5268 		if (cache_zapfontHR())
5269 		{
5270 			return 1;
5271 		}
5272 
5273 	ip = (char *)(src->bpp_1);
5274 
5275 	/* Now, create the font */
5276 	if (src->h > 8)
5277 	{
5278 		int e = src->h * (src->l > 256 ? 256 : src->l);
5279 		op += (src->f * src->h);
5280 		for (c = src->f * src->h; c < e; c++)
5281 			*op++ = byte_to_word_flipped(*ip++);
5282 		fullscreen_height = src->h;
5283 	}
5284 	else
5285 	{
5286 		int e = src->h * (src->l > 256 ? 256 : src->l);
5287 		op += (src->f * src->h) * 2;
5288 		for (c = src->f * src->h; c < e; c++)
5289 		{
5290 			int t = byte_to_word_flipped(*ip++);
5291 			*op++ = t;
5292 			*op++ = t;
5293 		}
5294 		fullscreen_height = src->h * 2;
5295 	}
5296 
5297 	fullscreen_topline = TERM_TOPLINE_HR;
5298 	fullscreen_topline += ((16 - fullscreen_height) * 13);
5299 
5300 	return 1;
5301 }
5302 
5303 
5304 
5305 
5306 
cache_fs_fontLR(void)5307 static int cache_fs_fontLR(void)
5308 {
5309 	ZapFont *src = SYSTEM_FONT;
5310 	int c, e;
5311 	int *op;
5312 	char *ip;
5313 
5314 	/* Allocate the storage for the font */
5315 	fullscreen_font = f_malloc(256 * 4 * 8);
5316 	if (!fullscreen_font)
5317 	{
5318 		return 0;
5319 	}
5320 	op = (int *)fullscreen_font;
5321 
5322 	/* Check to see if the main term's font is suitable (ie. 8x8) */
5323 	if ((data[0].font->w == 8) && (data[0].font->h <= 8))
5324 		src = data[0].font;
5325 
5326 	ip = (char *)(src->bpp_1);
5327 
5328 	/* Now, create the font */
5329 	e = src->h * (src->l > 256 ? 256 : src->l);
5330 	op += (src->f * src->h);
5331 	for (c = src->f * src->h; c < e; c++)
5332 		*op++ = byte_to_word_flipped(*ip++);
5333 
5334 	fullscreen_height = src->h;
5335 	fullscreen_topline = TERM_TOPLINE_LR;
5336 	fullscreen_topline += ((8 - fullscreen_height) * 13);
5337 
5338 	return 1;
5339 }
5340 
5341 
5342 
set_keys(int claim)5343 static void set_keys(int claim)
5344 {
5345 	static int old_c_state;
5346 	static int old_f_state[8];
5347 	int i;
5348 
5349 	if (claim)
5350 	{
5351 		/* Cursors/copy act as function keys */
5352 		/* f0-f9, cursors, generate 0x80-0x8f */
5353 		/* sh-f0-f9,cursors, generate 0x90-0x9f */
5354 		/* ctrl f0-f9,cursors, generate 0xa0-0xaf */
5355 		/* sh-c-f0-f9,cursors, generate 0xb0-0xbf */
5356 		/* f10-f12 generate 0xca-0xcc */
5357 		/* shift f10-f12 generate 0xda-0xdc */
5358 		/* ctrl f10-f12 generate 0xea-0xec */
5359 		/* ctrlshift f10-f12 generate 0xfa-0xfc */
5360 
5361 		SWI(3, 2, SWI_OS_Byte, 4, 2, 0, /**/ NULL, &old_c_state);
5362 
5363 		for (i = 0; i < 4; i++)
5364 		{
5365 			SWI(3, 2, SWI_OS_Byte, 225 + i, 0x80 + (i * 0x10), 0, NULL,
5366 				old_f_state + i);
5367 			SWI(3, 2, SWI_OS_Byte, 221 + i, 0xc0 + (i * 0x10), 0, NULL,
5368 				old_f_state + i + 4);
5369 		}
5370 	}
5371 	else
5372 	{
5373 		SWI(3, 0, SWI_OS_Byte, 4, old_c_state, 0);
5374 		for (i = 0; i < 4; i++)
5375 		{
5376 			SWI(3, 0, SWI_OS_Byte, 225 + i, old_f_state[i], 0);
5377 			SWI(3, 0, SWI_OS_Byte, 221 + i, old_f_state[i + 4], 0);
5378 		}
5379 	}
5380 }
5381 
5382 
5383 
5384 
5385 
5386 
5387 /*
5388  | Enter the full screen mode.
5389  |
5390  | Full screen display uses either mode 27 (if supported) and 8x16 fonts
5391  | (or system font 'twiddled' to double height), or mode 12 (if mode 27
5392  | is unavailable) and the system font (or an 8x8 font).
5393  |
5394  */
5395 
enter_fullscreen_mode(void)5396 static void enter_fullscreen_mode(void)
5397 {
5398 	int vduvars[2] =
5399 	{ 149, -1 };
5400 	int i;
5401 
5402 	/* New in 1.18 - protect against 're-entracy' */
5403 	if (fullscreen_font)
5404 		return;
5405 
5406 	/* New in 1.20 - hack IClear out of the way */
5407 	if (allow_iclear_hack)
5408 		iclear_hack();
5409 
5410 	/* Remember where the mouse is */
5411 	old_mouse_posn[0] = 4;
5412 	SWI(2, 0, SWI_OS_Word, 21, &old_mouse_posn);
5413 
5414 	/* Choose the mode we want */
5415 	fullscreen_mode = select_fullscreen_mode();
5416 
5417 	if (!fullscreen_mode)
5418 	{
5419 		plog("Unable to select a suitable screen mode (27 or 12)");
5420 		return;
5421 	}
5422 
5423 	if (!((fullscreen_mode == 12) ? cache_fs_fontLR() : cache_fs_fontHR()))
5424 	{
5425 		plog("Unable to cache a font for full screen mode");
5426 		return;
5427 	}
5428 
5429 	/* Read the current screen mode */
5430 	/* SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &old_screenmode ); */
5431 	old_screenmode = current_mode();
5432 
5433 	Stop_Hourglass;
5434 
5435 	/* Change to the chosen screen mode */
5436 	change_screenmode(fullscreen_mode);
5437 
5438 	/* Restrict the pointer */
5439 	constrain_pointer();
5440 
5441 	/* Remove the cursors */
5442 	SWI(0, 0, SWI_OS_RemoveCursors);
5443 
5444 	Start_Hourglass;
5445 
5446 	/* Get the base address of screen memory */
5447 	SWI(2, 0, SWI_OS_ReadVduVariables, vduvars, vduvars);
5448 	fullscreen_base = (int *)(vduvars[0]);
5449 
5450 	/* Fudge the Term interface */
5451 	for (i = 0; i < MAX_TERM_DATA; i++)
5452 	{
5453 		term *t = &(data[i].t);
5454 		t->xtra_hook = Term_xtra_acnFS;
5455 		t->wipe_hook = Term_wipe_acnFS;
5456 		t->curs_hook = Term_curs_acnFS;
5457 		t->text_hook = Term_text_acnFS;
5458 	}
5459 
5460 	/* Grab the palette */
5461 	Term_xtra_acn_reactFS(TRUE);
5462 
5463 	/* Make sure that the keys work properly */
5464 	set_keys(TRUE);
5465 
5466 	/* refresh the term */
5467 	/*Term_activate( &(data[0].t) ); */
5468 	redraw_areaFS(0, 0, 80, 24);
5469 	if (data[0].cursor.visible)
5470 		draw_cursor(data[0].cursor.pos.x, data[0].cursor.pos.y);
5471 
5472 	/* Display a reminder of how to get back... */
5473 	/* Hack: disable force_mono */
5474 	i = force_mono;
5475 	force_mono = 0;
5476 	Term_text_acnFS(0, TIME_LINE, strlen(fs_quit_key_text), 8,
5477 					fs_quit_key_text);
5478 	force_mono = i;
5479 }
5480 
5481 
5482 
5483 
leave_fullscreen_mode(void)5484 static void leave_fullscreen_mode(void)
5485 {
5486 	int i;
5487 
5488 	/* New in 1.18 - protect against 're-entracy' */
5489 	if (!fullscreen_font)
5490 		return;
5491 
5492 	/* Restore the Term interface */
5493 	for (i = 0; i < MAX_TERM_DATA; i++)
5494 	{
5495 #ifndef FULLSCREEN_ONLY
5496 		term *t = &(data[i].t);
5497 		t->xtra_hook = Term_xtra_acn;
5498 		t->wipe_hook = Term_wipe_acn;
5499 		t->curs_hook = Term_curs_acn;
5500 		t->text_hook = Term_text_acn;
5501 #endif
5502 		mark_ood(&(data[i]), 0, 0, 80, 24);
5503 	}
5504 
5505 	/* Deallocate the font */
5506 	f_free(fullscreen_font);
5507 	fullscreen_font = 0;
5508 	fullscreen_mode = 0;
5509 
5510 	Stop_Hourglass;
5511 
5512 	/* Restore the screen mode */
5513 	Wimp_SetMode(old_screenmode);
5514 
5515 	/* Restore the pointer position */
5516 	old_mouse_posn[0] = 3;
5517 	SWI(2, 0, SWI_OS_Word, 21, &old_mouse_posn);
5518 
5519 	Start_Hourglass;
5520 
5521 	/* Restore the various soft keys */
5522 	set_keys(FALSE);
5523 
5524 	/* New in 1.20 Remove the IClear hack */
5525 	if (allow_iclear_hack)
5526 		remove_iclear_hack();
5527 
5528 #ifndef FULLSCREEN_ONLY
5529 	/* Refresh the windows - this probably isn't necessary anyway */
5530 	if (!minimise_memory)
5531 		refresh_windows();
5532 #endif /* FULLSCREEN_ONLY */
5533 }
5534 
5535 
5536 
5537 
5538 
fs_writechars(int x,int y,int n,const char * chars,char attr)5539 static void fs_writechars(int x, int y, int n, const char *chars, char attr)
5540 {
5541 	int *scr, *scrb;
5542 	int *cdat;
5543 	int j;
5544 	unsigned int fgm;
5545 
5546 	if (force_mono)
5547 	{
5548 		if (attr != TERM_DARK)
5549 		{
5550 			attr = TERM_WHITE;
5551 		}
5552 	}
5553 	fgm = (unsigned int)zpalette[(unsigned int) attr];
5554 
5555 	scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
5556 				   + x * 4 + 320 * fullscreen_topline);
5557 
5558 	while (n--)
5559 	{
5560 		scr = scrb++;
5561 		cdat = (int *)(((int)fullscreen_font)
5562 					   + (*chars++) * (fullscreen_height << 2));
5563 		for (j = 0; j < fullscreen_height; j++)
5564 		{
5565 			*scr = *cdat++ & fgm;
5566 			scr += 80;
5567 		}
5568 	}
5569 }
5570 
5571 
fs_writechar(int x,int y,char c,char attr)5572 static void fs_writechar(int x, int y, char c, char attr)
5573 {
5574 	int *scrb;
5575 	int *cdat;
5576 	int j;
5577 	unsigned int fgm;
5578 
5579 	if (force_mono)
5580 	{
5581 		if (attr != TERM_DARK)
5582 		{
5583 			attr = TERM_WHITE;
5584 		}
5585 	}
5586 	fgm = (unsigned int)zpalette[(unsigned int) attr];
5587 
5588 	scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
5589 				   + x * 4 + 320 * fullscreen_topline);
5590 	cdat = (int *)(((int)fullscreen_font) + (c * (fullscreen_height << 2)));
5591 	for (j = 0; j < fullscreen_height; j++)
5592 	{
5593 		*scrb = *cdat++ & fgm;
5594 		scrb += 80;
5595 	}
5596 }
5597 
5598 
5599 
draw_cursorHR(int x,int y)5600 static void draw_cursorHR(int x, int y)
5601 {
5602 	ColourTrans_SetGCOL(cursor_rgb, 0, 0);
5603 	GFX_Move(x * 16,
5604 			 959 - y * (fullscreen_height * 2) - fullscreen_topline * 2);
5605 	GFX_DrawBy(14, 0);
5606 	GFX_DrawBy(0, -(fullscreen_height * 2 - 2));
5607 	GFX_DrawBy(-14, 0);
5608 	GFX_DrawBy(0, fullscreen_height * 2 - 2);
5609 }
5610 
draw_cursorLR(int x,int y)5611 static void draw_cursorLR(int x, int y)
5612 {
5613 	ColourTrans_SetGCOL(cursor_rgb, 0, 0);
5614 	GFX_Move(x * 16,
5615 			 1023 - y * (fullscreen_height * 4) - fullscreen_topline * 4);
5616 	GFX_DrawBy(14, 0);
5617 	GFX_DrawBy(0, -(fullscreen_height * 4 - 4));
5618 	GFX_DrawBy(-14, 0);
5619 	GFX_DrawBy(0, fullscreen_height * 4 - 4);
5620 }
5621 
5622 
5623 
5624 
5625 
draw_cursor(int x,int y)5626 static void draw_cursor(int x, int y)
5627 {
5628 	if (fullscreen_mode == 12)
5629 		draw_cursorLR(x, y);
5630 	else
5631 		draw_cursorHR(x, y);
5632 }
5633 
5634 
5635 
redraw_areaFS(int x,int y,int w,int h)5636 static void redraw_areaFS(int x, int y, int w, int h)
5637 {
5638 	int i, j;
5639 	for (j = y; j < y + h; j++)
5640 		for (i = x; i < x + w; i++)
5641 			fs_writechar(i, j, data[0].t.old->c[j][i], data[0].t.old->a[j][i]);
5642 }
5643 
5644 
5645 
wimp_code(int c)5646 static int wimp_code(int c)
5647 {
5648 	/* shift/ctrl keypad? */
5649 	if (c >= '0' && c <= '9')
5650 	{
5651 		kbd_modifiers m = Kbd_GetModifiers(FALSE);
5652 		if (m.shift)
5653 		{
5654 			c |= 0x800;
5655 		}
5656 		if (m.ctrl)
5657 		{
5658 			c |= 0x400;
5659 		}
5660 		return c;
5661 	}
5662 	if (c == 9)
5663 	{
5664 		return 0x18a;
5665 	}							/* Tab */
5666 	if (c <= 127)
5667 	{
5668 		return c;
5669 	}							/* normal ASCII/ctrl */
5670 	if (c >= 0x80 && c <= 0xff)
5671 	{
5672 		return c + 0x100;
5673 	}							/* f0-f9, etc. */
5674 
5675 	return -1;					/* unknown */
5676 }
5677 
5678 
5679 
5680 
do_keypress(int code)5681 static void do_keypress(int code)
5682 {
5683 	static const char hex[] = "0123456789ABCDEF";
5684 
5685 	if (code == KEYPRESS_QUIT && !minimise_memory)
5686 	{
5687 #ifdef FULLSCREEN_ONLY
5688 		Sound_SysBeep();
5689 #else
5690 		leave_fullscreen_mode();
5691 #endif
5692 		return;
5693 	}
5694 
5695 	if (code == 27)
5696 	{
5697 		if (Kbd_KeyDown(inkey_CTRL))
5698 		{
5699 			ack_alarm();
5700 			return;
5701 		}
5702 	}
5703 
5704 	if (code <= 255)
5705 	{
5706 		Term_keypress(code);
5707 	}
5708 	else
5709 	{
5710 		Term_keypress(31);
5711 		Term_keypress(hex[(code & 0xf00) >> 8]);
5712 		Term_keypress(hex[(code & 0x0f0) >> 4]);
5713 		Term_keypress(hex[(code & 0x00f)]);
5714 		Term_keypress(13);
5715 	}
5716 }
5717 
5718 
5719 
5720 
Term_xtra_acn_checkFS(void)5721 static errr Term_xtra_acn_checkFS(void)
5722 {
5723 	int bh, bl;
5724 	int c;
5725 
5726 	Stop_Hourglass;
5727 
5728 	bored();
5729 
5730 	SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
5731 	bl = (bl & 0xff) + (bh << 8);
5732 
5733 	if (bl > 0)
5734 	{
5735 		SWI(0, 1, SWI_OS_ReadC, &c);
5736 		bl = wimp_code(c);
5737 		if (bl >= 0)
5738 		{
5739 			do_keypress(bl);
5740 		}
5741 	}
5742 
5743 	Start_Hourglass;
5744 
5745 	return 0;
5746 }
5747 
5748 
5749 
5750 
Term_xtra_acn_eventFS(void)5751 static errr Term_xtra_acn_eventFS(void)
5752 {
5753 	int c;
5754 	int w = -1;
5755 
5756 	Stop_Hourglass;
5757 
5758 	for (w = -1; w == -1;)
5759 	{
5760 		int bh, bl;
5761 		do
5762 		{
5763 			bored();
5764 			SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
5765 			bl = (bl & 0xff) + (bh << 8);
5766 		}
5767 		while (!bl);
5768 
5769 		SWI(0, 1, SWI_OS_ReadC, &c);
5770 		w = wimp_code(c);
5771 		if (w >= 0)
5772 		{
5773 			do_keypress(w);
5774 		}
5775 	}
5776 
5777 	Start_Hourglass;
5778 
5779 	return 0;
5780 }
5781 
5782 
5783 
5784 /*
5785  * React to changes
5786  */
Term_xtra_acn_reactFS(int force)5787 static errr Term_xtra_acn_reactFS(int force)
5788 {
5789 	unsigned int i;
5790 	int p, r, g, b;
5791 
5792 	static double old_gamma = -1.0;
5793 
5794 	if (gamma != old_gamma)
5795 	{
5796 		force = 1;
5797 		old_gamma = gamma;
5798 	}
5799 
5800 	/* Set the screen colours */
5801 	for (i = 0; i < 16; i++)
5802 	{
5803 		if (COLOUR_CHANGED(i) || force)
5804 		{
5805 			r = (int)(255.0 *
5806 					  pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
5807 			g = (int)(255.0 *
5808 					  pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
5809 			b = (int)(255.0 *
5810 					  pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
5811 			GFX_VDU(19);
5812 			GFX_VDU(i);
5813 			GFX_VDU(16);
5814 			GFX_VDU(r);
5815 			GFX_VDU(g);
5816 			GFX_VDU(b);
5817 
5818 			palette[i] = (b << 24) | (g << 16) | (r << 8);
5819 			p = i;
5820 			p |= (p << 4);
5821 			p |= (p << 8);
5822 			p |= (p << 16);
5823 			zpalette[i] = p;
5824 
5825 			a_palette[i][1] = angband_color_table[i][1];
5826 			a_palette[i][2] = angband_color_table[i][2];
5827 			a_palette[i][3] = angband_color_table[i][3];
5828 
5829 			/* Find any higher colour numbers and make them "wrong" */
5830 			for (p = 16; p < 256; p++)
5831 				if ((zpalette[p] & 0xf) == i)
5832 					a_palette[p][1] = angband_color_table[p][1] + 2;
5833 		}
5834 	}
5835 
5836 
5837 	/* Go through the palette updating any changed values */
5838 	for (i = 16; i < 256; i++)
5839 	{
5840 		if (COLOUR_CHANGED(i) || force)
5841 		{
5842 			r = (int)(255.0 *
5843 					  pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
5844 			g = (int)(255.0 *
5845 					  pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
5846 			b = (int)(255.0 *
5847 					  pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
5848 			p = (b << 24) | (g << 16) | (r << 8);
5849 			palette[i] = p;
5850 			SWI(1, 1, SWI_ColourTrans_ReturnColourNumber, palette[i], &p);
5851 			p |= (p << 4);
5852 			p |= (p << 8);
5853 			p |= (p << 16);
5854 			zpalette[i] = p;
5855 			a_palette[i][1] = angband_color_table[i][1];
5856 			a_palette[i][2] = angband_color_table[i][2];
5857 			a_palette[i][3] = angband_color_table[i][3];
5858 		}
5859 	}
5860 
5861 	cursor_rgb = palette[CURSOR_COLOUR];
5862 
5863 	return 0;
5864 }
5865 
5866 
Term_curs_acnFS(int x,int y)5867 static errr Term_curs_acnFS(int x, int y)
5868 {
5869 	if (Term == &(data[0].t))
5870 	{
5871 		if (data[0].cursor.visible)
5872 			redraw_areaFS(data[0].cursor.pos.x, data[0].cursor.pos.y, 1, 1);
5873 		data[0].cursor.pos.x = x;
5874 		data[0].cursor.pos.y = y;
5875 		if (data[0].cursor.visible)
5876 			draw_cursor(x, y);
5877 	}
5878 	return 0;
5879 }
5880 
Term_xtra_acn_clearFS(void)5881 static errr Term_xtra_acn_clearFS(void)
5882 {
5883 	char e[80];
5884 	int j;
5885 
5886 	if (Term == &(data[0].t))
5887 	{
5888 		for (j = 0; j < 80; j++)
5889 			e[j] = ' ';
5890 
5891 		GFX_Wait();
5892 
5893 		for (j = 0; j < 24; j++)
5894 			fs_writechars(0, j, 80, e, 0);
5895 	}
5896 
5897 	return 0;
5898 }
5899 
5900 
5901 
5902 
5903 
5904 
5905 
Term_xtra_acnFS(int n,int v)5906 static errr Term_xtra_acnFS(int n, int v)
5907 {
5908 	term_data *t = (term_data *)Term;
5909 
5910 	switch (n)
5911 	{
5912 		case TERM_XTRA_EVENT:
5913 			if (v)
5914 				return Term_xtra_acn_eventFS();
5915 			else
5916 				return Term_xtra_acn_checkFS();
5917 
5918 		case TERM_XTRA_BORED:
5919 			bored();
5920 			return Term_xtra_acn_checkFS();
5921 
5922 		case TERM_XTRA_FLUSH:
5923 			/* 1.21 - Hack: wait until no keys are pressed */
5924 			if (hack_flush)
5925 				for (v = 0; v != 0xff;)
5926 					SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
5927 			SWI(3, 0, SWI_OS_Byte, 21, 0, 0);	/* Flush Kbd buffer */
5928 			return 0;
5929 
5930 		case TERM_XTRA_FRESH:
5931 			return 0;
5932 
5933 		case TERM_XTRA_FROSH:
5934 			return 0;
5935 
5936 		case TERM_XTRA_SHAPE:
5937 			if (t == (&data[0]))
5938 			{
5939 				t->cursor.visible = v;
5940 				if (v)
5941 					draw_cursor(t->cursor.pos.x, t->cursor.pos.y);
5942 				else
5943 					redraw_areaFS(t->cursor.pos.x, t->cursor.pos.y, 1, 1);
5944 			}
5945 			return 0;
5946 
5947 		case TERM_XTRA_NOISE:
5948 			Sound_SysBeep();
5949 			return 0;
5950 
5951 		case TERM_XTRA_REACT:
5952 			return Term_xtra_acn_reactFS(FALSE);
5953 
5954 		case TERM_XTRA_DELAY:
5955 			if (v > 0)
5956 			{
5957 				unsigned int start = Time_Monotonic();
5958 				v = (v + 5) / 10;	/* Round to nearest cs */
5959 				GFX_Wait();
5960 				while ((Time_Monotonic() - start) < v)
5961 					;
5962 			}
5963 			return (0);
5964 
5965 		case TERM_XTRA_SOUND:	/* Play a sound :) */
5966 			if (enable_sound)
5967 			{
5968 				play_sound(v);
5969 			}
5970 			return 0;
5971 
5972 		default:
5973 			return 1;
5974 	}
5975 }
5976 
Term_wipe_acnFS(int x,int y,int n)5977 static errr Term_wipe_acnFS(int x, int y, int n)
5978 {
5979 	if (Term == &(data[0].t))
5980 		while (n--)
5981 			fs_writechar(x++, y, ' ', 0);
5982 	return 0;
5983 }
5984 
Term_text_acnFS(int x,int y,int n,byte a,cptr s)5985 static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s)
5986 {
5987 	if (Term == &(data[0].t))
5988 		fs_writechars(x, y, n, s, (char)a);
5989 	return 0;
5990 }
5991 
5992 
5993 
bored()5994 static void bored()
5995 {
5996 	static int last = -1;
5997 	char ts[80];
5998 	time_t ct;
5999 	struct tm *lt;
6000 	unsigned int l;
6001 	int ofm;
6002 	static int alarm_flash = 1;
6003 
6004 
6005 	/* Really no need to check the alarm more than once per second. */
6006 	if (alarm_type && Time_Monotonic() > alarm_lastcheck + 200)
6007 	{
6008 		check_alarm();
6009 	}
6010 
6011 	l = Time_Monotonic();
6012 	if ((l - last) < (alarm_flash ? 25 : 50))
6013 	{
6014 		return;
6015 	}
6016 	last = l;
6017 
6018 	time(&ct);
6019 	lt = localtime(&ct);
6020 	l = strftime(ts, 80, "%c %Z", lt);
6021 
6022 	/* Hack: disable force_mono around printing the time */
6023 	ofm = force_mono;
6024 	force_mono = 0;
6025 
6026 	/* Hack: Is the alarm supposed to be going off? */
6027 	if (alarm_disp || alarm_flash)
6028 	{
6029 		char blk[60];
6030 		int c = 8;
6031 		if (!alarm_disp)
6032 		{
6033 			alarm_flash = 11;
6034 		}
6035 		switch (alarm_flash / 2)
6036 		{
6037 			case 4: sprintf(blk, "%-57s", alarm_cancel_text);
6038 				break;
6039 			case 5: sprintf(blk, "%-57s", fs_quit_key_text);
6040 				break;
6041 			default:
6042 				c = alarm_flash & 1 ? TERM_RED : TERM_WHITE;
6043 				sprintf(blk, "%02d:%02d %-51s", alarm_h, alarm_m,
6044 						alarm_message);
6045 		}
6046 		fs_writechars(0, TIME_LINE, 57, blk, c);
6047 		if (++alarm_flash > 11)
6048 		{
6049 			alarm_flash = 0;
6050 		}
6051 	}
6052 
6053 	/* Display time */
6054 	fs_writechar(79 - l, TIME_LINE, ' ', 0);
6055 	fs_writechars(80 - l, TIME_LINE, l, ts, 8);
6056 
6057 	force_mono = ofm;
6058 }
6059 
6060 
6061 
6062 
6063 
6064 
6065 #ifdef USE_DA
6066 /*--------------------------------------------------------------------------*/
6067 /* (Simple) Heap management (using OS_Heap)									*/
6068 /*--------------------------------------------------------------------------*/
6069 
6070 typedef void *heap;
6071 
Heap_Initialise(heap h,size_t size)6072 static os_error *Heap_Initialise(heap h, size_t size)
6073 {
6074 	return SWI(4, 0, SWI_OS_Heap, 0, h, 0, size);
6075 }
6076 
Heap_Claim(heap h,size_t size)6077 static void *Heap_Claim(heap h, size_t size)
6078 {
6079 	void *fred;
6080 	os_error *e;
6081 	e = SWI(4, 3, SWI_OS_Heap, 2, h, 0, size, NULL, NULL, &fred);
6082 	return e ? NULL : fred;
6083 }
6084 
Heap_Release(heap h,void * block)6085 static os_error *Heap_Release(heap h, void *block)
6086 {
6087 	return SWI(3, 0, SWI_OS_Heap, 3, h, block);
6088 }
6089 
Heap_ChangeHeapSize(heap h,int resize_by)6090 static int Heap_ChangeHeapSize(heap h, int resize_by)
6091 {
6092 	int by;
6093 	SWI(4, 4, SWI_OS_Heap, 5, h, 0, resize_by, 0, 0, 0, &by);
6094 	return by;
6095 }
6096 
6097 
6098 
6099 /*--------------------------------------------------------------------------*/
6100 /* Stuff below here is for using Dynamic areas (under RO3.5+)				*/
6101 /*--------------------------------------------------------------------------*/
6102 
6103 static int game_area = -1;	/* The DA the game is using */
6104 static int font_area = -1;	/* The DA the fonts are using */
6105 
6106 static void *game_area_base;	/* base address of game area */
6107 static void *font_area_base;	/* base address of font area */
6108 
6109 static int font_area_size;	/* size of the fonts' DA */
6110 static int font_heap_size;	/* size of the fonts' heap */
6111 static int game_area_size;	/* size of the game's DA */
6112 static int game_heap_size;	/* size of the game's heap */
6113 
6114 #define MAX_F_DA_SIZE (2<<20)	/* Max size of font area (2Mb) */
6115 #define MAX_G_DA_SIZE (4<<20)	/* Max size of game area (4Mb) */
6116 #define SHRINK_GRAN (4<<10)		/* Try to recalaim wastage > this (4Kb) */
6117 
6118 
6119 /*
6120  | Free dynamic areas when we exit
6121  */
cleanup_memory(void)6122 static void cleanup_memory(void)
6123 {
6124 	if (game_area != -1)
6125 	{
6126 		SWI(2, 0, SWI_OS_DynamicArea, 1, game_area);
6127 		game_area = -1;
6128 	}
6129 
6130 	if (font_area != -1)
6131 	{
6132 		SWI(2, 0, SWI_OS_DynamicArea, 1, font_area);
6133 		font_area = -1;
6134 	}
6135 
6136 }
6137 
6138 
6139 
6140 /*
6141  | Set up the memory allocation stuff.
6142  | We check to see if DAs are possible and if so initialise two:
6143  | one for the game's use (via the rnalloc() hooks) and one for
6144  | our own use (for fonts, etc).
6145  |
6146  | Each area is created 16Kb in size, with a max size of 2/4Mb.
6147  |
6148  | If 'daf' is TRUE, an area is created for the fonts.
6149  | If 'dag' is TRUE, an area is created for the game.
6150  */
init_memory(int daf,int dag)6151 static void init_memory(int daf, int dag)
6152 {
6153 	os_error *e = NULL;
6154 
6155 	if (!daf)
6156 	{
6157 		/* Paranoia */
6158 		font_area = -1;
6159 		font_area_base = 0;
6160 	}
6161 	else
6162 	{
6163 		e = SWI(9, 4, SWI_OS_DynamicArea, 0,	/* Create */
6164 				-1,				/* Let OS allocate no. */
6165 				16 << 10,		/* Initial size */
6166 				-1,				/* Let OS allocate address */
6167 				1 << 7,			/* Cacheable, bufferable, RW */
6168 				MAX_F_DA_SIZE,	/* Max size */
6169 				0,				/* handler */
6170 				0,				/* handler workspace */
6171 				VARIANT " font data",	/* Name */
6172 				/* */
6173 				NULL,			/* r0 */
6174 				&font_area,		/* area number allocated */
6175 				NULL,			/* r2 */
6176 				&font_area_base	/* base address of area */
6177 			);
6178 
6179 		if (e)
6180 		{
6181 			game_area = font_area = -1;
6182 			game_area_base = font_area_base = 0;	/* paranoia */
6183 			return;
6184 		}
6185 		else
6186 		{
6187 			e = SWI(2, 3, SWI_OS_DynamicArea, 2, font_area,
6188 					NULL, NULL, &font_area_size);
6189 			if (e)
6190 			{
6191 				Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6192 			}
6193 
6194 			e = Heap_Initialise((heap) font_area_base, font_area_size);
6195 			if (e)
6196 			{
6197 				Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6198 			}
6199 			font_heap_size = font_area_size;
6200 		}
6201 	}
6202 
6203 	/* Make sure DA(s) are removed when we quit */
6204 	atexit(cleanup_memory);
6205 
6206 	if (!dag)
6207 	{
6208 		/* Paranoia */
6209 		game_area = -1;
6210 		game_area_base = 0;
6211 	}
6212 	else
6213 	{
6214 		e = SWI(9, 4, SWI_OS_DynamicArea, 0,	/* Create */
6215 				-1,				/* Let OS allocate no. */
6216 				16 << 10,		/* Initial size */
6217 				-1,				/* Let OS allocate address */
6218 				1 << 7,			/* Cacheable, bufferable, RW */
6219 				MAX_G_DA_SIZE,	/* Max size */
6220 				0,				/* handler */
6221 				0,				/* handler workspace */
6222 				VARIANT " game data",	/* Name */
6223 				/* */
6224 				NULL,			/* r0 */
6225 				&game_area,		/* area number allocated */
6226 				NULL,			/* r2 */
6227 				&game_area_base	/* base address of area */
6228 			);
6229 
6230 		if (e)
6231 		{
6232 			game_area = -1;
6233 			game_area_base = 0;	/* paranoia */
6234 		}
6235 		else
6236 		{
6237 			e = SWI(2, 3, SWI_OS_DynamicArea, 2, game_area,
6238 					NULL, NULL, &game_area_size);
6239 			if (e)
6240 			{
6241 				Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6242 			}
6243 
6244 			e = Heap_Initialise((heap) game_area_base, game_area_size);
6245 			if (e)
6246 			{
6247 				Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6248 			}
6249 			game_heap_size = game_area_size;
6250 		}
6251 	}
6252 }
6253 
grow_dynamicarea(int area,int by)6254 static int grow_dynamicarea(int area, int by)
6255 {
6256 	os_error *e;
6257 	e = SWI(2, 2, SWI_OS_ChangeDynamicArea, area, by, /**/ NULL, &by);
6258 	/* Can't check errors since a 'failed' shrink returns one... */
6259 	return by;
6260 }
6261 
6262 
6263 /*
6264  | Try to shrink the font-cache heap and area as much as possible.
6265  */
f_shrink_heap(void)6266 static void f_shrink_heap(void)
6267 {
6268 	int s;
6269 	/* Shrink the heap as far as possible */
6270 	font_heap_size -=
6271 		Heap_ChangeHeapSize((heap) font_area_base, -MAX_F_DA_SIZE);
6272 	/* Shrink the dynamic area if necessary */
6273 	s = font_area_size - font_heap_size;
6274 	if (s >= SHRINK_GRAN)
6275 		font_area_size -= grow_dynamicarea(font_area, -s);
6276 }
6277 
6278 /*
6279  | Allocate a block of memory in the font heap
6280  */
f_malloc(size_t size)6281 static void *f_malloc(size_t size)
6282 {
6283 	void *c;
6284 	int s;
6285 	if (font_area == -1)
6286 	{
6287 		return malloc(size);
6288 	}
6289 	c = Heap_Claim((heap) font_area_base, size);
6290 
6291 	if (!c)
6292 	{
6293 		/* The Claim failed.  Try to grow the area by the size of the block */
6294 		s = grow_dynamicarea(font_area, size + 64);	/* 64 is overkill */
6295 		if (!s)
6296 		{
6297 			return NULL;
6298 		}
6299 		font_area_size += s;
6300 		s = font_area_size - font_heap_size;
6301 		font_heap_size += Heap_ChangeHeapSize((heap) font_area_base, s);
6302 		c = Heap_Claim((heap) font_area_base, size);
6303 		if (c)
6304 		{
6305 			f_shrink_heap();
6306 		}
6307 	}
6308 	return c;
6309 }
6310 
6311 
6312 /*
6313  | Free a block of memory in the font heap
6314  */
f_free(void * blk)6315 static void f_free(void *blk)
6316 {
6317 	os_error *e;
6318 	if (font_area == -1)
6319 	{
6320 		free(blk);
6321 		return;
6322 	}
6323 	e = Heap_Release((heap) font_area_base, blk);
6324 	if (e)
6325 		Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
6326 	f_shrink_heap();
6327 }
6328 
6329 
6330 
6331 
6332 
6333 /*
6334  | Allocate a block of memory in the game heap
6335  */
g_malloc(huge size)6336 static vptr g_malloc(huge size)
6337 {
6338 	void *c;
6339 	int s;
6340 
6341 	if (game_area == -1)
6342 	{
6343 		return malloc((size_t) size);
6344 	}
6345 	c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
6346 	if (!c)
6347 	{
6348 		/* The Claim failed.  Try to grow the area by the size of the block */
6349 		s = grow_dynamicarea(game_area, (size_t) size + 64);	/* 64 is overkill */
6350 		if (!s)
6351 		{
6352 			return NULL;
6353 		}
6354 		game_area_size += s;
6355 		s = game_area_size - game_heap_size;
6356 		game_heap_size += Heap_ChangeHeapSize((heap) game_area_base, s);
6357 		c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
6358 	}
6359 
6360 	if (c)
6361 	{
6362 		strcpy((char *)c, "MUSH");
6363 		c = (void *)(((int)c) + 4);
6364 	}
6365 
6366 	if (log_g_malloc)
6367 		fprintf(stderr, "ralloc(%ld) == %p\n", (long)size, c);
6368 
6369 	return c;
6370 }
6371 
6372 
6373 /*
6374  | Free a block of memory in the game heap
6375  |
6376  | The 'len' is to be compatible with z-virt.c (we don't need/use it)
6377  | Returns NULL.
6378  */
g_free(vptr blk)6379 static vptr g_free(vptr blk)
6380 {
6381 	os_error *e;
6382 	int s;
6383 
6384 	if (game_area == -1)
6385 	{
6386 		free(blk);
6387 		return NULL;
6388 	}
6389 
6390 	if (log_g_malloc)
6391 		fprintf(stderr, "rnfree(%p)\n", blk);
6392 
6393 	if (strncmp(((char *)blk) - 4, "MUSH", 4))
6394 		core("game heap corrupt / bad attempt to free memory");
6395 
6396 	blk = (void *)(((int)blk) - 4);
6397 
6398 	e = Heap_Release((heap) game_area_base, blk);
6399 	if (e)
6400 		Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
6401 
6402 	/* Shrink the heap as far as possible */
6403 	game_heap_size -=
6404 		Heap_ChangeHeapSize((heap) game_area_base, -MAX_G_DA_SIZE);
6405 
6406 	/* Shrink the dynamic area if necessary */
6407 	s = game_area_size - game_heap_size;
6408 	if (s >= SHRINK_GRAN)
6409 		game_area_size -= grow_dynamicarea(game_area, -s);
6410 
6411 	return NULL;
6412 }
6413 
6414 
6415 #endif /* USE_DA */
6416 
6417 
6418 
6419 /*--------------------------------------------------------------------------*/
6420 
6421 /*
6422  | New to 1.04: Sound support :)
6423  |
6424  | We use the PlayIt module (for convenience).
6425  |
6426  | The Lib/xtra/sound/sound.cfg file is used to map sample names onto
6427  | event names.
6428  |
6429  | Since textual names are used in the .cfg file, we need to have a lookup
6430  | table to translate them into numbers.  At present we use the
6431  | angband_sound_name array defined in variable.c
6432  |
6433  | Since there can be multiple sounds for each event we need to use a
6434  | list to store them.
6435  */
6436 
6437 /* NB: This will be clipped to 10 under RISC OS 2 */
6438 #define MAX_SAMPNAME_LEN 64
6439 
6440 
6441 
6442 
6443 /*
6444  | The list format:
6445  */
6446 typedef struct samp_node
6447 {
6448 	char sample_name[MAX_SAMPNAME_LEN + 1];	/* Sample name */
6449 	struct samp_node *next;	/* -> next node */
6450 }
6451 SampNode;
6452 
6453 typedef struct samp_info
6454 {
6455 	int samples;	/* # samples for this event */
6456 	SampNode *samplist;	/* list of sample names */
6457 }
6458 SampInfo;
6459 
6460 
6461 /*
6462  | Just need an array of SampInfos
6463  */
6464 static SampInfo sample[SOUND_MAX];
6465 
6466 /*
6467  | This flag will only be set non-zero if the SampInfo array is
6468  | valid.
6469  */
6470 static int sound_initd = 0;
6471 
6472 
read_sound_config(void)6473 static void read_sound_config(void)
6474 {
6475 	int i;
6476 	char buffer[2048];
6477 	FILE *f;
6478 	int max_sampname_len = truncate_names()? 10 : MAX_SAMPNAME_LEN;
6479 	FILE *dbo = NULL;
6480 
6481 	if (show_sound_alloc)
6482 	{
6483 		sprintf(buffer, "%s%s", resource_path, "sndmap/out");
6484 		dbo = fopen(buffer, "w");
6485 		if (!dbo)
6486 		{
6487 			core("can't create sndmap/out debugging file");
6488 		}
6489 	}
6490 
6491 	if (!sound_initd)
6492 	{
6493 		/* Initialise the sample array */
6494 		for (i = 0; i < SOUND_MAX; i++)
6495 		{
6496 			sample[i].samples = 0;
6497 			sample[i].samplist = NULL;
6498 		}
6499 		sound_initd = 1;
6500 	}
6501 	else
6502 	{
6503 		/* Deallocate the sample lists */
6504 		for (i = 0; i < SOUND_MAX; i++)
6505 		{
6506 			SampNode *si = sample[i].samplist;
6507 			sample[i].samples = 0;
6508 			sample[i].samplist = NULL;
6509 			while (si)
6510 			{
6511 				SampNode *ns = si->next;
6512 				free(si);
6513 				si = ns;
6514 			}
6515 		}
6516 	}
6517 
6518 
6519 	/* Open the config file */
6520 	sprintf(buffer, "%sSound:%s", RISCOS_VARIANT, "sound/cfg");
6521 	f = fopen(buffer, "r");
6522 
6523 	/* No cfg file => no sounds */
6524 	if (!f)
6525 	{
6526 		if (show_sound_alloc)
6527 		{
6528 			fprintf(dbo, "** Can't open cfg file '%s'\n", buffer);
6529 			fclose(dbo);
6530 		}
6531 		return;
6532 	}
6533 
6534 	/* Parse the file */
6535 	while (fgets(buffer, sizeof(buffer), f))
6536 	{
6537 		char *sample_name;
6538 		int event_number;
6539 
6540 		/* Skip comments and lines that begin with whitespace */
6541 		if (*buffer == '#' || isspace((unsigned char)*buffer))
6542 		{
6543 			continue;
6544 		}
6545 
6546 		/* Hack: ignore any line beginning '[' (section marker) */
6547 		if (*buffer == '[')
6548 		{
6549 			continue;
6550 		}
6551 
6552 		/* Place a NULL after the event name and find the first sample name */
6553 		sample_name = buffer;
6554 		while (*sample_name && !isspace((unsigned char)*sample_name))
6555 			sample_name++;
6556 
6557 		/* Bad line? */
6558 		if (*sample_name == 0)
6559 		{
6560 			continue;
6561 		}						/* just ignore it */
6562 
6563 		/* Terminate the sample name */
6564 		*sample_name++ = 0;
6565 
6566 		/* Look up the event name to get the event number */
6567 		for (event_number = SOUND_MAX - 1; event_number >= 0; event_number--)
6568 			if (!strcmp(buffer, angband_sound_name[event_number]))
6569 				break;
6570 
6571 		/* No match -> just ignore the line */
6572 		if (event_number < 0)
6573 		{
6574 			if (show_sound_alloc)
6575 				fprintf(dbo, "* Ignoring unknown event '%s'\n", buffer);
6576 			continue;
6577 		}
6578 
6579 		/* Find the = */
6580 		while (*sample_name && *sample_name != '=')
6581 			sample_name++;
6582 
6583 		/* Bad line? */
6584 		if (*sample_name == 0)
6585 		{
6586 			continue;
6587 		}						/* just ignore it */
6588 
6589 		/* Skip the '=' */
6590 		sample_name++;
6591 
6592 
6593 		/*
6594 		   | Now we find all the sample names and add them to the
6595 		   | appropriate list in the sample mapping array
6596 		 */
6597 
6598 		while (*sample_name)
6599 		{
6600 			char *s;
6601 			SampNode *sn;
6602 
6603 			/* Find the start of the next word */
6604 			while (isspace((unsigned char)*sample_name) && *sample_name)
6605 				sample_name++;
6606 
6607 			/* End of line? */
6608 			if (!*sample_name)
6609 			{
6610 				break;
6611 			}
6612 
6613 			/* Find the end of the sample name */
6614 			s = sample_name;	/* start of the name */
6615 			while (!isspace((unsigned char)*sample_name) && *sample_name)
6616 				sample_name++;
6617 
6618 			/* Hack: shorten sample names that are too long */
6619 			if ((sample_name - s) > max_sampname_len)
6620 				s[max_sampname_len] = ' ';
6621 
6622 			/* Allocate a node in the sample list for the event */
6623 			if ((sn = malloc(sizeof(SampNode))) == NULL)
6624 				core("Out of memory (scanning sound.cfg)");
6625 
6626 			/* Link the node to the list */
6627 			sn->next = sample[event_number].samplist;
6628 			sample[event_number].samplist = sn;
6629 
6630 			/* Imcrement the sample count for that event */
6631 			sample[event_number].samples++;
6632 
6633 			/*
6634 			   | Copy the sample name into the node, converting it into
6635 			   | RISC OS style as we go.
6636 			 */
6637 			for (i = 0; !isspace((unsigned char)s[i]) && s[i]; i++)
6638 			{
6639 				if (s[i] == '.')
6640 					sn->sample_name[i] = '/';
6641 				else if (s[i] == '/')
6642 					sn->sample_name[i] = '.';
6643 				else
6644 					sn->sample_name[i] = s[i];
6645 			}
6646 			/*
6647 			   | The sample name '*' is special and means "no new sound"
6648 			   | so don't store a filename for these mappings.
6649 			 */
6650 			if (i == 1 && sn->sample_name[0] == '*')
6651 			{
6652 				i = 0;
6653 			}
6654 			sn->sample_name[i] = 0;
6655 		}
6656 	}
6657 
6658 	/* Close the file */
6659 	fclose(f);
6660 
6661 	if (show_sound_alloc)
6662 	{
6663 		int i;
6664 		SampNode *l;
6665 
6666 		for (i = 0; i < SOUND_MAX; i++)
6667 		{
6668 			fprintf(dbo, "\n\nEvent '%s'", angband_sound_name[i]);
6669 			fprintf(dbo, " (%d sounds)\n", sample[i].samples);
6670 			for (l = sample[i].samplist; l; l = l->next)
6671 				fprintf(dbo, "\t%s\n", l->sample_name);
6672 		}
6673 		fclose(dbo);
6674 	}
6675 
6676 }
6677 
6678 
6679 
6680 /*
6681  | Try to make sure that PlayIt is loaded.
6682  | This requires AngSound rel. 4
6683  */
check_playit(void)6684 static void check_playit(void)
6685 {
6686 	if (SWI(2, 0, SWI_OS_Module, 18, "PlayIt"))
6687 	{
6688 		int t;
6689 		SWI(2, 1, SWI_OS_File, 17, "Angsound:LoadPlayIt", &t);
6690 		if (t == 1)
6691 			SWI(1, 0, SWI_OS_CLI,
6692 				"RMEnsure PlayIt 0.00 Run AngSound:LoadPlayIt");
6693 	}
6694 }
6695 
6696 
6697 
6698 
initialise_sound(void)6699 static void initialise_sound(void)
6700 {
6701 	/* Load the configuration file */
6702 	Hourglass_On();
6703 	read_sound_config();
6704 	check_playit();
6705 	Hourglass_Off();
6706 }
6707 
6708 
6709 
play_sample(char * leafname)6710 static void play_sample(char *leafname)
6711 {
6712 	char buffer[260];
6713 
6714 	strcpy(buffer, "%playit_stop");
6715 
6716 	if (!SWI(1, 0, SWI_OS_CLI, buffer))
6717 	{
6718 		SWI(1, 0, SWI_PlayIt_Volume, sound_volume);
6719 		sprintf(buffer, "%%playit_play %sSound:%s", RISCOS_VARIANT, leafname);
6720 		SWI(1, 0, SWI_OS_CLI, buffer);
6721 	}
6722 
6723 	return;
6724 }
6725 
6726 
6727 
play_sound(int event)6728 static void play_sound(int event)
6729 {
6730 	/* Paranoia */
6731 	if (!sound_initd)
6732 	{
6733 		return;
6734 	}
6735 
6736 	/* Paranoia */
6737 	if (event < 0 || event >= SOUND_MAX)
6738 		return;
6739 
6740 	/* msg_format("Sound '%s'",angband_sound_name[event]); */
6741 
6742 	/* Choose a sample */
6743 	if (sample[event].samples)
6744 	{
6745 		int s = rand() % sample[event].samples;
6746 		SampNode *sn = sample[event].samplist;
6747 		while (s--)
6748 		{
6749 			sn = sn->next;
6750 			if (!sn)
6751 			{
6752 				plog("Adny botched the sound config - please send him a copy of your sound/cfg file.");
6753 			}
6754 		}
6755 		if (*(sn->sample_name))
6756 			play_sample(sn->sample_name);
6757 	}
6758 }
6759 
6760 
6761 
6762 
6763 /*--------------------------------------------------------------------------*/
6764 
6765 
6766 
6767 /*
6768  | Let the user change the alarm message
6769  */
do_alarm_message_input(int y)6770 static void do_alarm_message_input(int y)
6771 {
6772 	int k;
6773 	int inspos = strlen(alarm_message);
6774 	char old_message[52];
6775 
6776 	strcpy(old_message, alarm_message);
6777 
6778 	do
6779 	{
6780 	    put_fstr(26, y, CLR_YELLOW "%-51s", alarm_message);
6781 		Term_gotoxy(26 + inspos, y);
6782 		k = inkey();
6783 		switch (k)
6784 		{
6785 			case 21:			/* ^U */
6786 				*alarm_message = 0;
6787 				inspos = 0;
6788 				break;
6789 			case 128:  case 8:	/* delete */
6790 				if (inspos > 0)
6791 				{
6792 					alarm_message[--inspos] = 0;
6793 				}
6794 				break;
6795 			case 27:			/* escape */
6796 				strcpy(alarm_message, old_message);
6797 				k = 13;
6798 				break;
6799 			default:
6800 				if (k > 31 && k < 127 && inspos < 50)
6801 				{
6802 					alarm_message[inspos++] = k;
6803 					alarm_message[inspos] = 0;
6804 				}
6805 		}
6806 	}
6807 	while (k != 13);
6808 
6809 	put_fstr(26, y, CLR_WHITE "%-51s", alarm_message);
6810 }
6811 
6812 
6813 #define tum_col(X)  ((X) ? CLR_L_BLUE : CLR_WHITE )
6814 #define tum_onoff(X)  ((X) ? "On " : "Off")
6815 
Term_user_acn(int n)6816 static errr Term_user_acn(int n)
6817 {
6818 	int cursor_state;
6819 	int optn = 0;
6820 	int k, adj;
6821 	int redraw_mung = 0;
6822 	int max_opt = 11;
6823 	int alarm_modified = 0;	/* Will be true if the alarm choices need to be (re)saved */
6824 
6825 	/*
6826 	   | Hack: let the desktop front end know that
6827 	   | the user menu is active...
6828 	 */
6829 	user_menu_active = TRUE;
6830 
6831 
6832 	/*
6833 	   | This is thanks to Norcroft CC...  it seems to want
6834 	   | to set up FP regs nice and early and then relies
6835 	   | on them remaining constant over the function call.
6836 	   | The trouble is that we implicitly call Wimp_Poll
6837 	   | whilst waiting for a key press...
6838 	 */
6839 	event_mask.data.keepfpregisters = 1;
6840 
6841 	/*
6842 	   | Hack: alarm type 1 /looks/ the same as type 3 but doesn't get
6843 	   | cancelled as a type 3 would.  This allows alarms to go off and
6844 	   | be cancelled without affecting the alarm type whilst it's being
6845 	   | set up here.
6846 	 */
6847 	if (alarm_type == 3)
6848 	{
6849 		alarm_type = 1;
6850 	}
6851 
6852 	/*
6853 	   | Store the screen
6854 	 */
6855 	Term_activate(&(data[0].t));
6856 	Term_save();
6857 	Term_get_cursor(&cursor_state);
6858 	Term_set_cursor(TRUE);
6859 
6860 	do
6861 	{
6862 		redraw_mung = 0;
6863 		Term_clear();
6864 		put_fstr(2, 1, CLR_YELLOW "%s %s", VARIANT, VERSION);
6865 		put_fstr(2, 2, CLR_SLATE "Front-end %s", PORTVERSION);
6866 		put_fstr(2, 4, CLR_WHITE
6867 					 "Use cursor up/down to select an option then cursor left/right to alter it.");
6868 		put_fstr(2, 5, CLR_WHITE
6869 					 "Hit 'S' to save these settings (alarm settings are saved automatically).");
6870 		put_fstr(2, 6, CLR_WHITE "Hit ESC to return to the game.");
6871 
6872 		for (k = 0; k < 32; k++)
6873 		{
6874 			Term_putch(31 + k + (k / 2), 8, k / 2, '#');
6875 		}
6876 
6877 		do
6878 		{
6879 			/* Gamma value never goes about 9.00, 5 char array should be fine */
6880 			char gammas[5];
6881 			sprintf(gammas, "%.2lf", gamma);
6882 
6883 			put_fstr(2, 8,
6884 						 "%s     Gamma correction : %s", tum_col(optn == 0), gammas);
6885 			put_fstr(2, 9, "%s     Force monochrome : %s",
6886 						 tum_col(optn == 1), tum_onoff(force_mono));
6887 			put_fstr(2, 10, "%s        Sound effects : %s",
6888 						 tum_col(optn == 2), tum_onoff(enable_sound));
6889 			put_fstr(2, 11, "%s  Sound effect volume : ", tum_col(optn == 3));
6890 			put_fstr(26, 11, "%s%-3d",
6891 						 sound_volume > 127 ? CLR_RED : tum_col(optn == 3),
6892 						 sound_volume);
6893 			put_fstr(30, 11, "%s(127 = full volume)", tum_col(optn == 3));
6894 			put_fstr(2, 12, "%s     Start fullscreen : %s",
6895 						 tum_col(optn == 4), tum_onoff(start_fullscreen));
6896 			put_fstr(30, 12,
6897 						 "%s(also selects fullscreen/desktop now)", tum_col(optn == 4));
6898 			put_fstr(2, 13, "%s        Use hourglass : %s",
6899 						 tum_col(optn == 5), tum_onoff(use_glass));
6900 			put_fstr(2, 14,
6901 						 "%s'Hard' input flushing : %s", tum_col(optn == 6), tum_onoff(hack_flush));
6902 
6903 			put_fstr(7, 16, "%s      Alarm type : %-20s",
6904 						 tum_col(optn == 7), alarm_types[alarm_type]);
6905 			put_fstr(7, 17, CLR_WHITE "            Time : ");
6906 			put_fstr(26, 17, "%s%02d", tum_col(optn == 8), alarm_h);
6907 			put_fstr(28, 17, CLR_WHITE, ":");
6908 			put_fstr(29, 17, "%s%02d", tum_col(optn == 9), alarm_m);
6909 			put_fstr(7, 18, "%s         Message : %-51s",
6910 						 tum_col(optn == 10), alarm_message);
6911 			put_fstr(7, 19, "%s            Beep : %s",
6912 						 tum_col(optn == 11), tum_onoff(alarm_beep));
6913 
6914 #ifdef FE_DEBUG_INFO
6915 			put_fstr(2, 23, "%sShow debug info", tum_col(optn == 23));
6916 			max_opt = 12;
6917 #endif
6918 
6919 			switch (optn)
6920 			{
6921 				case 12: Term_gotoxy(2, 23);
6922 					break;
6923 				case 11: Term_gotoxy(26, 19);
6924 					break;
6925 				case 10: Term_gotoxy(26, 18);
6926 					break;
6927 				case 9: Term_gotoxy(29, 17);
6928 					break;
6929 				case 8: Term_gotoxy(26, 17);
6930 					break;
6931 				case 7: Term_gotoxy(26, 16);
6932 					break;
6933 				default: Term_gotoxy(26, optn + 8);
6934 			}
6935 
6936 			k = inkey();
6937 			adj = (k == '4' || k == 'h') ? -1 : (k == '6' || k == 'l') ? 1 : 0;
6938 
6939 			switch (k)
6940 			{
6941 				case 18:		/* Hack: force the screen to update */
6942 					redraw_mung = 1;
6943 					k = 27;
6944 					break;
6945 				case 's':  case 'S':
6946 					save_choices();
6947 					put_fstr(2, 23, CLR_YELLOW "Options saved.       ");
6948 					Term_fresh();
6949 					Term_xtra(TERM_XTRA_DELAY, 750);
6950 					Term_erase(2, 23, 60);
6951 					break;
6952 				case '8':  case 'k':
6953 					if (--optn < 0)
6954 					{
6955 						optn = max_opt;
6956 					}
6957 					break;
6958 				case '2':  case 'j':
6959 					if (++optn > max_opt)
6960 					{
6961 						optn = 0;
6962 					}
6963 					break;
6964 				case 13:  case 32:  case 't':	/* Allow return, space and t to toggle some options */
6965 				case '4':  case 'h':
6966 				case '6':  case 'l':
6967 				{
6968 					switch (optn)
6969 					{
6970 						case 0:	/* Gamma correction */
6971 							gamma += adj * 0.05;
6972 							if (gamma > 9.00)
6973 							{
6974 								gamma = 9.00;
6975 							}
6976 							if (gamma < 0.05)
6977 							{
6978 								gamma = 0.05;
6979 							}
6980 							Term_xtra(TERM_XTRA_REACT, 0);
6981 #ifndef FULLSCREEN_ONLY
6982 							set_gamma_window_state();
6983 #endif /* FULLSCREEN_ONLY */
6984 							/* flush(); */
6985 							Term_fresh();
6986 							break;
6987 						case 1:	/* Force monochrome */
6988 							force_mono = !force_mono;
6989 							if (fullscreen_font)
6990 								redraw_areaFS(0, 0, 80, 24);
6991 							else
6992 								Term_xtra(TERM_XTRA_REACT, 0);
6993 							/* flush(); */
6994 							Term_fresh();
6995 							break;
6996 						case 2:	/* Sound enable / disable */
6997 							enable_sound = !enable_sound;
6998 #ifndef FULLSCREEN_ONLY
6999 							set_sound_window_state();
7000 #endif /* FULLSCREEN_ONLY */
7001 							if (enable_sound)
7002 							{
7003 								initialise_sound();
7004 							}
7005 							break;
7006 						case 3:	/* Sound volume */
7007 							sound_volume += adj;
7008 							if (sound_volume < SOUND_VOL_MIN)
7009 								sound_volume = SOUND_VOL_MIN;
7010 							if (sound_volume > SOUND_VOL_MAX)
7011 								sound_volume = SOUND_VOL_MAX;
7012 #ifndef FULLSCREEN_ONLY
7013 							set_sound_window_state();
7014 #endif /* FULLSCREEN_ONLY */
7015 							break;
7016 						case 4:	/* Start fullscreen */
7017 							start_fullscreen = !start_fullscreen;
7018 							if (start_fullscreen)
7019 								enter_fullscreen_mode();
7020 							else if (!minimise_memory)
7021 								leave_fullscreen_mode();
7022 							break;
7023 						case 5:	/* Start fullscreen */
7024 							use_glass = !use_glass;
7025 							if (!use_glass)
7026 							{
7027 								if (glass_on)
7028 								{
7029 									Hourglass_Off();
7030 								}
7031 								glass_on = 0;
7032 							}
7033 							break;
7034 						case 6:	/* hack flush */
7035 							hack_flush = !hack_flush;
7036 							break;
7037 						case 7:	/* Alarm on/off */
7038 							alarm_type += adj;
7039 							if (adj)
7040 							{
7041 								alarm_modified = 1;
7042 							}
7043 							if (alarm_type > 2)
7044 							{
7045 								alarm_type = 0;
7046 							}
7047 							if (alarm_type < 0)
7048 							{
7049 								alarm_type = 2;
7050 							}
7051 							if (!alarm_type && alarm_disp)
7052 							{
7053 								ack_alarm();
7054 							}	/* XXXXX Cancel an already active alarm? */
7055 							break;
7056 						case 8:	/* Alarm hours */
7057 							alarm_h += adj;
7058 							if (adj)
7059 							{
7060 								alarm_modified = 1;
7061 							}
7062 							if (alarm_h < 0)
7063 							{
7064 								alarm_h += 24;
7065 							}
7066 							if (alarm_h > 23)
7067 							{
7068 								alarm_h -= 24;
7069 							}
7070 							if (alarm_disp)
7071 							{
7072 								ack_alarm();
7073 							}
7074 							break;
7075 						case 9:	/* Alarm minutes */
7076 							alarm_m += adj;
7077 							if (adj)
7078 							{
7079 								alarm_modified = 1;
7080 							}
7081 							if (alarm_m < 0)
7082 							{
7083 								alarm_m += 60;
7084 							}
7085 							if (alarm_m > 59)
7086 							{
7087 								alarm_m -= 60;
7088 							}
7089 							if (alarm_disp)
7090 							{
7091 								ack_alarm();
7092 							}
7093 							break;
7094 						case 10:
7095 							alarm_modified = 1;
7096 							do_alarm_message_input(18);
7097 							break;
7098 						case 11:
7099 							alarm_modified = 1;
7100 							alarm_beep = !alarm_beep;
7101 							break;
7102 						case 12:
7103 							show_debug_info();
7104 							redraw_mung = 1;
7105 							k = 27;
7106 							break;
7107 					}
7108 				}
7109 			}
7110 		}
7111 		while (k != 27);
7112 	}
7113 	while (redraw_mung);
7114 
7115 	/* Rehack the alarm type: */
7116 	if (alarm_type == 1)
7117 	{
7118 		alarm_type = 3;
7119 	}
7120 
7121 	if (alarm_modified)
7122 	{
7123 		write_alarm_choices();
7124 	}
7125 
7126 	Term_set_cursor(cursor_state);
7127 
7128 	/* Restore the screen */
7129 	Term_load();
7130 
7131 	/* Don't need to preserve FP regs any more */
7132 	event_mask.data.keepfpregisters = 0;
7133 
7134 	/*
7135 	   | Hack: tell the desktop front end that we're done.
7136 	 */
7137 	user_menu_active = FALSE;
7138 
7139 	return 0;
7140 }
7141 
7142 
7143 
7144 
7145 /*--------------------------------------------------------------------------*/
7146 
7147 #ifdef USE_FILECACHE
7148 
7149 /*
7150  | 'Random' File-cacheing for *band.
7151  |
7152  | Rewritten since as of Zang 225 the mechanism for handling
7153  | these files has changed dramatically and the old system
7154  | is no longer viable.
7155  |
7156  | These new functions basically provide an alternative to the
7157  | normal my_fopen() (or fopen()) and my_fgets() functions.
7158  |
7159  | To use the file caching it is therefore necessary to alter
7160  | files.c to call cached_fopen(), cached_fclose() and cached_fgets()
7161  | rather than the normal functions.
7162  |
7163  | Note that these funtions will only work for files that are intended
7164  | to be read as a series of \n terminated lines of ASCII text using my_fgets().
7165  |
7166  */
7167 
7168 /*
7169  | Hack: use the game's dynamic area if possible:
7170  */
7171 #define fc_malloc(X) (g_malloc(X))
7172 #define fc_free(X) (g_free(X,0))
7173 
7174 #ifndef ABBR_FILECACHE
7175 /*
7176  | Make these to do nothing.  They'll never
7177  | be called anyway.  Having them present makes
7178  | for neater code later on (ie. we use a variable
7179  | rather than the pre-processor to decide whether
7180  | to do compression).
7181  */
compress_string(char * os,char * s)7182 static int compress_string(char *os, char *s)
7183 {
7184 	core("main-acn internal logic error 001");
7185 	return 0;
7186 }
decompress_string(char * d,char * s,int max_len)7187 static int decompress_string(char *d, char *s, int max_len)
7188 {
7189 	core("main-acn internal logic error 002");
7190 	return 0;
7191 }
compressed_length(char * s)7192 static int compressed_length(char *s)
7193 {
7194 	core("main-acn internal logic error 003");
7195 	return 0;
7196 }
7197 
7198 #else
7199 
7200 /*
7201  | When caching files we try to use some abbreviations.
7202  | We use both whole words and pairs of letters.
7203  | NB: For this to work, the file must contain only
7204  | 7 bit characters.
7205  */
7206 static char *abbrv_w[] =
7207 {
7208 	/* These words all begin with a space */
7209 	" of ", " the ", " you ", " to ", " a ", " says", " is ", " that ", " and ",
7210 	" your ", " are ", " it ", " be ", " for ", " me", " will ", " in ",
7211 	" not ", " this ", " have ", " can ", " on ", " my ", " with ", " say ",
7212 	" all", " by ", " get ", " but ", " just ", " die", " as ", " time ",
7213 		" if ",
7214 	" like ",
7215 	/* These words do not */
7216 	"I ", "The ", "You ", "They ", "It ", "don", 0
7217 };
7218 
7219 /* Number of words */
7220 #define FC_ABBRV_NUMWORDS 41
7221 
7222 /* Number of them that don't start with a space */
7223 #define FC_ABBRV_NONSPC   6
7224 
7225 /*
7226  | NB: No letter pair may start with \0.
7227  */
7228 static char abbrv_lp[] =
7229 	"e  ttht s heiner aoure'\0, anonf  sd y r  ongofator.\0"
7230 	"n arllstha wes m ieaisen bl  yndtoo yometele d f hve"
7231 	"ayuralitneelN: chig ilroassaseliti lraa otedbede 'ri" "..u  nntno!'ee\0\0";
7232 
7233 
7234 /*
7235  | Compress the given string using the abbreviation tables above.
7236  | Returns compressed length *including* terminator (it may
7237  | be part of an abbreviation, you see...)
7238  | Note that we can compress the string in-place, ie. 'os' may be
7239  | the same as 's'.
7240  */
compress_string(char * os,char * s)7241 static int compress_string(char *os, char *s)
7242 {
7243 	char *o, *f, *d;
7244 	int i;
7245 
7246 	o = os;
7247 
7248 	while (*s)
7249 	{
7250 		int fw, lw;
7251 		if (*s == ' ')
7252 		{
7253 			fw = 0;
7254 			lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7255 		}
7256 		else
7257 		{
7258 			fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7259 			lw = FC_ABBRV_NUMWORDS;
7260 		}
7261 		for (i = fw; i < lw; i++)
7262 		{
7263 			d = abbrv_w[i];		/* Word to check against */
7264 			for (f = s; *f && *f == *d; f++, d++)
7265 				;
7266 			if (*d == 0)		/* Match? */
7267 			{
7268 				s = *f ? f : f - 1;	/* Update string pointer */
7269 				*o++ = 128 + i;	/* store code */
7270 				break;			/* Quit looking for words */
7271 			}
7272 		}
7273 
7274 		/* Do we need to check the letter pairs? */
7275 		if (i == lw)
7276 		{
7277 			for (i = 0; abbrv_lp[i]; i += 2)
7278 			{
7279 				if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
7280 				{
7281 					*o++ = 128 + FC_ABBRV_NUMWORDS + i / 2;
7282 					/* NB: If the next character is the terminator then we're done. */
7283 					if (!s[1])
7284 					{
7285 						return (o - os);
7286 					}
7287 					s += 2;		/* Quit looking for letters */
7288 					break;
7289 				}
7290 			}
7291 			/* NB: This next check is only safe because no letter pair starts with a NULL */
7292 			if (!abbrv_lp[i])
7293 				*o++ = *s++;
7294 		}
7295 	}
7296 
7297 	/* Don't forget that terminator! */
7298 	*o++ = 0;
7299 
7300 	return o - os;
7301 }
7302 
7303 /*
7304  | As compress_string (above), but stores nothing and
7305  | only returns the length of the compressed string.
7306  */
compressed_length(char * s)7307 static int compressed_length(char *s)
7308 {
7309 	char *f, *d;
7310 	int i, l;
7311 
7312 	l = 0;
7313 	while (*s)
7314 	{
7315 		int fw, lw;
7316 		if (*s == ' ')
7317 		{
7318 			fw = 0;
7319 			lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7320 		}
7321 		else
7322 		{
7323 			fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7324 			lw = FC_ABBRV_NUMWORDS;
7325 		}
7326 		for (i = fw; i < lw; i++)
7327 		{
7328 			d = abbrv_w[i];
7329 			for (f = s; *f && *f == *d; f++, d++)
7330 				;
7331 			if (*d == 0)		/* Match? */
7332 			{
7333 				s = *f ? f : f - 1;	/* Update string pointer */
7334 				l++;			/* increment output length */
7335 				break;			/* Quit looking for words */
7336 			}
7337 		}
7338 
7339 		/* Do we need to check the letter pairs? */
7340 		if (i == lw)
7341 		{
7342 			for (i = 0; abbrv_lp[i]; i += 2)
7343 			{
7344 				if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
7345 				{
7346 					l++;		/* increment output length */
7347 					/* NB: If the next character is the terminator then we're done. */
7348 					if (!s[1])
7349 					{
7350 						return l;
7351 					}
7352 					s += 2;		/* Quit looking for letters */
7353 					break;
7354 				}
7355 			}
7356 			/* NB: This next check is only safe because no letter pair starts with a NULL */
7357 			if (!abbrv_lp[i])
7358 			{
7359 				l++;
7360 				s++;
7361 			}
7362 		}
7363 	}
7364 	/* Don't forget that terminator! */
7365 	return l + 1;
7366 }
7367 
7368 
7369 
7370 /*
7371  | Decompress the given string 's' into the buffer at 'd'.
7372  | At most, max_len characters (incl. \0 terminator) will be
7373  | written into d.
7374  | Returns the length of 's'.
7375  */
decompress_string(char * d,char * s,int max_len)7376 static int decompress_string(char *d, char *s, int max_len)
7377 {
7378 	char *os = s;
7379 
7380 	while (max_len > 1)
7381 	{
7382 		int nc = *s++;	/* Get next character */
7383 
7384 		if (nc < 128)			/* Is it a plain character? */
7385 		{
7386 			if (0 == (*d++ = nc))
7387 			{
7388 				break;
7389 			}
7390 			max_len--;
7391 		}
7392 		else					/* Abbreviation to expand. */
7393 		{
7394 			if (nc >= FC_ABBRV_NUMWORDS + 128)	/* Letter pair? */
7395 			{
7396 				*d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2];
7397 				if (0 ==
7398 					(*d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2 + 1]))
7399 					break;
7400 				max_len -= 2;
7401 			}
7402 			else				/* It's a word */
7403 			{
7404 				char *ws = abbrv_w[nc - 128];
7405 				while (*ws && max_len > 1)
7406 				{
7407 					*d++ = *ws++;
7408 					max_len--;
7409 				}
7410 			}
7411 		}
7412 	}
7413 
7414 	/* Skip over the rest of the abbreviated string if we ran out of space */
7415 	if (max_len <= 1)			/* Out of space? */
7416 	{
7417 		int nc;
7418 		*d = 0;					/* Terminate */
7419 		do
7420 		{
7421 			nc = *s++;			/* Next char */
7422 			if (nc >= 128 + FC_ABBRV_NUMWORDS)	/* Ignore words */
7423 				nc = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128) * 2) + 1];	/* Only check 2nd letter of pair */
7424 		}
7425 		while (nc);
7426 	}
7427 	return s - os;				/* Length of abbreviated string */
7428 }
7429 
7430 #endif /* ABBR_FILECACHE */
7431 
7432 
7433 /* Each entry in the cache looks like this: */
7434 typedef struct fce_
7435 {
7436 	char *name;	/* canonical pathname of file */
7437 	char *text;	/* text (be it compressed or not) */
7438 	char *eof;	/* byte beyond the last byte of text */
7439 	int used;	/* access counter when the file was last used */
7440 	int compressed;	/* compression method, (ie. 0 for none or 1 for abbreviations) */
7441 }
7442 FileCacheEntry;
7443 
7444 /*
7445  | The handles we chuck around are pointers to one of these structs.
7446  | Note that since we actually return (and take) |FILE*|s we just
7447  | compare the value of a |FILE*| with the limits of the array of
7448  | |CachedFileHandle|s to decide whether its 'ours' or a genuine
7449  | (ie. stdio) file handle.  This /is/ pretty lame, I know...
7450  */
7451 typedef struct cfh_
7452 {
7453 	char *ptr;	/* sequential file pointer, as it were */
7454 	FileCacheEntry *fce;	/* ->the file-cache entry data */
7455 }
7456 CachedFileHandle;
7457 
7458 #define MAX_OPEN_CACHED_FILES	16	/* We allow up to 16 of these files open at once */
7459 #define MAX_CACHE_ENTRIES		64	/* We allow up to 64 cache entries */
7460 
7461 static FileCacheEntry *file_cache;	/* to be used as file_cache[MAX_CACHE_ENTRIES] */
7462 static CachedFileHandle *cached_file_handle;	/* to be used as cached_file_handle[MAX_OPEN_CACHED_FILES] */
7463 static int file_cache_initd = 0;	/* Is the cache initialised? */
7464 static int file_cache_size = 0;	/* Total size of the cached files */
7465 static int fc_access_counter = 1;	/* incremented on each cache access */
7466 /*
7467  | Pre-calculate max. possible value of a FILE* (ie. address in memory)
7468  | that could be a valid |CachedFileHandle*|.
7469  */
7470 static FILE *max_cfh_addr;	/* == (FILE*) (&(cached_file_handle[MAX_OPEN_CACHED_FILES-1])) */
7471 
7472 /*
7473  | Initialise the file cache
7474  */
init_file_cache(void)7475 static void init_file_cache(void)
7476 {
7477 	int i;
7478 
7479 	/* Allocate storage */
7480 	file_cache = fc_malloc(MAX_CACHE_ENTRIES * sizeof(FileCacheEntry));
7481 	cached_file_handle =
7482 		fc_malloc(MAX_OPEN_CACHED_FILES * sizeof(CachedFileHandle));
7483 
7484 	if (!file_cache || !cached_file_handle)
7485 	{
7486 		/* Disable file-caching */
7487 		if (file_cache)
7488 		{
7489 			fc_free(file_cache);
7490 		}
7491 		if (cached_file_handle)
7492 		{
7493 			fc_free(cached_file_handle);
7494 		}
7495 		use_filecache = 0;
7496 	}
7497 	else
7498 	{
7499 		/* Initialise the cache */
7500 		for (i = 0; i < MAX_CACHE_ENTRIES; i++)
7501 			file_cache[i].name = NULL;
7502 		for (i = 0; i < MAX_OPEN_CACHED_FILES; i++)
7503 			cached_file_handle[i].fce = NULL;
7504 		fc_access_counter = 1;
7505 		file_cache_size = 0;
7506 		max_cfh_addr =
7507 			(FILE *)(&(cached_file_handle[MAX_OPEN_CACHED_FILES - 1]));
7508 	}
7509 	file_cache_initd = 1;
7510 }
7511 
7512 
7513 /*
7514  | Helper: take a copy of the string and return a pointer to it
7515  */
string_cpy(char * s)7516 static char *string_cpy(char *s)
7517 {
7518 	char *d = fc_malloc(strlen(s) + 1L);
7519 	if (d)
7520 	{
7521 		strcpy(d, s);
7522 	}
7523 	return d;
7524 }
7525 
7526 
7527 /*
7528  | Cache the specified file, returning either the cache entry
7529  | that it has been cached at, or NULL for failure.
7530  |
7531  | Note that for the abbreviated file cache a temporary file
7532  | is used to allow the compression to be applied just once.
7533  | (otherwise it has to be done twice - once to determine the
7534  | eventual compressed size and once to actually store and compress
7535  | it).
7536  */
cache_file(char * name)7537 static FileCacheEntry *cache_file(char *name)
7538 {
7539 	int i, size = 0;
7540 	FILE *fp;
7541 	char buffer[1024];
7542 	char *d;
7543 
7544 	FILE *tf = NULL;	/* Used if abbr_filecache and abbr_tmpfile are set */
7545 	char cfn[1024];	/* Used if abbr_filecache and abbr_tmpfile are set */
7546 
7547 	/* Find the first free slot in the cache */
7548 	for (i = 0; i < MAX_CACHE_ENTRIES; i++)
7549 		if (!file_cache[i].name)
7550 			break;
7551 
7552 	/* No more entries? */
7553 	if (i >= MAX_CACHE_ENTRIES)
7554 	{
7555 		return NULL;
7556 	}
7557 
7558 	/* Set up the info on the file */
7559 	if ((file_cache[i].name = string_cpy(name)) == NULL)
7560 	{
7561 		return NULL;
7562 	}
7563 
7564 	/* Open the file */
7565 	fp = my_fopen(name, "r");
7566 	if (!fp)
7567 	{
7568 		fc_free(file_cache[i].name);
7569 		file_cache[i].name = 0;
7570 		return NULL;
7571 	}
7572 
7573 	/* Open/create tempfile if need be: */
7574 	if (abbr_filecache && abbr_tmpfile)
7575 	{
7576 		/* Hack: Form the pathname of the cached compressed file (in canonical form) */
7577 		sprintf(cfn, "%s%s", scrap_path,
7578 				riscosify_name(name + strlen(resource_path)));
7579 		/* Ensure that that particular directory exists... */
7580 		ensure_path(cfn);
7581 		/* Check whether cache file is out of date */
7582 		if (file_is_newer(riscosify_name(name), cfn))
7583 		{
7584 			tf = fopen(cfn, "wb");
7585 			size = 0;
7586 		}
7587 		else
7588 		{
7589 			tf = fopen(cfn, "rb");
7590 			if (tf)
7591 			{
7592 				size = myFile_Size(cfn);
7593 			}
7594 		}
7595 	}
7596 
7597 	/* If we don't have the cached file (but want it), compress the source text to it */
7598 	if (tf)
7599 	{
7600 		if (!size)
7601 		{
7602 			int k;
7603 			while (!my_fgets(fp, buffer, sizeof(buffer)))
7604 			{
7605 				if (smart_filecache && (!*buffer || *buffer == '#'))
7606 					continue;
7607 				k = compress_string(buffer, buffer);
7608 				if (fwrite(buffer, 1, k, tf) != k)
7609 				{
7610 					fclose(tf);
7611 					remove(cfn);
7612 					core("error writing tempfile");
7613 				}
7614 				size += k;
7615 			}
7616 			fclose(tf);
7617 			tf = fopen(cfn, "rb");
7618 		}
7619 	}
7620 	else
7621 	{
7622 		/* Count the number of bytes */
7623 		while (!my_fgets(fp, buffer, sizeof(buffer)))
7624 		{
7625 			if (smart_filecache && (!*buffer || *buffer == '#'))
7626 				continue;
7627 			if (abbr_filecache)
7628 				size += compressed_length(buffer);
7629 			else
7630 				size += strlen(buffer) + 1;
7631 		}
7632 	}
7633 
7634 	/* Close the (source) file */
7635 	my_fclose(fp);
7636 
7637 	/* Allocate enough storage for the text */
7638 	file_cache[i].text = fc_malloc(size + 1L);
7639 	if (!file_cache[i].text)
7640 	{
7641 		fc_free(file_cache[i].name);
7642 		file_cache[i].name = 0;
7643 		if (tf)
7644 		{
7645 			fclose(tf);
7646 		}
7647 		return NULL;
7648 	}
7649 
7650 	/* Do we have a tempfile to load? */
7651 	if (tf)
7652 	{
7653 		if (fread(file_cache[i].text, 1, size, tf) != size)
7654 			core("error reading tempfile");
7655 		fclose(tf);
7656 	}
7657 	else
7658 	{
7659 		/* Re-open the file... */
7660 		fp = my_fopen(name, "r");
7661 		if (!fp)
7662 		{
7663 			fc_free(file_cache[i].name);
7664 			fc_free(file_cache[i].text);
7665 			file_cache[i].name = 0;
7666 			return NULL;
7667 		}
7668 
7669 		/* And read it into the buffer... */
7670 		d = file_cache[i].text;
7671 		while (!my_fgets(fp, buffer, sizeof(buffer)))
7672 		{
7673 			if (smart_filecache && (!*buffer || *buffer == '#'))
7674 				continue;
7675 			if (abbr_filecache)
7676 				d += compress_string(d, buffer);
7677 			else
7678 			{
7679 				strcpy(d, buffer);
7680 				d += strlen(buffer) + 1;
7681 			}
7682 		}
7683 
7684 		if ((d - file_cache[i].text) != size)
7685 		{
7686 			debug("Calculated size is %d, pointer offset is %d", size,
7687 				  (d - file_cache[i].text));
7688 			core("Cached file is larger than calculated!");
7689 		}
7690 
7691 		/* Close the file */
7692 		my_fclose(fp);
7693 	}
7694 
7695 	/* Set up the 'last accessed' value, etc. */
7696 	file_cache[i].used = fc_access_counter++;
7697 	file_cache[i].eof = file_cache[i].text + size;
7698 	file_cache[i].compressed = abbr_filecache;
7699 	file_cache_size += size;
7700 
7701 	/* Return success */
7702 	return &(file_cache[i]);
7703 }
7704 
7705 
7706 /*
7707  | Discard a file from the cache
7708  */
discard_cached_file(int i)7709 static void discard_cached_file(int i)
7710 {
7711 	if (!file_cache[i].name)
7712 	{
7713 		return;
7714 	}							/* invalid request */
7715 	fc_free(file_cache[i].text);
7716 	fc_free(file_cache[i].name);
7717 	file_cache_size -= (file_cache[i].eof) - (file_cache[i].text);
7718 	file_cache[i].name = 0;
7719 }
7720 
7721 
7722 /*
7723  | Attempt to flush as much of the cache as required
7724  | to bring it within the size limit.
7725  | If protect != 0 then that entry in the cache won't be flushed.
7726  */
flush_file_cache(FileCacheEntry * protect)7727 static void flush_file_cache(FileCacheEntry * protect)
7728 {
7729 	int i, j, done;
7730 	int oldest_u, oldest_e;
7731 	FileCacheEntry *fce;
7732 	int needed = (4 << 10);	/* Hack: try to free at least 4K */
7733 
7734 	done = (file_cache_size + needed) <= max_file_cache_size;
7735 
7736 	while (!done)
7737 	{
7738 		oldest_u = fc_access_counter;
7739 		oldest_e = -1;
7740 
7741 		fce = file_cache;
7742 		/* Find oldest entry that isn't in use */
7743 		for (i = 0; i < MAX_CACHE_ENTRIES; fce++, i++)
7744 		{
7745 			if (fce == protect)
7746 			{
7747 				continue;
7748 			}					/* Hack ;) */
7749 			if (fce->name)		/* Is this cache slot full? */
7750 			{
7751 				for (j = 0; j < MAX_OPEN_CACHED_FILES; j++)
7752 					if (cached_file_handle[j].fce == fce)
7753 						break;
7754 				if (j < MAX_OPEN_CACHED_FILES)
7755 					continue;	/* Cached file is still open */
7756 				if (fce->used < oldest_u)
7757 				{
7758 					oldest_e = i;
7759 					oldest_u = file_cache[i].used;
7760 				}
7761 			}
7762 		}
7763 
7764 		if (oldest_e < 0)
7765 			done = 1;			/* We can flush nothing more */
7766 		else
7767 		{
7768 			discard_cached_file(oldest_e);
7769 			done = (file_cache_size + needed) <= max_file_cache_size;
7770 		}
7771 	}
7772 }
7773 
7774 
7775 /*
7776  | Locate the specified file within the cache.
7777  | Returns NULL if the file is not cached
7778  */
find_cached_file(char * name)7779 static FileCacheEntry *find_cached_file(char *name)
7780 {
7781 	int i;
7782 	FileCacheEntry *fce = file_cache;
7783 
7784 	for (i = 0; i < MAX_CACHE_ENTRIES; i++, fce++)
7785 	{
7786 		if (fce->name)
7787 			if (streq(fce->name, name))
7788 				return fce;
7789 	}
7790 	return NULL;
7791 }
7792 
7793 
7794 
7795 /*--------------------------------------------------------------------------*/
7796 /* Externally visible file cache stuff										*/
7797 /*--------------------------------------------------------------------------*/
7798 
7799 /*
7800  | Open a file...
7801  | Returns the file cache handle of the file, or NULL for failure.
7802  | Note that if mode is anything other than "r" the call defers to
7803  | my_fopen().
7804  |
7805  | NB: The returned handle is almost certainly *NOT* a |FILE*|
7806  | (although it may be if the cache cannot accomodate the file).
7807  |
7808  | Therefore, you *MUST* ensure that any file opened with cached_fopen()
7809  | is only ever accessed via cached_fgets() and cached_fclose().
7810  |
7811  | Failure to do so will result in, ahem, unpleasantness.  Extreme
7812  | unpleasantness.
7813  */
cached_fopen(char * name,char * mode)7814 FILE *cached_fopen(char *name, char *mode)
7815 {
7816 	FileCacheEntry *fcs = NULL;
7817 	int fch;
7818 
7819 	if (strcmp(mode, "r") || !use_filecache)
7820 		return my_fopen(name, mode);
7821 
7822 	if (!file_cache_initd)
7823 	{
7824 		init_file_cache();
7825 	}
7826 
7827 	if (max_file_cache_size >= 0)
7828 	{
7829 		/* Find a free cache entry */
7830 		for (fch = 0; fch < MAX_OPEN_CACHED_FILES; fch++)
7831 			if (!cached_file_handle[fch].fce)
7832 				break;
7833 
7834 		/* Out of handles? */
7835 		if (fch >= MAX_OPEN_CACHED_FILES)
7836 			return my_fopen(name, mode);
7837 
7838 		/* Is the file already cached? */
7839 		fcs = find_cached_file(name);
7840 		if (!fcs)
7841 		{
7842 			/* File wasn't cached, so cache it */
7843 			flush_file_cache(NULL);	/* Clean stuff out of the cache if need be */
7844 			fcs = cache_file(name);	/* Cache the new file */
7845 			flush_file_cache(fcs);	/* Flush, but keep the latest file */
7846 		}
7847 	}
7848 
7849 	/* Did we fail to cache the file? */
7850 	if (!fcs)
7851 	{
7852 		return my_fopen(name, mode);
7853 	}
7854 
7855 	/* File was cached OK */
7856 	cached_file_handle[fch].ptr = fcs->text;	/* Init sequential pointer */
7857 	cached_file_handle[fch].fce = fcs;	/* Cache block pointer */
7858 	fcs->used = fc_access_counter++;	/* Opening the file counts as an access */
7859 
7860 	return (FILE *)(&cached_file_handle[fch]);
7861 }
7862 
7863 
7864 /*
7865  | Close a file
7866  */
cached_fclose(FILE * fch_)7867 errr cached_fclose(FILE *fch_)
7868 {
7869 	CachedFileHandle *fch;
7870 
7871 	/* Is the FILE* genuine? */
7872 	if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
7873 		return my_fclose(fch_);
7874 
7875 	fch = (CachedFileHandle *) fch_;
7876 
7877 	/* Check for "Ooopses": */
7878 	if (fch->fce == NULL)
7879 		core("cached_fclose called on a non-open file handle");
7880 
7881 	flush_file_cache(NULL);		/* Clean out the cache if need be */
7882 	fch->fce = NULL;			/* Mark file handle as inactive */
7883 
7884 	return 0;
7885 }
7886 
7887 
7888 /*
7889  | Do the my_fgets thing on a file
7890  */
cached_fgets(FILE * fch_,char * buffer,int max_len)7891 errr cached_fgets(FILE *fch_, char *buffer, int max_len)
7892 {
7893 	CachedFileHandle *fch;
7894 	char *eof;
7895 	char *ptr;
7896 
7897 	/* Is the FILE* genuine? */
7898 	if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
7899 		return my_fgets(fch_, buffer, max_len);
7900 
7901 	fch = (CachedFileHandle *) fch_;
7902 
7903 	/* Check for "Oopses": */
7904 	if (!file_cache_initd)
7905 		core("cached_fgets() on uninitialised file-cache");
7906 	if (!fch->fce)
7907 		core("cached_fgets called for a un-open file");
7908 
7909 	eof = fch->fce->eof;
7910 	ptr = fch->ptr;
7911 
7912 	/* Out of bounds? */
7913 	if (ptr >= eof)
7914 	{
7915 		return 1;
7916 	}							/* Read failed */
7917 
7918 	/*
7919 	   | Read the next line, up to \0 (which would have v=been \n in the original file),
7920 	   | or max_len-1 characters
7921 	 */
7922 	if (fch->fce->compressed)
7923 		ptr += decompress_string(buffer, ptr, max_len);
7924 	else
7925 	{
7926 		if (eof - ptr < max_len)
7927 		{
7928 			max_len = eof - ptr;
7929 		}
7930 		for (; max_len >= 1; max_len--)
7931 			if ((*buffer++ = *ptr++) == 0)
7932 				break;
7933 		*buffer = 0;			/* terminate (paranoia) */
7934 	}
7935 
7936 	/* Update sequential pointer */
7937 	fch->ptr = ptr;
7938 
7939 	return 0;
7940 }
7941 
7942 #endif /* USE_FILECACHE */
7943 
7944 
7945 /*
7946  | This section deals with checking that the .raw files are up to date
7947  | wrt to the .txt files.  Note that this function won't work for RISC OS 2
7948  | (due to the lack of OS_Args 7) so it simply returns 0 to indicate that
7949  | the file isn't OOD.
7950  |
7951  | For this to work, the equivalent function (in init2.c) needs to be
7952  | #if-d out (and this function should be declared).  You'll probably
7953  | also need to zap the UNIX #includes at the top of the file
7954  */
7955 
check_modification_date(int fd,cptr template_file)7956 extern errr check_modification_date(int fd, cptr template_file)
7957 {
7958 	char raw_buf[1024];
7959 	char txt_buf[1024];
7960 	int i;
7961 	os_error *e;
7962 
7963 	if (os_version() < 300)
7964 	{
7965 		return 0;
7966 	}
7967 
7968 	/* Use OS_Args 7 to find out the pathname 'fd' refers to */
7969 	e = SWI(6, 0, SWI_OS_Args,
7970 			/* In: */
7971 			7,					/* Get path from filehandle */
7972 			fd,					/* file handle */
7973 			raw_buf,			/* buffer */
7974 			0, 0,				/* unused */
7975 			1024				/* size of buffer */
7976 			/* No output regs used */
7977 		);
7978 	if (e)
7979 	{
7980 		core(e->errmess);
7981 	}
7982 
7983 	/* Build the path to the template_file */
7984 	path_make(txt_buf, ANGBAND_DIR_EDIT, template_file);
7985 
7986 	i = file_is_newer(riscosify_name(txt_buf), raw_buf);
7987 
7988 	return i;
7989 }
7990 
7991 
7992 /*--------------------------------------------------------------------------*/
7993 
7994 /*
7995  | This is the hideous IClear hack.  Basically what we do is to take a copy
7996  | of the module and kill it in the RMA.  Then, on return to the desktop
7997  | we reinstate the module.
7998  |
7999  | NB: This is truly, truly evil.
8000  */
8001 
8002 static int *iclear_module = NULL;
8003 
8004 /*
8005  | Start the IClear hack
8006  */
iclear_hack(void)8007 static void iclear_hack(void)
8008 {
8009 	os_error *e;
8010 	int code = 0;
8011 	int *i;
8012 
8013 	/* Get base address of module */
8014 	e = SWI(2, 4, SWI_OS_Module, 18, "IClear", 0, 0, 0, &code);
8015 	if (e || !code)
8016 		return;
8017 
8018 	/* Module size is at code!-4 */
8019 	i = (int *)code;
8020 	iclear_module = malloc(i[-1] + 4);
8021 	if (!iclear_module)
8022 		return;
8023 
8024 	/* Copy the module */
8025 	*iclear_module = i[-1];
8026 	memcpy(iclear_module + 1, (void *)code, i[-1]);
8027 
8028 	/* Kill the current version */
8029 	e = SWI(2, 0, SWI_OS_Module, 4, "IClear");
8030 	if (e)
8031 	{
8032 		free(iclear_module);
8033 		iclear_module = NULL;
8034 	}
8035 }
8036 
8037 
8038 /*
8039  | Remove the IClear hack
8040  */
remove_iclear_hack(void)8041 static void remove_iclear_hack(void)
8042 {
8043 	os_error *e;
8044 
8045 	if (!iclear_module)
8046 		return;
8047 
8048 	e = SWI(3, 0, SWI_OS_Module, 11, iclear_module + 1, *iclear_module);
8049 	if (e)
8050 		debug("Failed to reinstall IClear: %s", e->errmess);
8051 
8052 	free(iclear_module);
8053 	iclear_module = NULL;
8054 }
8055 
8056 
8057 
8058 /*--------------------------------------------------------------------------*/
8059 
8060 /* Alarm functions */
8061 static int alarm_ackd = 0;	/* has the alarm been acknowledged? */
8062 static window_handle aw = 0;	/* alarm window */
8063 
8064 /*
8065  | Is the alarm due to go off, ie. is it enabled, and if so
8066  | does the current time match the alarm time?
8067  */
check_alarm()8068 static void check_alarm()
8069 {
8070 	time_t t;
8071 	struct tm *lt;
8072 
8073 	alarm_lastcheck = Time_Monotonic();
8074 
8075 	time(&t);
8076 	lt = localtime(&t);
8077 	if (lt->tm_hour == alarm_h && lt->tm_min == alarm_m)
8078 	{
8079 		if (!alarm_ackd) alarm_disp = 1;
8080 	}
8081 	else
8082 	{
8083 		alarm_ackd = 0;
8084 	}
8085 
8086 	/* Hack: if the alarm has already been acknowledged then don't re-trigger it */
8087 	if (alarm_ackd)
8088 	{
8089 		alarm_disp = 0;
8090 	}
8091 
8092 	/* Hack: if the alarm should make a noise, then make one: */
8093 	if (alarm_disp && alarm_beep == 1)
8094 	{
8095 		static unsigned int last_beep = 0;
8096 		unsigned int t = Time_Monotonic();
8097 		if (t > last_beep + 100)
8098 		{
8099 			Sound_SysBeep();
8100 			last_beep = t;
8101 		}
8102 	}
8103 
8104 	/*
8105 	   | If we're in the desktop then fire the alarm off if need be.
8106 	   | If we aren't then do nothing - the fullscreen bored() function
8107 	   | will take care of the alarm.
8108 	 */
8109 #ifndef FULLSCREEN_ONLY
8110 	if (!fullscreen_font && alarm_disp)
8111 		trigger_alarm_desktop();
8112 #endif /* FULLSCREEN_ONLY */
8113 }
8114 
8115 
ack_alarm(void)8116 static void ack_alarm(void)
8117 {
8118 	if (aw)
8119 	{
8120 		Window_Delete(aw);
8121 		aw = 0;
8122 	}
8123 	alarm_ackd = 1;
8124 	alarm_disp = 0;
8125 
8126 	if (alarm_type == 3)
8127 	{
8128 		/* One shot alarm */
8129 		alarm_type = 0;
8130 		write_alarm_choices();
8131 	}
8132 
8133 }
8134 
8135 #ifndef FULLSCREEN_ONLY
8136 /*
8137  | Click in the (desktop) alarm window
8138  */
Hnd_AlarmClick(event_pollblock * pb,void * ref)8139 static BOOL Hnd_AlarmClick(event_pollblock * pb, void *ref)
8140 {
8141 	ack_alarm();
8142 	return TRUE;
8143 }
8144 
8145 
8146 /*
8147  | The alarm has gone off in the desktop
8148  */
trigger_alarm_desktop(void)8149 static void trigger_alarm_desktop(void)
8150 {
8151 	char buffer[120];
8152 	if (aw)
8153 		return;
8154 
8155 	aw = Window_Create("alarm", template_TITLEMIN);
8156 	if (!aw)
8157 	{
8158 		core("failed to create Alarm window!");
8159 	}
8160 	sprintf(buffer, "Alarm from %s", VARIANT);
8161 	Window_SetTitle(aw, buffer);
8162 	Event_Claim(event_CLICK, aw, 0, Hnd_AlarmClick, NULL);
8163 	Event_Claim(event_CLOSE, aw, event_ANY, Hnd_AlarmClick, NULL);
8164 
8165 	Icon_printf(aw, 1, "An alarm was set for %02d:%02d", alarm_h, alarm_m);
8166 	Icon_SetText(aw, 2, alarm_message);
8167 	Window_Show(aw, open_CENTERED);
8168 }
8169 
8170 #endif /* FULLSCREEN_ONLY */
8171 
8172 
8173 /*--------------------------------------------------------------------------*/
8174 
8175 #ifndef FE_DEBUG_INFO
show_debug_info(void)8176 static void show_debug_info(void)
8177 {
8178 	core("main-acn internal logic error 004");
8179 }
8180 #else
8181 
8182 static int debug_cx = 0;
8183 static int debug_cy = 0;
8184 static int debug_cl = TERM_WHITE;
8185 static int debug_sl = 0;
8186 
8187 
debug_cls(void)8188 static void debug_cls(void)
8189 {
8190 	Term_clear();
8191 	debug_cx = debug_cy = debug_sl = 0;
8192 }
8193 
debug_tcol(int c)8194 static void debug_tcol(int c)
8195 {
8196 	debug_cl = c;
8197 }
8198 
8199 
debug_scroll(void)8200 static void debug_scroll(void)
8201 {
8202 	char **c = ((term_data *)Term)->t.scr->c;	/* char array [24][80] */
8203 	byte **a = ((term_data *)Term)->t.scr->a;	/* attr array [24][80] */
8204 	int cc;
8205 	char tmp[82];
8206 	int y, x, p;
8207 
8208 	cc = a[1][0];
8209 
8210 	for (y = 1; y < 23; y++)
8211 	{
8212 	    Term_gotoxy(0, y - 1);
8213 
8214 		for (x = p = 0; x < 80; x++)
8215 		{
8216 			Term_addch(a[y][x], c[y][x]);
8217 		}
8218 	}
8219 
8220 	Term_erase(0, 22, 80);
8221 }
8222 
8223 
debug_print_line(char * l)8224 static void debug_print_line(char *l)
8225 {
8226 	char *le;
8227 	int cr = 0;
8228 
8229 	/* Handle scrolling */
8230 	if (debug_cy > 22)
8231 	{
8232 		debug_cy = 22;
8233 		if (--debug_sl < 0)
8234 		{
8235 			int k;
8236 			put_fstr(0, 23, CLR_YELLOW "[RET one line, SPC one page]");
8237 			do
8238 			{
8239 				k = inkey();
8240 			}
8241 			while (k != 32 && k != 13);
8242 			Term_erase(0, 23, 79);
8243 			debug_sl = k == 32 ? 21 : 0;
8244 		}
8245 		debug_scroll();
8246 	}
8247 
8248 	/* Hack: check for NL */
8249 	for (le = l; *le; le++)
8250 		if (*le == '\n')
8251 		{
8252 			cr = 1;
8253 			break;
8254 		}
8255 
8256 	/* display text */
8257 	put_fstr(debug_cx, debug_cy, "$%c%.*s", 'A' + debug_cl, l);
8258 
8259 	/* move cursor */
8260 	if (!cr)
8261 	{
8262 		debug_cx += (le - l);
8263 		if (debug_cx >= 80)
8264 		{
8265 			cr = 1;
8266 		}
8267 	}
8268 	if (cr)
8269 	{
8270 		debug_cx = 0;
8271 		debug_cy += 1;
8272 	}
8273 
8274 	Term_gotoxy(debug_cx, debug_cy);
8275 
8276 }
8277 
8278 
debug_next_line(char * lb,char ** t,int cx)8279 static int debug_next_line(char *lb, char **t, int cx)
8280 {
8281 	int i = 0;
8282 	char *lt = *t;
8283 
8284 	if (!*lt)
8285 	{
8286 		return -1;
8287 	}							/* Out of text */
8288 
8289 	while (*lt && cx < 80)
8290 	{
8291 		lb[i] = *lt++;
8292 
8293 		if (lb[i] == '\n')		/* New line */
8294 		{
8295 			cx = 0;				/* Cursor x will be 0 after displaying */
8296 			i++;				/* Keep the \n in the output */
8297 			break;				/* All done */
8298 		}
8299 		else if (lb[i] == '\t')	/* Tab */
8300 		{
8301 			while (cx < 80)
8302 			{
8303 				lb[i++] = ' ';
8304 				cx++;
8305 				if ((cx & 7) == 0)
8306 				{
8307 					break;
8308 				}
8309 			}
8310 		}
8311 		else					/* Anything else */
8312 		{
8313 			cx++;
8314 			i++;
8315 		}
8316 	}
8317 
8318 	lb[i] = 0;					/* terminate line buffer */
8319 	*t = lt;					/* update text pointer */
8320 	return cx;					/* return cursor x after printing */
8321 }
8322 
debug_printf(char * fmt,...)8323 static void debug_printf(char *fmt, ...)
8324 {
8325 	char buffer[1024];
8326 	char line[82];
8327 	va_list ap;
8328 	char *p = buffer;
8329 
8330 	va_start(ap, fmt);
8331 	vsprintf(buffer, fmt, ap);
8332 	va_end(ap);
8333 
8334 	/* Now split the string into display lines */
8335 	while (debug_next_line(line, &p, debug_cx) >= 0)
8336 		debug_print_line(line);
8337 }
8338 
8339 
debug_version_info(void)8340 static void debug_version_info(void)
8341 {
8342 	debug_tcol(TERM_YELLOW);
8343 
8344 	debug_printf("\n\nMisc. Info:\n");
8345 	debug_tcol(TERM_WHITE);
8346 	debug_printf("\tVariant name = \"%s\"\n", VARIANT);
8347 	debug_printf("\tFront-end version: %s\n", PORTVERSION);
8348 	debug_printf("\tFront-end compiled: %s %s\n", __TIME__, __DATE__);
8349 	debug_printf("\tCompile time flags:\n");
8350 
8351 #ifdef USE_FILECACHE
8352 	debug_printf("\t\tUSE_FILECACHE\n");
8353 #endif
8354 
8355 #ifdef ABBR_FILECACHE
8356 	debug_printf("\t\tABBR_FILECACHE\n");
8357 #endif
8358 
8359 #ifdef SMART_FILECACHE
8360 	debug_printf("\t\tSMART_FILECACHE\n");
8361 #endif
8362 
8363 	debug_tcol(TERM_YELLOW);
8364 	debug_printf("\nResource path:\n");
8365 	debug_tcol(TERM_WHITE);
8366 	debug_printf("\t\"%s\"\n", resource_path);
8367 
8368 	debug_tcol(TERM_YELLOW);
8369 	debug_printf("\nTempfile path:\n");
8370 	debug_tcol(TERM_WHITE);
8371 	debug_printf("\t\"%s\"\n", scrap_path);
8372 	debug_printf("\tScrapfiles are %s deleted at exit.\n",
8373 				 (flush_scrap ? "" : "NOT"));
8374 
8375 	debug_tcol(TERM_YELLOW);
8376 	debug_printf("\nChoices files:\n");
8377 	debug_tcol(TERM_L_BLUE);
8378 	debug_printf("\tDesired files:\n");
8379 	debug_tcol(TERM_WHITE);
8380 	debug_printf("\tPrimary (r/w): \"%s\"\n", choices_file[CHFILE_WRITE]);
8381 	debug_printf("\t Fallback (r): \"%s\"\n", choices_file[CHFILE_READ]);
8382 	debug_printf("\t Mirror (r/w): \"%s\"\n", choices_file[CHFILE_MIRROR]);
8383 	debug_tcol(TERM_L_BLUE);
8384 	debug_printf("\tActual files:\n");
8385 	debug_tcol(TERM_WHITE);
8386 	debug_printf("\t        Write: \"%s\"\n", find_choices(TRUE));
8387 	debug_printf("\t         Read: \"%s\"\n", find_choices(FALSE));
8388 
8389 	debug_tcol(TERM_YELLOW);
8390 	debug_printf("\nAlarm files:\n");
8391 	debug_tcol(TERM_L_BLUE);
8392 	debug_printf("\tDesired files:\n");
8393 	debug_tcol(TERM_WHITE);
8394 	debug_printf("\tPrimary (r/w): \"%s\"\n", alarm_file[CHFILE_WRITE]);
8395 	debug_printf("\t Fallback (r): \"%s\"\n", alarm_file[CHFILE_READ]);
8396 	debug_tcol(TERM_L_BLUE);
8397 	debug_printf("\tActual files:\n");
8398 	debug_tcol(TERM_WHITE);
8399 	debug_printf("\t        Write: \"%s\"\n", find_alarmfile(TRUE));
8400 	debug_printf("\t         Read: \"%s\"\n", find_alarmfile(FALSE));
8401 #ifdef USE_DA
8402 	debug_tcol(TERM_YELLOW);
8403 	debug_printf("\nDynamic areas:\n");
8404 	debug_tcol(TERM_WHITE);
8405 	debug_printf("\tFontcache DA = %d\t", font_area);
8406 	debug_printf("size = %d\theap size = %d\n", font_area_size, font_heap_size);
8407 	debug_printf("\t   ralloc DA = %d\t", game_area);
8408 	debug_printf("size = %d\theap size = %d\n", game_area_size, game_heap_size);
8409 #endif
8410 }
8411 
8412 
debug_filecache_info(void)8413 static void debug_filecache_info(void)
8414 {
8415 #ifndef USE_FILECACHE
8416 	debug_tcol(TERM_L_DARK);
8417 	debug_printf("File cache disabled at compile time.\n");
8418 #else
8419 	int j, k;
8420 	int t, cs, ucs, cf;
8421 
8422 	cf = cs = ucs = j = k = 0;	/* To stop an usused warning if USE_FILECACHE is undefined */
8423 	t = strlen(resource_path);
8424 
8425 	if (!file_cache_initd)
8426 	{
8427 		init_file_cache();
8428 	}							/* Paranoia */
8429 	debug_tcol(TERM_YELLOW);
8430 	debug_printf("\nFilecache contents:\n");
8431 	debug_tcol(TERM_L_BLUE);
8432 	debug_printf("Flags: Smart=%d;  Abbrv=%d;  Slave=%d;   Enable=%d\n",
8433 				 smart_filecache, abbr_filecache, abbr_tmpfile, use_filecache);
8434 	debug_tcol(TERM_SLATE);
8435 	if (smart_filecache || abbr_filecache)
8436 		debug_printf("\t\t%3s  %6s/%-6s  %6s  %6s  Path (relative to lib/)\n",
8437 					 "Hnd", "Cache", "Disc", "Time", "Status");
8438 	else
8439 		debug_printf("\t\t%3s  %6s  %6s  %6s  Path (relative to lib/)\n", "Hnd",
8440 					 "Size", "Time", "Status");
8441 
8442 	for (j = 0; j < MAX_CACHE_ENTRIES; j++)
8443 	{
8444 		FileCacheEntry *fce = &(file_cache[j]);
8445 		if (fce->name)
8446 		{
8447 			cf++;
8448 			debug_tcol(TERM_L_GREEN);
8449 			debug_printf("\t\t%3d  ", j);
8450 			debug_tcol(TERM_L_UMBER);
8451 			if (!smart_filecache && !abbr_filecache)
8452 				debug_printf("%6d  ", fce->eof - fce->text);
8453 			else
8454 			{
8455 				debug_printf("%6d/", fce->eof - fce->text);
8456 				k = myFile_Size(riscosify_name(fce->name));
8457 				debug_printf("%-6d  ", k);
8458 				if (k > 0)
8459 				{
8460 					ucs += k;
8461 				}
8462 			}
8463 			cs += fce->eof - fce->text;
8464 			debug_printf("%6d  ", fce->used);
8465 			for (k = 0; k < MAX_OPEN_CACHED_FILES; k++)
8466 				if (cached_file_handle[k].fce == fce)
8467 					break;
8468 			debug_tcol(TERM_RED);
8469 			debug_printf("%-6s  ", k < MAX_OPEN_CACHED_FILES ? "Open" : "");
8470 			debug_tcol(TERM_L_UMBER);
8471 			debug_printf("%s\n", fce->name + t);
8472 		}
8473 	}
8474 
8475 	debug_tcol(TERM_L_BLUE);
8476 	debug_printf("\tTotal:\t%3d  ", cf);
8477 	if (ucs)
8478 		debug_printf("%6d/%-6d\n", cs, ucs);
8479 	else
8480 		debug_printf("%6d\n", cs);
8481 	debug_tcol(TERM_BLUE);
8482 #endif /* USE_FILECACHE */
8483 }
8484 
8485 
show_debug_info(void)8486 static void show_debug_info(void)
8487 {
8488 	int k;
8489 	/* blank the term */
8490 	debug_cls();
8491 
8492 	/* Repeatedly prompt for a command */
8493 	do
8494 	{
8495 		debug_tcol(TERM_VIOLET);
8496 		debug_printf("\nInfo: (V)ersion, (F)ilecache, ESC=exit ");
8497 		do
8498 		{
8499 			k = inkey();
8500 			switch (k)
8501 			{
8502 				case 'v':  case 'V': debug_version_info();
8503 					break;
8504 				case 'f':  case 'F': debug_filecache_info
8505 						();
8506 					break;
8507 				case 27: break;
8508 				default: k = 0;
8509 			}
8510 		}
8511 		while (!k);
8512 	}
8513 	while (k != 27);
8514 
8515 	Term_clear();
8516 }
8517 
8518 #endif /* FE_DEBUG_INFO */
8519 
8520 #endif /* __riscos */
8521 
8522 
8523