Angband 3.0.9b.
[angband.git] / src / main-ros.c
blob7fcc343bddbe7ca40a9e26c3e89a704767e95178
1 /*
2 * File: main-ros.c
3 * Purpose: Support for RISC OS versions of Angband
5 * Copyright (c) 2000-2007 Musus Umbra, Antony Sidwell, Thomas Harris,
6 * Andrew Sidwell, Ben Harrison.
8 * This work is free software; you can redistribute it and/or modify it
9 * under the terms of either:
11 * a) the GNU General Public License as published by the Free Software
12 * Foundation, version 2, or
14 * b) the "Angband licence":
15 * This software may be copied and distributed for educational, research,
16 * and not for profit purposes provided that this copyright and statement
17 * are included in all such copies. Other copyrights may also apply.
20 #ifdef __riscos
22 #include "angband.h"
25 * Purpose: Support for RISC OS Angband 2.9.x onwards (and variants)
26 * Current maintainer: Antony Sidwell <antony@isparp.co.uk> (ajps)
28 * NB: This code is still under continuous development - if you want to use
29 * it for your own compilation/variant, please contact me so that I can
30 * keep you up to date and give you support :)
32 * NB: This frontend will no longer work as-is for modern Zangbands (2.7.3
33 * onwards), as the display code has changed, and so our platform-specific
34 * menu needs adjusting to use the appropriate new functions. A version which
35 * should work should be supplied with the Z source. -- ajps
37 * NB: This frontend will not work with the in-development ToME3, as the file
38 * handling (and many other parts) have been completely overhauled. I suspect
39 * it is not worth trying to shoehorn that model into this code, and a fresh,
40 * UNIX-porting-tools approach may be better suited. -- ajps
42 * Prerequisites to compiling:
44 * DeskLib 2.50 or later
46 * An ANSI C compiler (tested with Acorn's C/C++ and GCC, but should be OK
47 * with any decent compiler)
49 * My binary distribution (for the templates and other bits)
51 * Note:
52 * The following symbols are *required* and *must* be defined properly.
56 * PORTVERSION
57 * This is the port version; it appears in the infobox.
59 #define PORTVERSION "1.34 (2007-06-24)"
62 * VARIANT & VERSION
63 * These two get variant and version data from Angband itself; older
64 * variants may not have these defined and will have to be altered.
66 #ifndef VARIANT
67 #define VARIANT VERSION_NAME
68 #endif
70 #ifndef VERSION
71 #define VERSION VERSION_STRING
72 #endif
75 * RISCOS_VARIANT
76 * This must match the entry in the !Variant Obey file, and it must only
77 * contain characters that are valid as part of a RISC OS path variable.
78 * [eg. "Yin-Yangband" is not okay, "EyAngband" is.]
80 #ifndef RISCOS_VARIANT
81 #define RISCOS_VARIANT "Angband"
82 #endif
85 * AUTHORS
86 * For the info box. [eg. "Ben Harrison"]
88 #ifndef AUTHORS
89 #define AUTHORS "Robert Ruehlmann"
90 #endif
93 * PORTERS
94 * For the info box. [eg. "Musus Umbra"]
96 #ifndef PORTERS
97 #define PORTERS "Antony Sidwell"
98 #endif
101 * ICONNAME
102 * Iconbar icon sprite name eg. "!angband". Note that this must be a valid
103 * sprite name; it may need modifying for long variant names.
105 #ifndef ICONNAME
106 #define ICONNAME "!"RISCOS_VARIANT
107 #endif
110 * PDEADCHK
111 * This should expand to an expression that is true if the player is dead.
112 * Examples (correct as of Feb 2004):
113 * Vanilla (and most variants): #define PDEADCHK (p_ptr->is_dead)
114 * Zangband: #define PDEADCHK (p_ptr->state.is_dead)
115 * Tome: #define PDEADCHK (!alive)
116 * SCthAngband: #define PDEADCHK (!alive || death)
118 #ifndef PDEADCHK
119 #define PDEADCHK (p_ptr->is_dead)
120 #endif
123 * MEMTYPE
124 * This defines which of the various sets of memory allocation prototypes
125 * should be used for this variant.
127 * 1: The pre 2.9.x type, where g_free returns an errr (largely obsolete)
128 * 2: The 2.9.x type, where sizes are type "huge" and g_free takes a size.
129 * 3: The 2.9.7(ish)+ type, where they take sizes as size_t
131 #ifndef MEMTYPE
132 #define MEMTYPE 3
133 #endif
136 * FD_TYPE
137 * Many of the variants based on older Angbands use huge rather than
138 * size_t in the fd_* functions. There are other halfway changes too,
139 * so you have to pick one of three for each variant. :(
141 #ifndef FDTYPE
142 #define FDTYPE 3
143 #endif
146 * HAS_MY_STRCPY, HAS_MY_STRCAT, HAS_MY_STRNICMP
147 * We require the definition of two functions: my_strcat and my_strcpy.
148 * Some variants already have these defined (e.g. modern Vanilla), and
149 * so these should be defined when compiling those.
152 #define HAS_MY_STRCPY
153 #define HAS_MY_STRCAT
154 #define HAS_MY_STRNICMP
158 * BIGSCREEN
159 * This should be TRUE if the variant actually supports bigscreen, FALSE
160 * otherwise.
162 #ifndef BIGSCREEN
163 #define BIGSCREEN FALSE
164 #endif
167 * HASNOCORE
168 * In case someone's removed the core() function in an attempt
169 * to clean up the code but just making work for people with no
170 * ultimate benefit.
172 #define HASNOCORE
175 * USE_DA
176 * If defined, it enables the use of dynamic areas (these are still only
177 * used when the !Variant file allows it). It is likely that this option
178 * will eventually be removed altogether as there is no major advantege
179 * to using DAs over just using the Wimpslot.
181 /* #define USE_DA */
184 * FULLSCREEN_ONLY
185 * If defined, the Wimp window-based interface will be disabled, and the
186 * game will only play in fullscreen mode. This turns out to be a pointless
187 * feature, as the memory saving is now negligable compared to the overall
188 * size of the game.
190 /* #define FULLSCREEN_ONLY */
193 * ZANGBAND_TERM_PACKAGE
194 * New version of Zangband (2.7.3ish and later) use a new term streamlined
195 * term package including a put_fstr function we have to use for our
196 * "platform specific menu" in the game. Define this if you are compiling
197 * such a version of Zangband or (who knows?) a variant.
199 /* #define ZANGBAND_TERM_PACKAGE */
203 * The following symbols control the (optional) file-cache:
204 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
205 * NOTE (11th Dec 2004): The file caches have not been used for the last
206 * few years - I do not believe they still work with most variants, except
207 * possibly some branched off old Zangband (e.g. Cth). The introduction of
208 * scripting into those variants which used to use large plain text files
209 * seems to have diminished their usefulness. -- Antony Sidwell
211 * NB: Variants that don't repeatedly read any files whilst running
212 * (eg. vanilla, sang, etc) should NOT define USE_FILECACHE, etc. as
213 * it causes a non-negligable amount of code to be compiled in.
215 * NB: The file-cache functions require that some code in files.c is modified
216 * to use the cached_* functions. This should be utterly trivial.
218 * NB: The returned handle from cached_fopen() is almost certainly *NOT*
219 * a |FILE*| (although it may be if the cache cannot accomodate the file).
221 * Therefore, you *MUST* ensure that any file opened with cached_fopen()
222 * is only ever accessed via cached_fgets() and cached_fclose().
224 * Failure to do so will result in, ahem, unpleasantness. Extreme
225 * unpleasantness. "Him fall down, go boom."
227 * This /may/ change in the near future (ie. to apply caching in a
228 * transparent manner), so do keep a backup of files.c (and any other files
229 * you modify). You always keep backups anyway, don't you? Don't you?!
233 * USE_FILECACHE
234 * if defined then some caching functions will be compiled for use by the
235 * various get_rnd_line(), etc. in files.c. This could be used in a
236 * variety of places that read data repeatedly, but it's up to you to
237 * implement it then.
240 /* #define USE_FILECACHE */
243 * SMART_FILECACHE
244 * This causes lines beginning with '#' (and blank lines) to be discarded
245 * when caching files. This should help Zangband 2.2.5+ but could cause
246 * trouble for other variants. If defined, then smart file caching will be
247 * on by default.
250 /* #define SMART_FILECACHE */
253 * ABBR_FILECACHE
254 * ABBR_FILECACHE causes data read into file-cache to be compressed (using a
255 * simple set of abbreviations) by default. This can be overridden using a
256 * command line option. If this symbol is not defined then no compression
257 * code will be compiled and the user option will be ignored/unavailable.
260 /* #define ABBR_FILECACHE */
263 * Note:
264 * The following symbols control debugging information.
268 * FE_DEBUG_INFO
269 * If defined, some functions will be compiled to display some info. on the
270 * state of the front-end (accessible) from the '!' user menu.
272 * NB: For actual releases you should NOT define this symbol since it causes
273 * a non-negligable amount of code/data to be sucked in.
275 /* #define FE_DEBUG_INFO */
278 /* sCthAngband oddities */
279 #ifdef IS_SCTH
280 #define SAVE_PLAYER_PARAM FALSE
281 #define PAUSE_LINE_PARAM
282 #define TERM_NAME(n) windows[n].name
283 #define TERM(i) windows[i].term
284 extern errr check_modification_date(int fd, cptr template_file);
285 #else
286 #define SAVE_PLAYER_PARAM
287 #define PAUSE_LINE_PARAM 23
288 #define TERM_NAME(n) angband_term_name[n]
289 #define TERM(i) angband_term[i]
290 #endif
293 /* NPP (for now) oddities */
294 #ifdef HASNOCORE
295 extern void core(cptr str);
296 #endif
298 /* V, post3.0.7, has conflicting types for these we have to #define around */
299 #undef event_type
300 #undef menu_flags
301 #undef menu_item
303 /* Constants, etc. ---------------------------------------------------------*/
305 /* Deal with any weird file-caching symbols */
306 #ifndef USE_FILECACHE
307 # undef ABBR_FILECACHE
308 # undef SMART_FILECACHE
309 #endif
311 /* Maximum terminals */
312 #define MAX_TERM_DATA 8
314 /* Menu entry numbers */
315 enum
317 IBAR_MENU_INFO = 0,
318 IBAR_MENU_SAVE,
319 IBAR_MENU_FULLSCREEN,
320 IBAR_MENU_GAMMA,
321 IBAR_MENU_SOUND,
322 IBAR_MENU_WINDOWS,
323 IBAR_MENU_SAVECHOICES,
324 IBAR_MENU_QUIT
327 enum
329 TERM_MENU_INFO = 0,
330 TERM_MENU_SAVE,
331 TERM_MENU_SIZE,
332 TERM_MENU_FONT,
333 TERM_MENU_WINDOWS
336 /* Icon numbers */
337 #define SND_VOL_SLIDER 0
338 #define SND_VOL_DOWN 1
339 #define SND_VOL_UP 2
340 #define SND_ENABLE 3
342 #define GAMMA_ICN 0
343 #define GAMMA_DOWN 1
344 #define GAMMA_UP 2
346 #define SAVE_ICON 2
347 #define SAVE_PATH 1
348 #define SAVE_OK 0
349 #define SAVE_CANCEL 3
351 #define SIZE_WIDTH 1
352 #define SIZE_HEIGHT 4
353 #define SIZE_CANCEL 7
354 #define SIZE_SET 6
356 /* Position and size of the colours strip in the gamma window */
357 #define GC_XOFF 20
358 #define GC_YOFF -14
359 #define GC_WIDTH 512
360 #define GC_HEIGHT 72
362 /* Maximum and minimum allowed volume levels */
363 #define SOUND_VOL_MIN 16
364 #define SOUND_VOL_MAX 176
366 /*--------------------------------------------------------------------------*/
369 #undef rename
370 #undef remove
371 #undef UNUSED
373 #include "Desklib:Event.h"
374 #include "Desklib:EventMsg.h"
375 #include "Desklib:Template.h"
376 #include "Desklib:Window.h"
377 #include "Desklib:Handler.h"
378 #include "Desklib:Screen.h"
379 #include "Desklib:Menu.h"
380 #include "Desklib:Msgs.h"
381 #include "Desklib:Icon.h"
382 #include "Desklib:Resource.h"
383 #include "Desklib:SWI.h"
384 #include "DeskLib:Str.h"
385 #include "Desklib:Time.h"
386 #include "Desklib:Sound.h"
387 #include "Desklib:KeyCodes.h"
388 #include "Desklib:Kbd.h"
389 #include "Desklib:GFX.h"
390 #include "Desklib:ColourTran.h"
391 #include "Desklib:Error.h"
392 #include "Desklib:Coord.h"
393 #include "Desklib:Slider.h"
394 #include "Desklib:Hourglass.h"
395 #include "Desklib:Save.h"
396 #include "Desklib:KernelSWIs.h"
397 #include "DeskLib:File.h"
398 #include "DeskLib:Filing.h"
400 #include <stdlib.h>
401 #include <stdio.h>
402 #include <string.h>
403 #include <ctype.h>
404 #include <signal.h>
405 #include <stdarg.h>
406 #include <time.h>
407 #include <math.h>
409 /*--------------------------------------------------------------------------*/
412 | We use the hourglass around calls to Wimp_Poll in an attempt to stop
413 | users thinking that the game has 'hung'.
414 | Kamband/Zangband and the Borg in particular can have quite long delays at
415 | times.
419 * Note that empty-bracketed macros aren't ANSI/ISO C89 defined. GCC and
420 * Norcroft don't mind them though.
422 #define START_HOURGLASS \
423 do { if (use_glass && !glass_on) { glass_on=1; Hourglass_Start(50); }} while (0)
424 #define STOP_HOURGLASS \
425 do { if (glass_on) { glass_on=0; Hourglass_Off(); } } while (0)
428 /*--------------------------------------------------------------------------*/
429 /* Types */
430 /*--------------------------------------------------------------------------*/
433 | A ZapRedraw block
435 typedef struct
437 union
439 unsigned int value;
440 struct
442 unsigned int vdu:1;
443 unsigned int double_height:1;
444 unsigned int extension:1;
445 unsigned int padding:29;
447 bits;
449 r_flags;
451 int r_minx; /* min x of redraw in pixels from LHS, incl */
452 int r_miny; /* min y of redraw in pixels from top, incl */
453 int r_maxx; /* max x of redraw in pixels from LHS, excl */
454 int r_maxy; /* max y of redraw in pixels from top, excl */
456 void *r_screen; /* DSA: address of screen to write to (0=>read) */
457 int r_bpl; /* DSA: bytes per raster line */
459 int r_bpp; /* log base 2 of bits per pixel */
460 int r_charw; /* width of a character in pixels */
461 int r_charh; /* height of a character in pixels */
462 void *r_caddr; /* DSA: ->character cache | VDU: ->font name */
463 int r_cbpl; /* DSA: #bytes/character line | VDU: x OS offset */
464 int r_cbpc; /* DSA: #bytes/character | VDU: y OS offset */
466 int r_linesp; /* line spacing (pixels) */
468 void *r_data; /* -> text to display */
469 int r_scrollx; /* see Redraw dox */
470 int r_scrolly; /* see Redraw dox */
472 void *r_palette; /* -> palette lookup table */
473 int r_for; /* foreground colour at start of line */
474 int r_bac; /* background colour at start of line */
476 void *r_workarea; /* -> word aligned workspace */
478 int r_magx; /* log2 x OS coords per pixel */
479 int r_magy; /* log2 y OS coords per pixel */
481 int r_xsize; /* width of screen in pixels */
482 int r_ysize; /* height of screen in pixels */
484 unsigned int r_mode; /* current screen mode */
486 ZapRedrawBlock;
490 | We cache font data using an array of 'font handles' (since there is a
491 | known maximum no. of fonts required).
492 | This is what a font 'handle' looks like:
494 typedef struct
496 char *name; /* font name */
497 int usage; /* usage count */
498 int w, h; /* width, height */
499 int f, l; /* first and last character defined */
500 void *bpp_1; /* source bitmap */
501 void *bpp_n; /* bitmap for the current screen mode */
503 ZapFont;
506 | A struct to hold all the data relevant to a term window
508 typedef struct
510 term t; /* The Term itself */
511 window_handle w; /* Window handle */
512 ZapFont *font; /* Font */
513 wimp_box changed_box; /* Area out of date */
514 struct
516 wimp_point pos; /* Cursor position */
517 BOOL visible; /* visibility flag */
519 cursor;
520 char name[12]; /* Name to give menus opened from the term */
521 int def_open; /* Open by default? */
522 wimp_box def_pos; /* default position */
523 wimp_point def_scroll; /* default scroll offset */
524 int unopened; /* Has this window not been opened yet? */
526 term_data;
530 /*--------------------------------------------------------------------------*/
531 /* ZapRedraw SWI numbers */
532 /*--------------------------------------------------------------------------*/
534 #define SWI_ZapRedraw_ 0x48480
535 #define SWI_ZapRedraw_RedrawArea (SWI_ZapRedraw_ + 0x00)
536 #define SWI_ZapRedraw_GetPaletteEntry (SWI_ZapRedraw_ + 0x01)
537 #define SWI_ZapRedraw_RedrawRaster (SWI_ZapRedraw_ + 0x02)
538 #define SWI_ZapRedraw_ConvertBitmap (SWI_ZapRedraw_ + 0x03)
539 #define SWI_ZapRedraw_PrepareDataLine (SWI_ZapRedraw_ + 0x04)
540 #define SWI_ZapRedraw_AddCursor (SWI_ZapRedraw_ + 0x05)
541 #define SWI_ZapRedraw_FindCharacter (SWI_ZapRedraw_ + 0x06)
542 #define SWI_ZapRedraw_MoveBytes (SWI_ZapRedraw_ + 0x07)
543 #define SWI_ZapRedraw_CachedCharSize (SWI_ZapRedraw_ + 0x08)
544 #define SWI_ZapRedraw_ConvBitmapChar (SWI_ZapRedraw_ + 0x09)
545 #define SWI_ZapRedraw_CreatePalette (SWI_ZapRedraw_ + 0x0a)
546 #define SWI_ZapRedraw_InsertChar (SWI_ZapRedraw_ + 0x0b)
547 #define SWI_ZapRedraw_ReadSystemChars (SWI_ZapRedraw_ + 0x0c)
548 #define SWI_ZapRedraw_ReverseBitmaps (SWI_ZapRedraw_ + 0x0d)
549 #define SWI_ZapRedraw_ReadVduVars (SWI_ZapRedraw_ + 0x0e)
550 #define SWI_ZapRedraw_GetRectangle (SWI_ZapRedraw_ + 0x0f)
551 #define SWI_ZapRedraw_AddVduBitmaps (SWI_ZapRedraw_ + 0x10)
552 #define SWI_ZapRedraw_CacheFontChars (SWI_ZapRedraw_ + 0x11)
553 #define SWI_ZapRedraw_SpriteSize (SWI_ZapRedraw_ + 0x12)
554 #define SWI_ZapRedraw_RedrawWindow (SWI_ZapRedraw_ + 0x13)
558 | Other SWI numbers that aren't defined in DeskLib's SWI.h:
560 #define SWI_ColourTrans_ReturnColourNumber 0x40744
561 #define SWI_Wimp_ReportError 0x400df
562 #define SWI_PlayIt_Volume 0x4d146
566 /*--------------------------------------------------------------------------*
567 | File scope variables |
568 *--------------------------------------------------------------------------*/
569 static int ftype = 0xffd; /* hack so saved games get the right type */
570 static int filehandle[16]; /* we keep track of open files with this */
571 static int openfiles = 0; /* how many files are currently open */
574 | Paths we use...
576 static char resource_path[260] = ""; /* Path pointng to "!Angband.Lib." */
577 static char scrap_path[260] = ""; /* Path to create scrap files on */
578 static char choices_file[3][260] =
579 { "", "", "" }; /* Choices paths (read/write, mirror, read) */
580 static char alarm_file[2][260] =
581 { "", "" }; /* Alarm choices paths (read/write, mirror, read) */
583 | So we can use something more meaningful later...
584 | NB: Mirror is only meaningful for Choices and we don't
585 | even reserve space for alarm_file[CHFILE_MIRROR].
587 #define CHFILE_WRITE 0
588 #define CHFILE_READ 1
589 #define CHFILE_MIRROR 2
592 | Other 'globals':
594 static int initialised = 0; /* Used to determine whether to try to save */
595 static int game_in_progress = 0; /* if Quit (or core() is called), etc. */
597 static byte a_palette[256][4]; /* a copy of the raw Angband palette */
598 static unsigned int palette[256]; /* palette as gamma'd bbggrrxx words */
599 static unsigned int zpalette[256]; /* And our version for ZapRedraw */
600 static int gamma = 100; /* assume gamma of 1.0 if unspecified */
602 static int enable_sound = 0; /* enable sound FX */
603 static int sound_volume = 127; /* Full volume */
604 static int force_mono = 0; /* force monochrome */
605 static int start_fullscreen = 0; /* start up full screen (added in 1.18) */
606 static int hack_flush = 0; /* Should TERM_XTRA_FLUSH wait for all keys to be released? */
607 static int flush_scrap = 1; /* Should any scrapfiles (incl. filecache) be deleted at exit? */
608 static int max_file_cache_size = 64 << 10;
609 static unsigned int vfiletype;
610 static int alarm_type = 0; /* is there an alarm set? */
611 static int alarm_h = 0, alarm_m = 0; /* alarm time (midnight) */
612 static char alarm_message[80] = "Time for bed!"; /* the message to give */
613 static int alarm_disp = 0; /* is the alarm being displayed? */
614 static int alarm_beep = 0; /* should be beep? */
615 static const char *alarm_types[] =
616 { "Off", "On (one-shot)", "On (repeating)", "On (one-shot)" };
617 static unsigned int alarm_lastcheck = 0;
620 /* A little macro to save some typing later: */
621 #define COLOUR_CHANGED(x) \
622 ( (angband_color_table[x][1]!=a_palette[x][1]) || \
623 (angband_color_table[x][2]!=a_palette[x][2]) || \
624 (angband_color_table[x][3]!=a_palette[x][3]) )
626 static int got_caret = 0; /* Do we own the caret? */
627 static int key_pressed = 0; /* 'Key has been pressed' Flag */
628 static int use_glass = 1; /* use the hourglass between WimpPolls? */
629 static int glass_on = 1; /* is the hourglass on? */
630 static int user_menu_active = FALSE; /* set to TRUE when the user menu is active */
632 /* Font system variables */
633 static ZapFont fonts[MAX_TERM_DATA + 1]; /* The +1 is for the system font */
635 /* The system font is always font 0 */
636 #define SYSTEM_FONT (&(fonts[0]))
638 /* Term system variables */
639 static term_data data[MAX_TERM_DATA]; /* One per term */
641 #ifndef FULLSCREEN_ONLY
642 static char *r_data = NULL; /* buffer for ZapRedraw data */
643 static int r_maxwid, r_maxhgt;
645 /* Wimp variables */
646 static icon_handle ibar_icon; /* Iconbar icon handle */
647 static window_handle info_box; /* handle of the info window */
648 static window_handle resize_win; /* term resizing window */
649 static window_handle gamma_win; /* gamma correction window */
650 static window_handle sound_win; /* sound options window */
651 static window_handle save_box; /* The savebox */
652 static menu_ptr ibar_menu; /* Iconbar menu */
653 static menu_ptr term_menu; /* Term window menu */
654 static menu_ptr wind_menu; /* windows (sub) menu */
655 static menu_ptr font_menu; /* Font (sub)menu */
657 static save_saveblock *saveblk = NULL; /* For the save box */
659 static term_data *menu_term; /* term the last menu was opened for */
661 #endif /* FULLSCREEN_ONLY */
663 static ZapRedrawBlock zrb; /* a redraw block */
665 /* Cursor colour */
666 #define CURSOR_COLOUR 255 /* Cursor's Angband colour */
667 #define CURSOR_RGB 0x00ffff00 /* if undefined, use bbggrrxx */
669 static int cursor_rgb = -1; /* colour to use for cursor */
671 static int fullscreen_mode = 0; /* screen mode in use */
672 static int old_screenmode = 0; /* Mode we started out in */
673 static int *fullscreen_font = 0; /* font data for fullscreen use */
674 static int *fullscreen_base = 0; /* base address of screen */
675 static int fullscreen_height; /* height of the fullscreen font */
676 static int fullscreen_topline; /* raster offset of fullscreen */
678 #define KEYPRESS_QUIT 0x1cc /* F12 gets back to the desktop */
679 #define TERM_TOPLINE_HR 32 /* vertical pixel offset in mode 27 */
680 #define TERM_TOPLINE_LR 16 /* vertical pixel offset in mode 12 */
681 #define TIME_LINE 26 /* Line to display the clock on */
683 /* text to display at the bottom left of the fullscreen display */
684 static const char *fs_quit_key_text = "Press f12 to return to the desktop";
685 static const char *alarm_cancel_text = "(Press ^Escape to cancel the alarm)";
687 /* Debugging flags, etc. */
688 static int log_g_malloc = 0; /* Log calls to ralloc, etc */
689 static int show_sound_alloc = 0; /* Log sound mappings, etc */
691 /* Activate file caching? */
692 #ifdef USE_FILECACHE
693 static int use_filecache = TRUE;
694 #else
695 static int use_filecache = FALSE;
696 #endif
698 /* Cripple some things to save memory */
699 static int minimise_memory = 0;
701 /* Forward declarations of some of the Full Screen Mode stuff */
702 static void enter_fullscreen_mode(void);
703 static void leave_fullscreen_mode(void);
704 static void set_keys(int claim);
706 /* Forwards declarations of the sound stuff */
707 static void initialise_sound(void);
708 static void play_sound(int event);
710 /* Forward declarations of Term hooks, etc. */
711 static void Term_init_acn(term *t);
712 static errr Term_user_acn(int n);
714 #ifndef FULLSCREEN_ONLY
715 static errr Term_curs_acn(int x, int y);
716 static errr Term_text_acn(int x, int y, int n, byte a, cptr s);
717 static errr Term_xtra_acn(int n, int v);
718 static errr Term_wipe_acn(int x, int y, int n);
719 static errr Term_xtra_acn_check(void);
720 static errr Term_xtra_acn_event(void);
721 static errr Term_xtra_acn_react(void);
722 #endif /* FULLSCREEN_ONLY */
724 static errr Term_curs_acnFS(int x, int y);
725 static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
726 static errr Term_wipe_acnFS(int x, int y, int n);
727 static errr Term_xtra_acn_clearFS(void);
728 static errr Term_xtra_acn_eventFS(int);
729 static errr Term_xtra_acn_reactFS(int force);
730 static void bored(void);
731 static void redraw_areaFS(int x, int y, int w, int h);
732 static void draw_cursor(int x, int y);
734 #ifdef USE_DA
735 /* Forward declarations of the memory stuff */
736 static void init_memory(int, int);
737 #endif
739 /* Forward declarations of the alarm stuff */
740 static void check_alarm(void);
741 #ifndef FULLSCREEN_ONLY
742 static void trigger_alarm_desktop(void);
743 #endif /* FULLSCREEN_ONLY */
744 static void ack_alarm(void);
745 static void write_alarm_choices(void);
746 static void read_alarm_choices(void);
749 /* This just shows some debugging info (if enabled with FE_DEBUG_INFO) */
750 static void show_debug_info(void);
754 /* File-caching functions (if enabled at compile time) */
755 #ifdef USE_FILECACHE
756 FILE *cached_fopen(char *name, char *mode);
757 errr cached_fclose(FILE *fch);
758 errr cached_fgets(FILE *fch, char *buffer, int max_len);
759 #endif
763 * There are various different ways in which the g_malloc and g_free functions
764 * are prototyped, depending on the variant. This is an attempt to unify the
765 * codebase across thoe variants.
767 #if MEMTYPE == 3
768 #define G_MALLOC_PROT static void *g_malloc(size_t size)
769 #define G_FREE_PROT static void *g_free(void *blk)
770 #elif MEMTYPE == 2
771 #define G_MALLOC_PROT static vptr g_malloc(huge size)
772 #define G_FREE_PROT static vptr g_free(vptr blk, huge size)
773 #elif MEMTYPE == 1
774 #define G_MALLOC_PROT static vptr g_malloc(huge size)
775 #define G_FREE_PROT static errr g_free(vptr blk, huge size)
776 #endif
779 | These functions act as malloc/free, but (if possible) using memory
780 | in the 'Game' Dynamic Area created by init_memory()
781 | We attach these functions to the ralloc_aux and rnfree_aux hooks
782 | that z-virt.c provides.
784 #ifdef USE_DA
785 G_MALLOC_PROT;
786 G_FREE_PROT;
787 #else
788 #define g_malloc(size) malloc(size);
789 #if MEMTYPE > 2
790 #define g_free(block, size) free(block);
791 #else
792 #define g_free(block) free(block);
793 #endif
794 #endif
797 | These functions act as malloc/free, but (if possible) using memory
798 | in the 'Fonts' Dynamic Area created by init_memory()
800 #ifdef USE_DA
801 static void* f_malloc(size_t size);
802 static void f_free(void *blk);
803 #else
804 #define f_malloc(size) malloc(size);
805 #define f_free(block) free(block);
806 #endif
811 | We use this to locate the choices file(s)...
813 static char *find_choices(int write);
814 static char *find_choices_mirror(void);
815 static char *find_alarmfile(int write);
820 | This function is supplied as a wrapper to the save_player function.
822 | Its purpose is to change the filename that the game will be saved with
823 | the leafname "!!PANIC!!" so that panic saves that break the savefile
824 | won't overwrite the original savefile.
826 | To get this to work, you'll need to ammend files.c and change the call
827 | to save_player in the panic save function(s) (search for "panic save")
828 | to a call to save_player_panic_acn. You can declare a prototype for
829 | the function if you like.
832 extern int save_player_panic_acn(void)
834 char *e, *l;
836 /* Find the final / in the savefile name */
837 for (l = e = savefile; *e; e++)
838 if (*e == '/')
840 l = e + 1;
843 /* Write over the current leaf with the special panic one */
844 strcpy(l, "!!PANIC!!");
846 /* save the game */
847 return save_player(SAVE_PLAYER_PARAM);
851 /*--------------------------------------------------------------------------*/
852 /* Error reporting, etc. */
853 /*--------------------------------------------------------------------------*/
856 /* Tell the user something important */
857 static void plog_hook(cptr str)
859 Msgs_Report(1, "err.plog", str);
862 /* Tell the user something, then quit */
863 static void quit_hook(cptr str)
865 /* str may be null */
866 if (str) Msgs_Report(1, "err.quit", str);
867 exit(0);
870 /* Tell the user something then crash ;) */
871 static void core_hook(cptr str)
873 Msgs_Report(1, "err.core", str);
875 if (game_in_progress && character_generated)
876 save_player_panic_acn();
878 quit(NULL);
881 static void debug(const char *fmt, ...)
883 va_list ap;
884 char buffer[260];
886 va_start(ap, fmt);
887 vstrnfmt(buffer, sizeof(buffer), fmt, ap);
888 va_end(ap);
890 plog(buffer);
897 /*--------------------------------------------------------------------------*/
898 /* File handling */
899 /*--------------------------------------------------------------------------*/
902 * We use myFile_WriteBytes and myFile_ReadBytes because we require a
903 * different return value to that given by the DeskLib "File_" equivalents.
905 static int myFile_WriteBytes(const int handle, const void *buf, const int n)
907 int ntf;
908 if (SWI(4, 4, SWI_OS_GBPB, 2, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
909 return n;
910 return ntf;
913 static int myFile_ReadBytes(const int handle, void *buf, const int n)
915 int ntf;
916 if (SWI(4, 4, SWI_OS_GBPB, 4, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
917 return n;
918 return ntf;
923 | Determine if one file is newer than another.
925 | The filenames should be specified in RISC OS style.
927 | Returns -1 if 'a' is newer than 'b'.
929 static int file_is_newer(const char *a, const char *b)
931 unsigned char a_time[5];
932 unsigned char b_time[5];
933 int n;
935 /* If 'a' doesn't exist then 'b' isn't out of date */
936 if (!File_Exists(a)) return 0;
938 /* If 'b' doesn't exist then 'b' is out of date */
939 if (!File_Exists(b)) return -1;
941 /* Get the datestamp of the 'a' file */
942 File_Date(a, a_time);
944 /* Get the datestamp of the 'b' file */
945 File_Date(b, b_time);
947 /* Compare timestamps, defaulting to 0 if they are of equal age */
948 for (n = 4; n >= 0; n--)
950 if (b_time[n] < a_time[n])
952 return -1;
954 if (b_time[n] > a_time[n]) return 0;
957 return 0;
962 | As fprintf, but output to all files (if their handles are non zero).
963 | NB: void type.
965 static void f2printf(FILE *a, FILE *b, const char *fmt, ...)
967 va_list ap;
968 char buffer[2048];
969 va_start(ap, fmt);
970 vstrnfmt(buffer, sizeof(buffer), fmt, ap);
971 va_end(ap);
973 if (a) fprintf(a, buffer);
974 if (b) fprintf(b, buffer);
976 va_end(ap);
982 /*--------------------------------------------------------------------------*/
983 /* Clean up (ie. close files, etc). */
984 /*--------------------------------------------------------------------------*/
986 static void final_acn(void)
988 int i;
990 for (i = 0; i < openfiles; i++) File_Close(filehandle[i]);
992 if (fullscreen_mode)
994 /* Restore the screen mode */
995 Wimp_SetMode(old_screenmode);
997 /* Restore the various soft keys */
998 set_keys(FALSE);
1001 if (flush_scrap && *scrap_path)
1003 char tmp[512];
1004 my_strcpy(tmp, scrap_path, sizeof(tmp));
1005 tmp[strlen(tmp) - 1] = 0; /* Remove trailing dot */
1007 /* ie. "*Wipe <scrapdir> r~c~v~f" */
1008 SWI(4, 0, SWI_OS_FSControl, 27, tmp, 0, 1);
1011 #ifdef FULLSCREEN_ONLY
1012 Wimp_CommandWindow(-1);
1013 #endif /* FULLSCREEN_ONLY */
1015 STOP_HOURGLASS;
1019 /*--------------------------------------------------------------------------*
1020 | Various UNIX-like support funtions |
1021 *--------------------------------------------------------------------------*/
1024 | Hack: determine whether filenames should be truncated to 10 chars or not.
1026 | Needed since RO2 (and RO3 with Truncate configured off) will return
1027 | errors instead of automatically truncating long filenames.
1029 static int truncate_names(void)
1031 int r1, r2;
1033 /* Okay, so we've got RO3 (or later), so check the CMOS RAM */
1034 OS_Byte(osbyte_READCMOSRAM, 28, 0, &r1, &r2);
1036 /* Bit 0 of byte 28 is the Truncate flag */
1037 return !(r2 & 1);
1042 | The PathName translation is now done by two separate functions:
1043 | unixify_name() and riscosify_name().
1045 | This is done because only the UNIX=>RISCOS translation should
1046 | ever affect the length of the leafname (ie. by truncating it to
1047 | 10 chars if necessary).
1049 | Note that the two functions are identical but for the truncation
1050 | check so all that's really been done is that translate_name() now
1051 | takes an extra argument: 'trunc' that controls whether truncation
1052 | is applied, and riscosify and unixify just call translate_name().
1054 static char *translate_name(const char *path, int trunc)
1056 static char buf[260];
1057 char c, *p;
1059 /* Copy 'path' into 'buf', swapping dots and slashes */
1060 p = buf; /* Output position */
1063 c = *path++;
1064 if (c == '/')
1065 c = '.';
1066 else if (c == '.')
1067 c = '/';
1068 *p++ = c;
1070 while (c); /* Terminator /is/ copied */
1073 | When saving a game, the old game is renamed as
1074 | "SavedGame.old", the new one is saved as "SavedGame.new",
1075 | "SavedGame.old" is deleted, "SavedGame.new" is renamed
1076 | as "SavedGame". This will go wrong on a Filecore based filing
1077 | system if the saved game has a leafname > 8 chars.
1080 if ((p = strstr(buf, "/old")) == NULL)
1082 p = strstr(buf, "/new");
1084 if (!p)
1086 ftype = 0xffd;
1088 else
1090 char *q = strrchr(buf, '.');
1091 if (q)
1092 if (p - q > 6)
1094 memmove(q + 6, p, 5);
1096 ftype = vfiletype;
1100 | Hack: Do we need to truncate the leafname?
1102 if (trunc)
1104 if (truncate_names())
1106 char *a, *b;
1108 | Assume that only the leafname needs attention
1109 | (this should be true for any variant)
1111 for (a = b = buf; *a; a++)
1112 if (*a == '.')
1113 b = a + 1;
1115 | Now b points to the start of the leafname.
1116 | If the leafname is >10 chars, write over the 10th with a
1117 | terminator.
1119 if (strlen(b) > 10)
1121 b[10] = 0;
1126 return buf;
1130 extern char *riscosify_name(const char *path)
1132 return translate_name(path, TRUE);
1135 static char *unixify_name(const char *path)
1137 return translate_name(path, FALSE);
1141 /*--------------------------------------------------------------------------*/
1145 * Open a file [as fopen()] but translate the requested filename first
1147 FILE *my_fopen(const char *f, const char *m)
1149 FILE *fp;
1150 char *n = riscosify_name(f); /* translate for RO */
1152 /* Try to open the file */
1153 fp = fopen(n, m);
1155 /* If it succeded and the file was opened for binary output
1156 | then set the type according to the 'ftype' hack.
1157 | NB: This will fail on some filing systems.
1160 if (fp && strstr(m, "wb"))
1162 File_SetType(n, ftype);
1165 return fp;
1170 * Close a file, a la fclose()
1172 #if FCLOSETYPE == 1
1173 #define RETURN
1174 void my_fclose(FILE *fp)
1175 #else
1176 #define RETURN return
1177 errr my_fclose(FILE *fp)
1178 #endif
1180 /* Close the file, return 1 for an error, 0 otherwise */
1181 RETURN fclose(fp) ? 1 : 0;
1183 #undef RETURN
1187 * Open/Create a file
1189 int fd_make(cptr file, int mode)
1191 char *real_path;
1192 file_handle handle;
1194 /* Translate the filename into a RISCOS one */
1195 real_path = riscosify_name(file);
1197 /* Try to OPENOUT the file (no path, error if dir or not found) */
1198 handle = File_Open(real_path, (file_access) 0x8f);
1200 /* Check for failure */
1201 if (!handle) return -1;
1203 /* Try to set the filetype according to the ftype hack */
1204 File_SetType(real_path, ftype);
1206 /* We keep track of up to 16 open files at any given time */
1207 if (openfiles < 16) filehandle[openfiles++] = handle;
1209 return (int) handle;
1213 /* Delete a file [as remove()] */
1214 errr fd_kill(cptr file)
1216 return remove(riscosify_name(file)) ? 1 : 0;
1220 /* Rename a file [as rename()] */
1221 errr fd_move(cptr old, cptr new)
1223 char new_[260];
1224 my_strcpy(new_, riscosify_name(new), sizeof(new_));
1225 return rename(riscosify_name(old), new_) ? 1 : 0;
1228 /* Open a file */
1229 int fd_open(cptr path, int flags)
1231 file_handle handle = 0;
1232 char *real_path = riscosify_name(path);
1234 switch (flags & 0x0f)
1236 case O_RDONLY: /* Read only */
1237 handle = File_Open(real_path, (file_access) 0x4f);
1238 break;
1239 case O_WRONLY: /* Write only */
1240 case O_RDWR: /* Read/Write */
1241 handle = File_Open(real_path, (file_access) 0xcf);
1244 /* Check for failure */
1245 if (!handle) return (-1);
1247 /* Keep track of upto 16 open files... */
1248 if (openfiles < 16)
1249 filehandle[openfiles++] = handle;
1251 return (int) handle;
1255 /* Close a file opened with fd_make or fd_open */
1256 errr fd_close(int handle)
1258 int i;
1260 if (handle <= 0)
1262 return -1;
1263 } /* Illegal handle */
1265 /* Try to close the file */
1266 if (File_Close(handle))
1268 return 1;
1271 /* Mark the file as closed in our array of file handles */
1272 openfiles--;
1273 /* Find the entry in the array (if it exists) */
1274 for (i = 0; i < 16; i++)
1275 if (filehandle[i] == handle)
1277 break;
1279 /* Shuffle the remaining entries down */
1280 for (; i < openfiles; i++)
1281 filehandle[i] = filehandle[i + 1];
1283 return 0; /* Sucess */
1288 /* Read some bytes from a file */
1289 #if FDTYPE == 1
1290 errr fd_read(int handle, char *buf, huge nbytes)
1291 #elif FDTYPE == 2
1292 errr fd_read(int handle, char *buf, huge nbytes)
1293 #else
1294 errr fd_read(int handle, char *buf, size_t nbytes)
1295 #endif
1297 int unread;
1299 /* Check the handle is legal */
1300 if (handle <= 0) return -1;
1302 unread = myFile_ReadBytes(handle, buf, (int) nbytes);
1304 return unread ? 1 : 0;
1308 /* Write some bytes to a file */
1309 #if FDTYPE == 1
1310 errr fd_write(int handle, const char *buf, huge nbytes)
1311 #elif FDTYPE == 2
1312 errr fd_write(int handle, cptr buf, huge nbytes)
1313 #else
1314 errr fd_write(int handle, const char *buf, size_t nbytes)
1315 #endif
1317 int unwritten;
1319 /* Check the handle is legal */
1320 if (handle <= 0) return -1;
1322 unwritten = myFile_WriteBytes(handle, buf, (int)nbytes);
1324 return unwritten ? 1 : 0;
1328 /* Seek in a file */
1329 #if FDTYPE == 1
1330 errr fd_seek(int handle, huge offset)
1331 #elif FDTYPE == 2
1332 errr fd_seek(int handle, long offset)
1333 #else
1334 errr fd_seek(int handle, long offset)
1335 #endif
1337 os_error *e;
1339 /* Check the handle is legal */
1340 if (handle <= 0) return -1;
1342 e = File_Seek(handle, (int)offset);
1344 return e ? 1 : 0;
1348 /* RISC OS provides no file locking facilities, so: */
1349 errr fd_lock(int handle, int what)
1351 return 0;
1355 /* Get a temporary filename */
1356 errr path_temp(char *buf, int max)
1360 | New in 1.25 - use the scrap path we decided on earlier, or
1361 | fall back on tmpnam() if that fails for some reason.
1363 if (*scrap_path)
1365 time_t t;
1366 int m;
1367 char tmp[512];
1370 * We try up to eighty scrapfiles based on the current time
1371 * until we find a filenane that doesn't yet exist.
1373 time(&t);
1374 for (m = 0; m < 80; m++)
1376 sprintf(tmp, "%s0x%08x", scrap_path, (int) t + m);
1378 if (File_Size(tmp) == -1) break;
1381 if (m < 80)
1383 strncpy(buf, unixify_name(tmp), max);
1384 return 0;
1388 strncpy(buf, unixify_name(tmpnam(NULL)), max);
1389 return 0;
1392 #ifdef NEEDS_ACCESS
1393 int access(const char *path, int mode)
1395 os_error *e;
1396 int f;
1398 e = SWI(2, 1, SWI_OS_Find, (1<<2) | (1<<3) | 0x40, riscosify_name(path), &f);
1400 if (e || f == 0) return -1;
1402 SWI(2, 0, SWI_OS_Find, 0, f);
1404 return 0;
1406 #endif
1410 * Create a new path by appending a file (or directory) to a path
1412 * This requires no special processing on simple machines, except
1413 * for verifying the size of the filename, but note the ability to
1414 * bypass the given "path" with certain special file-names.
1416 * Note that the "file" may actually be a "sub-path", including
1417 * a path and a file.
1419 * Note that this function yields a path which must be "parsed"
1420 * using the "parse" function above.
1422 #if PATHBUILDTYPE == 1
1423 void path_build(char *buf, int max, cptr path, cptr file)
1424 #elif PATHBUILDTYPE == 2
1425 errr path_build(char *buf, int max, cptr path, cptr file)
1426 #else
1427 errr path_build(char *buf, size_t max, cptr path, cptr file)
1428 #endif
1430 /* Special file */
1431 if (file[0] == '~')
1433 /* Use the file itself */
1434 strnfmt(buf, max, "%s", file);
1437 /* Absolute file, on "normal" systems */
1438 else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
1440 /* Use the file itself */
1441 strnfmt(buf, max, "%s", file);
1444 /* No path given */
1445 else if (!path[0])
1447 /* Use the file itself */
1448 strnfmt(buf, max, "%s", file);
1451 /* Path and File */
1452 else
1454 /* Build the new path */
1455 strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
1458 #if PATHBUILDTYPE > 1
1459 /* Success */
1460 return 0;
1461 #endif
1466 /*--------------------------------------------------------------------------*/
1472 /*--------------------------------------------------------------------------*/
1473 /* Font Functions */
1474 /*--------------------------------------------------------------------------*/
1477 | Cache the system font as fonts[0]
1478 | Returns 1 for sucess or 0 for failure.
1479 | NB: The n_bpp data is *not* cached, just the 1bpp data and font info.
1480 | Also, the usage is never affected.
1482 static int cache_system_font(void)
1484 ZapFont *sys = SYSTEM_FONT;
1485 ZapRedrawBlock zrb;
1486 char work_area[16];
1487 int i;
1489 /* Cache the system font (as fonts[0]) */
1490 if (sys->bpp_1)
1492 f_free(sys->bpp_1);
1493 sys->bpp_1 = 0;
1495 sys->bpp_1 = f_malloc(8 * 256); /* 2K */
1496 if (!sys->bpp_1)
1498 return 0;
1501 /* Mung so that undefined characters show up as inverted ?s */
1502 work_area[3] = '?';
1503 SWI(2, 0, SWI_OS_Word, 10, work_area + 3);
1504 for (i = 4; i < 12; i++)
1505 work_area[i] ^= 255; /* invert colours */
1506 SWI(4, 0, SWI_ZapRedraw_ReverseBitmaps, 0, work_area + 4, work_area + 4, 8);
1507 for (i = 0; i < 0x20; i++)
1508 memcpy(((char *)sys->bpp_1) + i * 8, work_area + 4, 8);
1510 /* Read the system font */
1511 zrb.r_workarea = work_area;
1512 SWI(2, 0, SWI_ZapRedraw_ReadSystemChars, sys->bpp_1, &zrb);
1514 /* Set up some little bits of info */
1515 sys->name = (char *) "<System>";
1516 sys->w = sys->h = 8;
1517 sys->f = 0;
1518 sys->l = 255;
1520 return 1;
1526 | Prepare the font system
1528 static void initialise_fonts(void)
1530 /* Initialise the array */
1531 memset(fonts, 0, sizeof(fonts));
1533 /* Cache the system font */
1534 cache_system_font();
1535 fonts[0].usage = 0; /* No users */
1539 #ifndef FULLSCREEN_ONLY
1541 | Find a font (by name) in the array.
1542 | Returns 0 if the font isn't loaded, or a ZapFont* for it if it is.
1544 static ZapFont *find_font_by_name(char *name)
1546 int i;
1547 for (i = 0; i <= MAX_TERM_DATA; i++)
1548 if (fonts[i].name)
1549 if (!strcmp(fonts[i].name, name))
1550 return &(fonts[i]);
1551 return NULL;
1555 | Find a free slot in the fonts array
1557 static ZapFont *find_free_font(void)
1559 int i;
1560 for (i = 1; i <= MAX_TERM_DATA; i++)
1561 if (!fonts[i].name)
1563 return &(fonts[i]);
1565 return NULL;
1568 #ifndef HAS_MY_STRCPY
1570 * The my_strcpy() function copies up to 'bufsize'-1 characters from 'src'
1571 * to 'buf' and NUL-terminates the result. The 'buf' and 'src' strings may
1572 * not overlap.
1574 * my_strcpy() returns strlen(src). This makes checking for truncation
1575 * easy. Example: if (my_strcpy(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
1577 * This function should be equivalent to the strlcpy() function in BSD.
1579 size_t my_strcpy(char *buf, const char *src, size_t bufsize)
1581 size_t len = strlen(src);
1582 size_t ret = len;
1584 /* Paranoia */
1585 if (bufsize == 0) return ret;
1587 /* Truncate */
1588 if (len >= bufsize) len = bufsize - 1;
1590 /* Copy the string and terminate it */
1591 (void)memcpy(buf, src, len);
1592 buf[len] = '\0';
1594 /* Return strlen(src) */
1595 return ret;
1597 #endif /* !HAS_MY_STRCPY */
1599 #ifndef HAS_MY_STRCAT
1601 * The my_strcat() tries to append a string to an existing NUL-terminated string.
1602 * It never writes more characters into the buffer than indicated by 'bufsize' and
1603 * NUL-terminates the buffer. The 'buf' and 'src' strings may not overlap.
1605 * my_strcat() returns strlen(buf) + strlen(src). This makes checking for
1606 * truncation easy. Example:
1607 * if (my_strcat(buf, src, sizeof(buf)) >= sizeof(buf)) ...;
1609 * This function should be equivalent to the strlcat() function in BSD.
1611 static size_t my_strcat(char *buf, const char *src, size_t bufsize)
1613 size_t dlen = strlen(buf);
1615 /* Is there room left in the buffer? */
1616 if (dlen < bufsize - 1)
1618 /* Append as much as possible */
1619 return (dlen + my_strcpy(buf + dlen, src, bufsize - dlen));
1621 else
1623 /* Return without appending */
1624 return (dlen + strlen(src));
1627 #endif /* !HAS_MY_STRCAT */
1631 | Load a font from disc and set up the header info, etc.
1632 | NB: doesn't cache the nbpp data, just the 1bpp data.
1633 | (Sets usage to 1)
1634 | Returns NULL if failed.
1636 static ZapFont *load_font(char *name, ZapFont *f)
1638 int handle, extent;
1639 char path[260];
1640 struct
1642 char id[8];
1643 int w, h, f, l, r1, r2;
1645 header;
1646 char *font_path;
1647 char *t;
1648 char *real_name = name; /* need to preserve this */
1651 | 1.10 - the first element of the name determines the path to load
1652 | the font from.
1655 /* The font paths start <RISCOS_VARIANT>$ */
1656 t = path + sprintf(path, "%s$", RISCOS_VARIANT);
1658 /* Copy the path specifier and move 'name' past it */
1659 for (; *name != '.'; *t++ = *name++) ;
1661 /* After this, the name now points to the font name proper */
1662 name++;
1664 /* Append the end of the path name */
1665 strcpy(t, "$FontPath");
1667 /* Get the path setting */
1668 font_path = getenv(path);
1669 if (!font_path || !*font_path)
1670 my_strcpy(path, "null:$.", sizeof(path));
1671 else
1673 my_strcpy(path, font_path, sizeof(path));
1674 for (t = path; *t > ' '; t++)
1676 if (t[-1] != '.' && t[-1] != ':')
1678 *t++ = '.';
1680 *t = 0;
1682 my_strcat(path, name, sizeof(path));
1685 /* Open the file */
1686 handle = File_Open(path, (file_access) 0x4f);
1687 if (!handle)
1689 return NULL;
1692 /* Read the header */
1693 if (myFile_ReadBytes(handle, &header, sizeof(header)))
1695 File_Close(handle);
1696 return NULL;
1699 /* Check that it's a zapfont */
1700 if (strncmp(header.id, "ZapFont\r", 8))
1702 File_Close(handle);
1703 return NULL;
1706 /* Calculate the size of the 1bpp data */
1707 extent = File_ReadExtent(handle) - sizeof(header);
1709 /* Allocate the storage for the 1bpp data */
1710 f->bpp_1 = f_malloc(extent);
1711 if (!f->bpp_1)
1713 File_Close(handle);
1714 return NULL;
1717 /* Load the 1bpp data */
1718 if (myFile_ReadBytes(handle, f->bpp_1, extent))
1720 f_free(f->bpp_1);
1721 f->bpp_1 = 0;
1722 File_Close(handle);
1723 return NULL;
1726 /* Close the file and set the header, etc. */
1727 File_Close(handle);
1728 f->name = f_malloc(strlen(real_name) + 1);
1729 if (!f->name)
1731 f_free(f->bpp_1);
1732 f->bpp_1 = 0;
1733 return NULL;
1736 strcpy(f->name, real_name);
1737 f->w = header.w;
1738 f->h = header.h;
1739 f->f = header.f;
1740 f->l = header.l;
1741 f->usage = 1;
1743 return f;
1750 | Cache a font at a suitable number of bpp for the current mode
1751 | Returns 0 for failure, 1 for sucess.
1752 | If the call fails then the font's bpp_n entry will be NULL.
1754 static int cache_font_for_mode(ZapFont *f)
1756 ZapRedrawBlock b;
1757 char work_area[128];
1758 int size;
1760 if (!f)
1762 return 0;
1764 if (!f->bpp_1)
1766 return 0;
1769 b.r_workarea = work_area;
1770 SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
1772 b.r_workarea = work_area; /* Paranoia */
1773 b.r_charh = f->h;
1774 b.r_charw = f->w;
1775 SWI(4, 4, SWI_ZapRedraw_CachedCharSize, b.r_bpp, 0, f->w, f->h,
1776 NULL, NULL, &(b.r_cbpl), &(b.r_cbpc));
1778 size = 256 * b.r_cbpc;
1779 if (f->bpp_n)
1781 f_free(f->bpp_n);
1782 f->bpp_n = NULL;
1784 f->bpp_n = f_malloc(size);
1785 if (!f->bpp_n)
1787 return 0;
1790 b.r_workarea = work_area; /* Paranoia */
1791 b.r_caddr = f->bpp_n;
1792 SWI(5, 0, SWI_ZapRedraw_ConvertBitmap, 0, &b, 0, 255, f->bpp_1);
1794 return 1;
1800 | Stop using a font.
1801 | If the font's usage drops to zero then the font data is purged.
1803 static void lose_font(ZapFont *f)
1805 if (--f->usage)
1807 /*debug("Losing font %s (still cached)",f->name); */
1808 return;
1810 /*debug("Losing font %s (no longer in use)",f->name); */
1811 f_free(f->name);
1812 f_free(f->bpp_1);
1813 if (f->bpp_n)
1815 f_free(f->bpp_n);
1817 memset(f, 0, sizeof(ZapFont));
1822 | Get a font.
1824 static ZapFont *find_font(char *name)
1826 ZapFont *f;
1828 /* Check to see if it's already loaded */
1829 f = find_font_by_name(name);
1830 if (f)
1832 /*debug("Find font %s (already cached)",name); */
1833 f->usage++;
1834 if (f == SYSTEM_FONT)
1836 if (!cache_system_font())
1837 core("Failed to cache system font!");
1838 if (!cache_font_for_mode(SYSTEM_FONT))
1839 core("Failed to cache system font!");
1841 return f;
1844 /* Ok, now check to see if there's a free slot for it */
1845 f = find_free_font();
1846 if (!f)
1848 return NULL;
1849 } /* Oh dear :( */
1851 /* Load the font */
1852 /*debug("Find font %s (loading)",name); */
1853 f = load_font(name, f);
1854 if (f)
1856 if (!cache_font_for_mode(f))
1857 return NULL;
1858 return f;
1860 return NULL;
1867 | Cache the n_bpp data for all the active fonts (including system)
1869 static void cache_fonts(void)
1871 int i;
1872 for (i = 0; i <= MAX_TERM_DATA; i++)
1873 if (fonts[i].name)
1874 if (!cache_font_for_mode(&(fonts[i])))
1875 core("Failed to (re)cache font tables");
1883 typedef struct
1885 int load, exec, size, attr, type;
1886 char name[4]; /* Actual size is unknown */
1888 osgbpb10_block;
1891 | NB: This function is recursive.
1893 static menu_ptr make_zfont_menu(const char *dir)
1895 int entries, entry;
1896 int read, offset;
1897 unsigned int max_width;
1898 menu_ptr m;
1899 menu_item *mi;
1900 char *temp;
1901 filing_direntry *item_info;
1902 char buffer[1024]; /* 1Kb buffer */
1904 /* Count the entries in the directory */
1905 entries = offset = 0;
1906 while (offset != -1)
1908 read = 77;
1910 if (Filing_ReadDirNames(dir, buffer, &read, &offset, sizeof(buffer), (char *) "*"))
1912 offset = -1;
1913 read = 0;
1915 entries += read;
1918 if (!entries) return NULL;
1920 /* Allocate a big enough area of storage for the number of entries */
1921 m = f_malloc(sizeof(menu_block) + entries * sizeof(menu_item));
1922 if (!m)
1924 return NULL;
1926 memset(m, 0, sizeof(menu_block) + entries * sizeof(menu_item));
1928 /* Set up the menu header */
1929 strncpy(m->title, Str_LeafName((char *) dir), 12);
1930 m->titlefore = 7;
1931 m->titleback = 2;
1932 m->workfore = 7;
1933 m->workback = 0;
1934 m->height = 44;
1935 m->gap = 0;
1936 mi = (menu_item *) (((int)m) + sizeof(menu_block));
1937 max_width = strlen(m->title);
1939 entry = 0;
1941 /* Read the entries */
1942 offset = 0;
1943 while (offset != -1)
1945 read = 77;
1947 if (Filing_ReadDirEntry(dir, (filing_direntry *) buffer, &read, &offset, sizeof(buffer), (char *) "*"))
1949 offset = -1;
1950 read = 0;
1953 item_info = (filing_direntry *) buffer;
1955 /* Create a menu item for each entry read (if it fits) */
1956 while (read-- > 0)
1958 switch (item_info->objtype)
1960 case filing_FILE:
1962 if ((item_info->loadaddr & 0xffffff00) == 0xfffffd00)
1964 /* Data file */
1965 mi[entry].submenu.value = -1;
1966 mi[entry].iconflags.data.text = 1;
1967 mi[entry].iconflags.data.filled = 1;
1968 mi[entry].iconflags.data.foreground = 7;
1969 mi[entry].iconflags.data.background = 0;
1970 strncpy(mi[entry].icondata.text, item_info->name, 12);
1971 if (strlen(mi[entry].icondata.text) > max_width)
1972 max_width = strlen(mi[entry].icondata.text);
1973 entry++;
1975 break;
1978 case filing_DIRECTORY:
1979 case filing_IMAGEFILE:
1981 menu_ptr sub;
1982 char new_path[260];
1983 if (strchr(":.", dir[strlen(dir) - 1]))
1984 sprintf(new_path, "%s%s", dir, item_info->name);
1985 else
1986 sprintf(new_path, "%s.%s", dir, item_info->name);
1987 sub = make_zfont_menu(new_path);
1988 if (sub)
1990 /* Add the submenu */
1991 mi[entry].submenu.menu = sub;
1992 mi[entry].iconflags.data.text = 1;
1993 mi[entry].iconflags.data.filled = 1;
1994 mi[entry].iconflags.data.foreground = 7;
1995 mi[entry].iconflags.data.background = 0;
1996 strncpy(mi[entry].icondata.text, item_info->name, 12);
1997 if (strlen(mi[entry].icondata.text) > max_width)
1998 max_width = strlen(mi[entry].icondata.text);
1999 entry++;
2002 break;
2006 temp = ((char *) item_info) + 20;
2007 while (*temp++);
2008 item_info = (filing_direntry *) WORDALIGN((int) temp);
2012 if (entry)
2014 m->width = (max_width + 2) * 16;
2015 mi[entry - 1].menuflags.data.last = 1;
2017 | We could possibly realloc() the storage to fit the
2018 | actual no. of entries read, but this is probably more
2019 | trouble than it's worth.
2022 else
2024 /* No point in returning an empty menu. */
2025 f_free(m);
2026 m = NULL;
2029 return m;
2032 #endif /* FULLSCREEN_ONLY */
2037 /*--------------------------------------------------------------------------*/
2040 | Initialise the palette stuff
2042 static void initialise_palette(void)
2044 memset(a_palette, 0, sizeof(a_palette));
2045 memset(palette, 0, sizeof(palette));
2046 memset(zpalette, 0, sizeof(zpalette));
2051 #ifndef FULLSCREEN_ONLY
2054 | Cache the ZapRedraw palette
2056 static void cache_palette(void)
2058 static ZapRedrawBlock b;
2059 char workspace[128];
2060 int i;
2062 static int old_gamma = -1;
2064 /* Idiocy check: */
2065 if (gamma < 1)
2067 plog("Internal error: Attempt to apply zero gamma - recovering...");
2068 gamma = 100;
2071 if (gamma != old_gamma)
2073 memset(a_palette, 0, sizeof(a_palette));
2074 old_gamma = gamma;
2077 /* Go through the palette updating any changed values */
2078 for (i = 0; i < 256; i++)
2080 if (COLOUR_CHANGED(i))
2082 int r, g, b;
2083 r = (int)(255.0 * pow(angband_color_table[i][1] / 255.0, 1.0 / ((double) gamma / 100.0)));
2084 g = (int)(255.0 * pow(angband_color_table[i][2] / 255.0, 1.0 / ((double) gamma / 100.0)));
2085 b = (int)(255.0 * pow(angband_color_table[i][3] / 255.0, 1.0 / ((double) gamma / 100.0)));
2086 palette[i] = (b << 24) | (g << 16) | (r << 8);
2087 a_palette[i][1] = angband_color_table[i][1];
2088 a_palette[i][2] = angband_color_table[i][2];
2089 a_palette[i][3] = angband_color_table[i][3];
2093 cursor_rgb = palette[CURSOR_COLOUR];
2095 /* Cache the ZapRedraw palette for it */
2096 b.r_workarea = workspace;
2098 if (b.r_mode != screen_mode.screen_mode)
2099 SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
2101 SWI(5, 0, SWI_ZapRedraw_CreatePalette, 2, &b, palette, zpalette, 256);
2107 /*--------------------------------------------------------------------------*/
2110 | Functions for dealing with the SaveBox
2114 #ifndef HAS_MY_STRNICMP
2116 | Hack: can't use Str.h without defining HAS_STRICMP. Rather than
2117 | require that the header files are altered we simply provide our
2118 | own strnicmp() function.
2120 static int my_strnicmp(const char *a, const char *b, int n)
2122 int i;
2124 n--;
2126 for (i = 0; i <= n; i++)
2128 if (tolower((unsigned char)a[i]) != tolower((unsigned char)b[i]))
2129 return tolower((unsigned char)a[i]) - tolower((unsigned char)b[i]);
2131 if (a[i] == '\0')
2132 break;
2135 return 0;
2137 #endif /* HAS_MY_STRNICMP */
2141 | This is the handler called when a 'save' occurrs.
2142 | All it does is to update the game's own savefile setting and
2143 | then (if possible) save the character.
2145 static BOOL SaveHnd_FileSave(char *filename, void *ref)
2147 char old_savefile[1024];
2149 /* Hack: refuse to save if the character is dead */
2150 if (PDEADCHK)
2152 Msgs_Report(0, "err.cheat");
2153 return FALSE;
2156 /* Hack: disallow saves to <Wimp$Scrap>* */
2157 if (!my_strnicmp("<wimp$scrap>", filename, 12))
2159 Msgs_Report(0, "err.scrap");
2160 return FALSE;
2163 /* Preserve the old path, in case something goes wrong... */
2164 my_strcpy(old_savefile, savefile, sizeof(old_savefile));
2166 /* Set the new path */
2167 my_strcpy(savefile, unixify_name(filename), sizeof(savefile));
2169 /* Try a save (if sensible) */
2170 if (game_in_progress && character_generated)
2172 if (inkey_flag)
2174 if (!save_player(SAVE_PLAYER_PARAM))
2176 Msgs_Report(0, "err.save", filename);
2177 my_strcpy(savefile, old_savefile, sizeof(savefile));
2178 return FALSE; /* => failure */
2181 else
2183 Msgs_Report(0, "err.nosave");
2184 my_strcpy(savefile, old_savefile, sizeof(savefile));
2185 return TRUE; /* Failed really, but not unexpectedly */
2189 /* Set the pathname icon */
2190 Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
2192 return TRUE; /* => Success */
2196 | Create the window and claim various handlers for it
2198 static void init_save_window(void)
2200 /* Create the window */
2201 save_box = Window_Create("save", template_TITLEMIN);
2203 /* Set the file icon */
2204 Icon_printf(save_box, SAVE_ICON, "file_%03x", vfiletype);
2206 saveblk = Save_InitSaveWindowHandler(save_box, /* Window handle */
2207 TRUE, /* it's part of a menu */
2208 FALSE, /* not a window */
2209 FALSE, /* Don't auto release the handlers */
2210 SAVE_ICON, /* The file icon */
2211 SAVE_OK, /* The OK icon */
2212 SAVE_CANCEL, /* The cancel icon */
2213 SAVE_PATH, /* The pathname icon */
2214 SaveHnd_FileSave, /* Handler to "save the file" */
2215 NULL, /* No RAM transfer support */
2216 NULL, /* No 'result handler' */
2217 100 << 10, /* Est. size (irelevant anyway) */
2218 vfiletype, /* filetype (irelevant) */
2219 NULL /* ref */
2225 | Handles MenuWarning messages
2227 static BOOL Hnd_MenuWarning(event_pollblock * pb, void *ref)
2229 os_error *e;
2230 window_handle win;
2232 if (menu_currentopen == ibar_menu)
2234 win = save_box;
2236 else if (menu_currentopen == term_menu)
2238 switch (pb->data.message.data.menuwarn.selection[0])
2240 case TERM_MENU_SAVE: win = save_box; break;
2241 case TERM_MENU_SIZE: win = resize_win; break;
2242 default: return FALSE;
2245 else
2247 return FALSE;
2250 if (win == save_box)
2252 /* Set the pathname */
2253 Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
2255 else
2257 /* Set the size */
2258 Icon_SetInteger(resize_win, SIZE_WIDTH, menu_term->t.wid);
2259 Icon_SetInteger(resize_win, SIZE_HEIGHT, menu_term->t.hgt);
2262 /* Open the submenu */
2263 e = Wimp_CreateSubMenu((menu_block *) win,
2264 pb->data.message.data.menuwarn.openpos.x,
2265 pb->data.message.data.menuwarn.openpos.y);
2267 if (e) Msgs_ReportFatal(0, "err.swi", __LINE__, e->errmess);
2269 return TRUE;
2273 /*--------------------------------------------------------------------------*/
2277 * Initialise the r_data array, reallocating memory for it if the size of our
2278 * "theoretical largest term" has changed since the last call.
2280 * We just set up the line offset pointers and make sure that the
2281 * lines themselves are 'safe' by writing end-of-line codes to them.
2283 static void initialise_r_data(void)
2285 int maxwid = 0, maxhgt = 0;
2286 char *new_r_data;
2288 int *lo;
2289 char *ld;
2291 int i;
2293 for (i = 0; i < MAX_TERM_DATA; i++)
2295 if (data[i].t.wid > maxwid) maxwid = data[i].t.wid;
2296 if (data[i].t.hgt > maxhgt) maxhgt = data[i].t.hgt;
2299 if (r_maxwid != maxwid || r_maxhgt != maxhgt)
2302 * We allocate enough memory for a theoretical largest zapredrawblock.
2303 * This consists of a list of line starts + terminator, then a big
2304 * block containing enough space for max_hgt lines of max_wid each
2305 * (allowing for possible control codes) and an end-of-line-terminator
2306 * for each line.
2308 new_r_data = realloc(r_data,
2309 (maxhgt + 1) * 4 + maxhgt * (maxwid * 5 + 4));
2311 if (new_r_data == NULL)
2313 Msgs_Report(0, "err.resize");
2314 return;
2317 r_data = new_r_data;
2318 zrb.r_data = r_data;
2320 r_maxwid = maxwid;
2321 r_maxhgt = maxhgt;
2324 lo = (int *) r_data;
2327 /* Make a dummy block where all lines point to the same empty line */
2328 ld = r_data + (maxhgt + 1) * 4;
2329 *ld++ = 0; /* 0,2 == */
2330 *ld = 2; /* end of line */
2332 for (i = 0; i < maxhgt; i++)
2334 /* Offset of line */
2335 lo[i] = (maxhgt + 1) * 4;
2337 lo[i] = 0; /* Terminate line index */
2343 | Create the r_data array for a term
2344 | This is typically quite fast (1ms or so on a RPC700)
2345 | so we don't bother caching r_data for each term or using the
2346 | 'frosh' concept.
2348 static void make_r_data(term_data *t)
2350 char **c = t->t.old->c; /* char array */
2351 byte **a = t->t.old->a; /* attr array */
2352 char *o;
2353 int i, j, cf;
2355 /* First byte of r_data after line index */
2356 o = r_data + (t->t.hgt + 1) * 4;
2358 if (force_mono)
2360 for (j = 0; j < t->t.hgt; j++)
2362 /* Set up the line offset entry */
2363 ((int *)r_data)[j] = o - r_data;
2365 for (i = 0; i < t->t.wid; i++)
2366 *o++ = a[j][i] != TERM_DARK ? c[j][i] : ' ';
2367 /* 0,2 => end of line */
2368 *o++ = 0;
2369 *o++ = 2;
2372 else
2374 for (j = 0; j < t->t.hgt; j++)
2376 /* Set up the line offset entry */
2377 ((int *)r_data)[j] = o - r_data;
2379 /* Each line starts in white */
2380 cf = TERM_WHITE;
2382 for (i = 0; i < t->t.wid; i++)
2384 if (a[j][i] != cf)
2386 /* 0,6 => change FG */
2387 *o++ = 0;
2388 *o++ = 6;
2389 cf = *o++ = a[j][i];
2391 *o++ = c[j][i];
2393 /* 0,2 => end of line */
2394 *o++ = 0;
2395 *o++ = 2;
2399 /* Terminate line index */
2400 ((int *) r_data)[t->t.hgt] = 0;
2405 | Set up 'zrb' for the current screen mode.
2407 static void set_up_zrb_for_mode(void)
2409 static char work_area[4096];
2410 zrb.r_workarea = work_area;
2411 zrb.r_palette = zpalette;
2412 zrb.r_linesp = 0;
2413 zrb.r_for = TERM_WHITE;
2414 zrb.r_bac = 0;
2415 zrb.r_data = r_data;
2416 SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &zrb);
2422 | Set up the ZapRedrawBlock ready to redraw term 't'
2423 | (caches the r_data as part of the process)
2425 static void set_up_zrb(term_data *t)
2427 int fw, fh;
2429 zrb.r_flags.value = 0;
2431 /* Set font info up */
2432 fw = t->font->w;
2433 fh = t->font->h;
2434 SWI(4, 4, SWI_ZapRedraw_CachedCharSize, zrb.r_bpp, 0, fw, fh,
2435 NULL, NULL, &(zrb.r_cbpl), &(zrb.r_cbpc));
2436 zrb.r_caddr = (void *)(((int)t->font->bpp_n) - (t->font->f * zrb.r_cbpc));
2438 zrb.r_charw = fw; /* Character size in pixels */
2439 zrb.r_charh = fh;
2441 if (t->font == SYSTEM_FONT)
2442 zrb.r_flags.bits.double_height = screen_eig.y == 1;
2443 else
2444 zrb.r_flags.bits.double_height = 0;
2446 make_r_data(t); /* Cache the r_data */
2453 * Redraws the contents of the given term, as initialised by a
2454 * Wimp_RedrawWindow or Wimp_UpdateWindow call.
2456 static void RO_redraw_window(window_redrawblock * rb, BOOL *more, term_data *t)
2458 int cx = 0, cy = 0, cw = 0, ch = 0;
2460 /* set GCOL for cursor colour */
2461 if (t->cursor.visible)
2463 cw = zrb.r_charw << screen_eig.x;
2464 ch = -(zrb.r_charh << screen_eig.y);
2465 if (zrb.r_flags.bits.double_height)
2467 ch *= 2;
2469 cx = t->cursor.pos.x * cw;
2470 cy = t->cursor.pos.y * ch;
2471 cx += (rb->rect.min.x - rb->scroll.x);
2472 cy += (rb->rect.max.y - rb->scroll.y);
2473 cw -= (1 << screen_eig.x);
2474 ch += (1 << screen_eig.y);
2475 cy -= (1 << screen_eig.y);
2478 while (*more)
2480 SWI(2, 0, SWI_ZapRedraw_GetRectangle, rb, &zrb);
2481 SWI(2, 0, SWI_ZapRedraw_RedrawArea, NULL, &zrb);
2482 if (t->cursor.visible)
2484 ColourTrans_SetGCOL(cursor_rgb, 0, 0);
2485 GFX_Move(cx, cy);
2486 GFX_DrawBy(cw, 0);
2487 GFX_DrawBy(0, ch);
2488 GFX_DrawBy(-cw, 0);
2489 GFX_DrawBy(0, -ch);
2491 Wimp_GetRectangle(rb, more);
2499 * Perform the redraw for the requested term window.
2501 static BOOL Hnd_Redraw(event_pollblock * pb, void *ref)
2503 term_data *t = (term_data *)ref;
2504 window_redrawblock rb;
2505 BOOL more;
2507 rb.window = t->w;
2508 Wimp_RedrawWindow(&rb, &more);
2510 set_up_zrb(t);
2512 RO_redraw_window(&rb, &more, t);
2514 return TRUE;
2521 * Redraw the out-of-date parts of the given term window
2523 static void refresh_window(term_data *t)
2525 window_redrawblock rb;
2526 BOOL more;
2527 int fw, fh;
2529 if ((t->changed_box.min.x >= t->changed_box.max.x) ||
2530 (t->changed_box.min.y >= t->changed_box.max.y))
2531 return;
2533 set_up_zrb(t);
2535 fw = zrb.r_charw << screen_eig.x;
2536 fh = -(zrb.r_charh << screen_eig.y);
2537 if (zrb.r_flags.bits.double_height)
2539 fh *= 2;
2542 rb.window = t->w;
2543 rb.rect.min.x = fw * t->changed_box.min.x;
2544 rb.rect.max.x = fw * t->changed_box.max.x;
2546 rb.rect.max.y = fh * t->changed_box.min.y;
2547 rb.rect.min.y = fh * t->changed_box.max.y;
2549 Wimp_UpdateWindow(&rb, &more);
2550 RO_redraw_window(&rb, &more, t);
2552 t->changed_box.min.x = t->changed_box.min.y = 255;
2553 t->changed_box.max.x = t->changed_box.max.y = 0;
2558 * Redraws the out-of-date parts of the all the open term windows.
2560 static void refresh_windows(void)
2562 int i;
2563 window_info info;
2565 for (i = 0; i < MAX_TERM_DATA; i++)
2567 info.window = data[i].w;
2568 Wimp_GetWindowInfo(&info);
2569 if (info.block.flags.data.open) refresh_window(&(data[i]));
2575 | Set the size of a window.
2576 | If the window grows but has no scroll bars then it is re-sized to
2577 | the new extent. If it shrinks then it is resized regardless.
2579 | If the window isn't open then it is opened behind the backwindow,
2580 | resized and then closed again... I /did/ have a reason for doing this
2581 | rather than simply recreating the window at the new size, but for the
2582 | life of me I can't remember what it was...
2584 | <ajps> I think this is simply so that the Wimp remembers the position
2585 | of the window for when we next call show_windows().
2587 static void set_window_size(window_handle w, int width, int height)
2589 window_state ws;
2590 int reclose;
2592 Wimp_GetWindowState(w, &ws);
2593 Window_SetExtent(w, 0, -height, width, 0);
2595 reclose = !ws.flags.data.open;
2596 if (!(ws.flags.value & (0xf << 27)))
2598 if (reclose)
2600 ws.openblock.behind = -3;
2601 Wimp_OpenWindow(&(ws.openblock));
2604 /* Keep the top right-hand corner of the window fixed. */
2605 ws.openblock.screenrect.max.x = ws.openblock.screenrect.min.x + width;
2606 ws.openblock.screenrect.min.y = ws.openblock.screenrect.max.y - height;
2608 Wimp_OpenWindow(&(ws.openblock));
2610 if (reclose)
2612 Wimp_CloseWindow(w);
2632 | Change the size of a window to suit the font displayed in it
2634 static void force_term_resize(term_data *t)
2636 int fw, fh;
2637 set_up_zrb(t);
2639 fw = zrb.r_charw << screen_eig.x;
2640 fh = zrb.r_charh << screen_eig.y;
2641 if (zrb.r_flags.bits.double_height)
2643 fh *= 2;
2646 /* Calculate new size */
2647 fw *= t->t.wid;
2648 fh *= t->t.hgt;
2650 set_window_size(t->w, fw, fh);
2655 * Change the actual size of the terminal (so as to support variable-sized
2656 * "bigscreen" windows.
2658 static void term_change_size(term_data *t, int wid, int hgt)
2660 term *old_t = Term;
2662 /* Ignore attempts to resize too small */
2663 if (t == &data[0] && (wid < 80 || hgt < 24)) return;
2665 /* Hack -- activate the Term */
2666 Term_activate(&(t->t));
2668 /* Only do all the complicated stuff if the resize attempt succeeded */
2669 #ifdef IS_SCTH
2670 Term_resize(wid, hgt);
2671 #else
2672 if (Term_resize(wid, hgt) == NULL)
2673 #endif
2675 initialise_r_data();
2676 force_term_resize(t);
2679 /* Hack -- restore the old Term */
2680 Term_activate(old_t);
2690 static BOOL Hnd_Caret(event_pollblock * pb, void *ref)
2692 if (ref) got_caret = 1;
2693 else got_caret = 0;
2695 return TRUE;
2702 | Attach a (named) font to the specified term.
2703 | If 'font' is NULL then the system font is attached.
2704 | The bpp_n data is calculated if necessary
2705 | returns:
2706 | 1 => the font was attached OK
2707 | 0 => the system font was substituted
2709 static int attach_font_to_term(term_data *t, char *font)
2711 if (t->font != SYSTEM_FONT) lose_font(t->font);
2713 if (font) t->font = find_font(font);
2715 if (!t->font)
2717 t->font = SYSTEM_FONT;
2718 if (font) Msgs_Report(1, "err.font_l", font);
2720 else
2722 if (!t->font->bpp_n)
2724 lose_font(t->font);
2725 t->font = SYSTEM_FONT;
2726 if (font) Msgs_Report(1, "err.font_c", font);
2730 force_term_resize(t);
2732 return !(t->font == SYSTEM_FONT);
2738 /*--------------------------------------------------------------------------*/
2743 | Create a menu of all the (probable!) fonts in the specified location
2744 | NB: Any file of type 'data' is considered a font.
2746 | Subdirectories are recursively searched.
2748 | 1.10 - Uses <variant>$FontPaths to get a (space separated) list of paths
2749 | to search. For each path name, the menu text will be the name and the
2750 | path searched will be <variant>$<name>$FontPath
2752 | Eg. (for angband):
2753 | Angband$FontPaths Zap Angband
2754 | Angband$Zap$FontPath ZapFonts:
2755 | Angband$Angband$FontPath Angband:xtra.fonts.
2757 static void make_font_menu(void)
2759 char *t;
2760 char buffer[260];
2761 char menu_buffer[260];
2762 int paths;
2763 int i;
2764 unsigned int max_width;
2765 const char *path[64]; /* pointers to path names */
2766 menu_item *mi;
2768 font_menu = NULL;
2770 /* Get the path (ie. dir) to look under */
2771 t = getenv(RISCOS_VARIANT "$FontPaths");
2773 /* Hack: cope if the path isn't set */
2774 if (!t)
2776 t = "";
2779 my_strcpy(buffer, t, sizeof(buffer));
2782 | Count how many paths there are, build an array of pointers to them
2783 | and terminate them in the buffer
2785 paths = 1; /* including the system font fake path '<System>' */
2786 for (t = buffer; *t; t++)
2788 if (*t == ' ')
2790 *t = 0;
2792 else
2794 if (t == buffer || !t[-1])
2796 path[paths] = t;
2797 paths++;
2803 | Create the menu
2805 path[0] = SYSTEM_FONT->name;
2807 font_menu = f_malloc(sizeof(menu_block) + paths * sizeof(menu_item));
2808 if (!font_menu)
2810 core("Out of memory (building font menu)");
2812 memset(font_menu, 0, sizeof(menu_block) + paths * sizeof(menu_item));
2814 strncpy(font_menu->title, "Fonts", 12);
2815 font_menu->titlefore = 7;
2816 font_menu->titleback = 2;
2817 font_menu->workfore = 7;
2818 font_menu->workback = 0;
2819 font_menu->height = 44;
2820 font_menu->gap = 0;
2821 max_width = strlen(font_menu->title);
2823 mi = (menu_item *) (font_menu + 1);
2825 for (i = 0; i < paths; i++)
2827 mi[i].submenu.value = -1;
2828 mi[i].iconflags.data.text = 1;
2829 mi[i].iconflags.data.filled = 1;
2830 mi[i].iconflags.data.foreground = 7;
2831 mi[i].iconflags.data.background = 0;
2832 strncpy(mi[i].icondata.text, path[i], 12);
2833 if (strlen(mi[i].icondata.text) > max_width)
2834 max_width = strlen(mi[i].icondata.text);
2836 font_menu->width = (max_width + 2) * 16;
2837 mi[i - 1].menuflags.data.last = 1;
2840 | Hack: add a dotted line after the system font entry if appropriate
2842 if (paths > 1) mi[0].menuflags.data.dotted = 1;
2845 | Iterate over the paths, building the appropriate submenus
2847 for (i = 1; i < paths; i++)
2849 menu_ptr sub_menu = NULL;
2851 sprintf(menu_buffer, "%s$%s$FontPath", RISCOS_VARIANT, path[i]);
2852 t = getenv(menu_buffer);
2853 /* Hack: cope if the path isn't defined */
2854 if (!t)
2856 t = "";
2859 /* Fudge so that the fontpath can be a path, not just a dir. */
2860 my_strcpy(menu_buffer, t, sizeof(menu_buffer));
2861 for (t = menu_buffer; *t > ' '; t++)
2863 if (t[-1] == '.')
2865 t--;
2867 *t = 0;
2869 /* Build the menu. Don't bother if the path variable was empty */
2870 if (*menu_buffer)
2871 sub_menu = make_zfont_menu(menu_buffer);
2873 if (!sub_menu)
2875 mi[i].iconflags.data.shaded = 1;
2877 else
2879 mi[i].submenu.menu = sub_menu;
2880 /* Override the title of the 'root' sub-menu */
2881 strncpy(sub_menu->title, path[i], 12);
2882 /* Add the submenu to the main menu */
2886 return;
2889 /* ----------------------------------------------- musus, xxxx-xx-xx ---
2890 * Create and set up the infobox.
2891 * --------------------------------------------------------------------- */
2892 static void create_info_box(void)
2894 info_box = Window_Create("info", template_TITLEMIN);
2895 Icon_printf(info_box, 0, "%s %s", VARIANT, VERSION);
2896 Icon_SetText(info_box, 2, AUTHORS);
2897 Icon_SetText(info_box, 3, PORTERS);
2898 Icon_SetText(info_box, 7, PORTVERSION);
2900 return;
2904 static BOOL Hnd_ResizeSet(event_pollblock *pb, void *ref)
2906 int newwid = Icon_GetInteger(resize_win, SIZE_WIDTH);
2907 int newhgt = Icon_GetInteger(resize_win, SIZE_HEIGHT);
2909 if (pb->type != event_CLICK || pb->data.mouse.button.data.adjust == 0)
2911 Menu_Show((menu_ptr) -1, -1, -1);
2914 /* Do simple validation of the values */
2915 if (menu_term == &data[0])
2917 if (newwid < 80 || newhgt < 24)
2919 Msgs_Report(0, "err.minsize1");
2920 return TRUE;
2923 else if (newwid < 1 || newhgt < 1)
2925 Msgs_Report(0, "err.minsize2");
2926 return TRUE;
2929 term_change_size(menu_term, newwid, newhgt);
2931 return TRUE;
2935 static BOOL Hnd_ResizeCancel(event_pollblock *pb, void *ref)
2937 /* Close menus */
2938 Menu_Show((menu_ptr) -1, -1, -1);
2940 return TRUE;
2944 static BOOL Hnd_ResizeKeypress(event_pollblock *pb, void *ref)
2946 if(pb->data.key.code == keycode_RETURN)
2947 return Hnd_ResizeSet(pb, ref);
2949 return FALSE;
2954 * Create and set up the term-resize window.
2956 static void create_resize_win(void)
2958 resize_win = Window_Create("resize", template_TITLEMIN);
2960 Event_Claim(event_CLICK, resize_win, SIZE_SET, Hnd_ResizeSet, NULL);
2961 Event_Claim(event_CLICK, resize_win, SIZE_CANCEL, Hnd_ResizeCancel, NULL);
2962 Event_Claim(event_KEY, resize_win, event_ANY, Hnd_ResizeKeypress, NULL);
2964 return;
2970 | Create the various menus
2972 static void init_menus(void)
2974 char buffer1[256];
2975 char buffer2[32];
2976 char *o;
2978 create_info_box();
2979 make_font_menu();
2980 create_resize_win();
2982 Msgs_Lookup("menu.ibar:Info|>Save As|Full screen,Gamma correction,Sound,"
2983 "Windows|Save choices|Quit (& save)", buffer1, 256);
2984 ibar_menu = Menu_New(VARIANT, buffer1);
2985 if (!ibar_menu) core("Can't create Iconbar menu!");
2987 Msgs_Lookup("menu.term:Info|>Save As|>Size,Font,Windows", buffer1, 256);
2988 term_menu = Menu_New(VARIANT, buffer1);
2989 if (!term_menu) core("Can't create Term menu!");
2991 #ifndef OLD_TERM_MENU
2992 o = buffer1;
2993 o += sprintf(buffer1, "%s|", VARIANT);
2994 o += sprintf(o, "%s,%s,%s|", TERM_NAME(1), TERM_NAME(2),
2995 TERM_NAME(3));
2996 sprintf(o, "%s,%s,%s,%s", TERM_NAME(4), TERM_NAME(5),
2997 TERM_NAME(6), TERM_NAME(7));
2998 #else
2999 Msgs_printf(buffer1, "menu.windows:%s|Term-1 (Mirror),Term-2 (Recall),"
3000 "Term-3 (Choice)|Term-4,Term-5,Term-6,Term-7", VARIANT);
3001 #endif
3002 Msgs_Lookup("menu.winT:Windows", buffer2, 32);
3003 wind_menu = Menu_New(buffer2, buffer1);
3004 if (!wind_menu)
3006 core("Can't create Windows menu!");
3009 /* Now attach the various submenus to where they belong */
3010 Menu_AddSubWindow(ibar_menu, IBAR_MENU_INFO, info_box);
3011 Menu_AddSubWindow(ibar_menu, IBAR_MENU_GAMMA, gamma_win);
3012 Menu_AddSubWindow(ibar_menu, IBAR_MENU_SOUND, sound_win);
3013 Menu_AddSubMenu(ibar_menu, IBAR_MENU_WINDOWS, wind_menu);
3015 Menu_AddSubMenu(term_menu, TERM_MENU_INFO, (menu_ptr) info_box);
3016 Menu_AddSubMenu(term_menu, TERM_MENU_WINDOWS, wind_menu);
3018 /* Add the handler for menu warnings */
3019 EventMsg_Claim(message_MENUWARN, event_ANY, Hnd_MenuWarning, NULL);
3021 /* Set up these menu items to issue menuwarn messages */
3022 /* Menu_Warn(ibar_menu, IBAR_MENU_SAVE, TRUE, NULL, NULL);
3023 Menu_Warn(term_menu, TERM_MENU_SAVE, TRUE, NULL, NULL);
3024 Menu_Warn(term_menu, TERM_MENU_SIZE, TRUE, NULL, NULL);*/
3026 if (font_menu)
3027 /* add the submenu */
3028 Menu_AddSubMenu(term_menu, TERM_MENU_FONT, font_menu);
3029 else
3030 /* If the font menu is buggered, shade its entry */
3031 /* unticked, shaded */
3032 Menu_SetFlags(term_menu, TERM_MENU_FONT, FALSE, TRUE);
3038 static void grab_caret(void)
3040 caret_block cb;
3041 cb.window = data[0].w;
3042 cb.icon = -1;
3043 cb.height = 1 << 25; /* Invisible */
3044 Wimp_SetCaretPosition(&cb);
3051 | (Recursively) clear all ticks from the specified menu
3053 static void clear_all_menu_ticks(menu_ptr mp)
3055 menu_item *mi = (menu_item *) (mp + 1);
3059 if (mi->menuflags.data.ticked) mi->menuflags.data.ticked = 0;
3061 if (mi->submenu.value != -1) clear_all_menu_ticks(mi->submenu.menu);
3063 mi++;
3065 while (mi[-1].menuflags.data.last != 1);
3074 | Set the font menu's ticks to match the specifed font name.
3076 | fm is the (sub) menu to scan down (recursing into it's submenus (if any))
3077 | fn is the font name to match
3078 | prefix is the menu text to be prepended to the menu entries due to
3079 | previous menus (eg. "08x16" will cause fn="08x16.fred" to match menu
3080 | entry "fred".
3082 | NB: recursive.
3086 static void set_font_menu_ticks(menu_ptr fm, char *fn, const char *prefix)
3088 char buffer[260];
3089 char *b_leaf; /* -> menu 'leaf' text in buffer */
3090 int pl; /* prefix string length */
3091 menu_item *mi = (menu_item *) (fm + 1);
3093 my_strcpy(buffer, prefix, sizeof(buffer));
3095 pl = strlen(buffer);
3096 b_leaf = buffer + pl;
3100 /* Check for (substring) match */
3101 strncpy(b_leaf, mi->icondata.text, 12);
3103 /* Is it a sub-menu? */
3104 if (mi->submenu.value == -1)
3106 /* No - must be an exact match */
3107 mi->menuflags.data.ticked = !strcmp(buffer, fn);
3109 else
3111 /* Yes - must be a partial match (with a dot on :) */
3112 my_strcat(b_leaf, ".", sizeof(buffer) - pl);
3113 mi->menuflags.data.ticked =
3114 !strncmp(buffer, fn, pl + strlen(b_leaf));
3115 if (mi->menuflags.data.ticked)
3116 set_font_menu_ticks(mi->submenu.menu, fn, buffer);
3117 else
3118 clear_all_menu_ticks(mi->submenu.menu);
3121 /* Next item */
3122 mi++;
3124 while (mi[-1].menuflags.data.last != 1); /* Until finished */
3138 | Set ticks, etc. in the term_menu to reflect the current state of the
3139 | term 't'
3141 static void set_up_term_menu(term_data *t)
3143 int i;
3144 menu_ptr mp;
3145 menu_item *mi;
3146 window_state ws;
3148 /* First of all, set up menu title to be the term's title */
3149 strncpy(term_menu->title, t->name, 12);
3151 /* Now set the ticks in the Windows> submenu (cuz it's easy) */
3152 mp = wind_menu; /* Windows submenu */
3153 mi = (menu_item *) (mp + 1); /* First entry */
3154 for (i = 0; i < MAX_TERM_DATA; i++)
3156 Wimp_GetWindowState(data[i].w, &ws);
3157 mi[i].menuflags.data.ticked = ws.flags.data.open;
3161 | Now, the tricky bit: find out which font is selected in this
3162 | term and tick it in the menu. Untick all the rest.
3164 set_font_menu_ticks(font_menu, t->font->name, "");
3166 /* Shade the 'Save>' entry if saving isn't possible (yet) */
3167 if (game_in_progress && character_generated)
3168 Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, PDEADCHK);
3169 else
3170 Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, TRUE);
3173 * Shade the resize entry if Term->fixed_shape is set, or if
3174 * bigscreen isn't supported
3176 if (BIGSCREEN == FALSE || t->t.fixed_shape)
3177 Menu_SetFlags(term_menu, TERM_MENU_SIZE, 0, TRUE);
3178 else
3179 Menu_SetFlags(term_menu, TERM_MENU_SIZE, 0, FALSE);
3185 | Generic 'click' handler for windows - turns a drag on the window
3186 | into a move-window style drag.
3188 static BOOL Hnd_Click(event_pollblock * pb, void *ref)
3191 if (pb->data.mouse.window == data[0].w)
3193 if (pb->data.mouse.button.data.select)
3195 int fw, fh;
3196 int xpos, ypos;
3197 wimp_point clickpoint = pb->data.mouse.pos;
3198 convert_block convert;
3200 set_up_zrb(&data[0]);
3202 fw = zrb.r_charw << screen_eig.x;
3203 fh = zrb.r_charh << screen_eig.y;
3204 if (zrb.r_flags.bits.double_height)
3206 fh *= 2;
3209 /* SO fw & fh are in OS units here */
3210 Window_GetCoords(data[0].w, &convert);
3211 Coord_PointToWorkArea(&clickpoint, &convert);
3213 xpos = clickpoint.x / fw;
3214 ypos = -clickpoint.y / fh;
3216 Term_mousepress(xpos, ypos, 1);
3217 key_pressed = 1;
3220 else if (pb->data.mouse.button.data.dragselect ||
3221 pb->data.mouse.button.data.dragadjust)
3223 drag_block b;
3224 b.window = pb->data.mouse.window;
3225 b.screenrect.min.x = b.screenrect.min.y = 0;
3226 b.screenrect.max.x = screen_size.x;
3227 b.screenrect.max.y = screen_size.y;
3228 b.type = drag_MOVEWINDOW;
3229 Wimp_DragBox(&b);
3232 return TRUE;
3238 | Handle a click on a Term window.
3240 static BOOL Hnd_TermClick(event_pollblock * pb, void *ref)
3242 term_data *t = (term_data *)ref;
3244 if (pb->data.mouse.button.data.menu)
3246 menu_term = t;
3247 set_up_term_menu(t);
3248 Menu_Show(term_menu, pb->data.mouse.pos.x - 32,
3249 pb->data.mouse.pos.y + 32);
3251 else
3253 grab_caret();
3254 Hnd_Click(pb, ref);
3257 return TRUE;
3260 #endif /* !FULLSCREEN_ONLY */
3264 static void mark_ood(term_data *t, int minx, int miny, int maxx, int maxy)
3266 if (t->changed_box.min.x > minx)
3267 t->changed_box.min.x = minx;
3268 if (t->changed_box.max.x < maxx)
3269 t->changed_box.max.x = maxx;
3270 if (t->changed_box.min.y > miny)
3271 t->changed_box.min.y = miny;
3272 if (t->changed_box.max.y < maxy)
3273 t->changed_box.max.y = maxy;
3277 #ifndef FULLSCREEN_ONLY
3279 /* Check for an event (ie. key press) */
3280 static errr Term_xtra_acn_check(void)
3282 static int last_poll = 0;
3283 unsigned int curr_time;
3284 int bh, bl;
3287 | Only poll the wimp if there's something in the keyboard buffer
3288 | or every 10cs. This is presumably so that we process as many
3289 | keypresses as possible before any other application gets a go.
3292 /* Check the kbd buffer */
3293 SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
3294 bl = (bl & 0xff) + (bh << 8);
3296 /* Check how long it is since we last polled */
3297 curr_time = Time_Monotonic();
3299 if ((bl > 0 && got_caret) || ((curr_time - last_poll) > 9))
3301 last_poll = curr_time;
3302 STOP_HOURGLASS;
3303 Event_Poll();
3304 START_HOURGLASS;
3308 | This allows the user to interrupt the borg.
3310 if (key_pressed) return key_pressed = 0;
3312 return 1;
3317 | Wait for an event (ie. keypress)
3318 | Note that we idle poll once a second to allow us to implement the
3319 | alarm system.
3321 static errr Term_xtra_acn_event(void)
3323 STOP_HOURGLASS;
3325 while (!key_pressed && !fullscreen_font)
3327 Event_PollIdle(100);
3329 START_HOURGLASS;
3331 return key_pressed = 0;
3336 /* React to changes (eg. palette change) */
3337 static errr Term_xtra_acn_react(void)
3339 int c;
3341 cache_palette();
3343 /* Mark the entirety of each window as out of date */
3344 for (c = 0; c < MAX_TERM_DATA; c++)
3346 mark_ood(&data[c], 0, 0, data[c].t.wid, data[c].t.hgt);
3349 /* Force a redraw of the windows */
3350 refresh_windows();
3352 /* Success */
3353 return 0;
3356 #endif /* FULLSCREEN_ONLY */
3358 /* Do various things to a term */
3359 static errr Term_xtra_acn(int n, int v)
3361 term_data *t = (term_data *)Term;
3363 switch (n)
3365 /* Clear the Term */
3366 case TERM_XTRA_CLEAR:
3368 #ifndef FULLSCREEN_ONLY
3369 if (fullscreen_font)
3371 #endif
3372 if (t == (&data[0])) Term_xtra_acn_clearFS();
3373 #ifndef FULLSCREEN_ONLY
3375 else
3377 mark_ood(t, 0, 0, Term->wid, Term->hgt);
3379 #endif
3380 return 0;
3383 /* Wait/check for an event */
3384 case TERM_XTRA_EVENT:
3386 #ifndef FULLSCREEN_ONLY
3387 if (fullscreen_font)
3389 #endif
3390 Term_xtra_acn_eventFS(v);
3391 #ifndef FULLSCREEN_ONLY
3393 else
3395 if (v) return Term_xtra_acn_event();
3396 else return Term_xtra_acn_check();
3398 #endif
3401 /* Bored */
3402 case TERM_XTRA_BORED:
3404 #ifndef FULLSCREEN_ONLY
3405 if (fullscreen_font)
3406 #endif
3407 Term_xtra_acn_eventFS(0);
3408 #ifndef FULLSCREEN_ONLY
3409 else return Term_xtra_acn_check();
3410 #endif
3413 /* Flush input */
3414 case TERM_XTRA_FLUSH:
3416 #ifndef FULLSCREEN_ONLY
3417 if (fullscreen_font || got_caret)
3419 #endif
3420 /* 1.21 - Hack: wait until no keys are pressed */
3421 if (hack_flush)
3423 v = 0;
3424 while (v != 0xff) SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
3427 /* Flush Kbd buffer */
3428 SWI(3, 0, SWI_OS_Byte, 21, 0, 0);
3429 #ifndef FULLSCREEN_ONLY
3431 #endif
3432 return 0;
3435 /* Flush output */
3436 case TERM_XTRA_FRESH:
3438 #ifndef FULLSCREEN_ONLY
3439 if (!fullscreen_font) refresh_window(t);
3440 #endif
3441 return 0;
3444 /* Ensure line 'v' is plotted */
3445 case TERM_XTRA_FROSH:
3447 /* Doesn't do anything */
3448 return 0;
3451 /* Set cursor visibility */
3452 case TERM_XTRA_SHAPE:
3454 #ifndef FULLSCREEN_ONLY
3455 if (fullscreen_font)
3457 #endif
3458 if (t == (&data[0]))
3460 t->cursor.visible = v;
3461 if (v)
3462 draw_cursor(t->cursor.pos.x, t->cursor.pos.y);
3463 else
3464 redraw_areaFS(t->cursor.pos.x, t->cursor.pos.y, 1, 1);
3466 #ifndef FULLSCREEN_ONLY
3468 else
3470 t->cursor.visible = v ? TRUE : FALSE;
3471 mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
3472 t->cursor.pos.x + 1, t->cursor.pos.y + 1);
3474 refresh_window(t); /* needed? */
3476 #endif
3477 return 0;
3480 /* Make a beep */
3481 case TERM_XTRA_NOISE:
3483 Sound_SysBeep();
3484 return 0;
3487 /* React to, eg. palette changes */
3488 case TERM_XTRA_REACT:
3490 #ifndef FULLSCREEN_ONLY
3491 if (fullscreen_font)
3493 #endif
3494 return Term_xtra_acn_reactFS(FALSE);
3495 #ifndef FULLSCREEN_ONLY
3497 else
3499 return Term_xtra_acn_react();
3501 #endif
3504 /* Delay for 'v' ms */
3505 case TERM_XTRA_DELAY:
3507 if (v > 0)
3509 unsigned int start = Time_Monotonic();
3511 /* Round to nearest cs */
3512 v = (v + 5) / 10;
3514 /* Wait for vsync for the hell of it. */
3515 GFX_Wait();
3516 while ((Time_Monotonic() - start) < v);
3518 return 0;
3522 * This is used by ToME2, and presumably will never be picked up by other
3523 * variants, so it should be safe to #ifdef out like so:
3525 #ifdef TERM_XTRA_SCANSUBDIR
3526 /* Subdirectory scan */
3527 case TERM_XTRA_SCANSUBDIR:
3529 filing_dirdata directory;
3530 filing_direntry *entry;
3532 scansubdir_max = 0;
3534 if (Filing_OpenDir(riscosify_name(scansubdir_dir), &directory, sizeof(filing_direntry), readdirtype_DIRENTRY) != NULL)
3536 Error_Report(0, "Couldn't open directory \"%s\"", riscosify_name(scansubdir_dir));
3537 return 0;
3540 while ((entry = Filing_ReadDir(&directory)) != NULL)
3542 if (entry->objtype == filing_DIRECTORY)
3544 string_free(scansubdir_result[scansubdir_max]);
3545 scansubdir_result[scansubdir_max] = string_make(entry->name);
3546 ++scansubdir_max;
3550 Filing_CloseDir(&directory);
3552 return 0;
3554 #endif /* TERM_XTRA_SCANSUBDIR */
3558 * This is used by ToME, and presumably will never be picked up by other
3559 * variants, so it should be safe to #ifdef out like so:
3561 #ifdef TERM_XTRA_GET_DELAY
3562 /* Return current "time" in milliseconds */
3563 case TERM_XTRA_GET_DELAY:
3565 Term_xtra_long = Time_Monotonic() * 100;
3567 return 0;
3569 #endif /* TERM_XTRA_GET_DELAY */
3572 * This is used by ToME, and presumably will never be picked up by other
3573 * variants, so it should be safe to #ifdef out like so:
3575 #ifdef TERM_XTRA_RENAME_MAIN_WIN
3576 #ifndef FULLSCREEN_ONLY
3577 /* Rename main window */
3578 case TERM_XTRA_RENAME_MAIN_WIN:
3580 Window_SetTitle(data[0].w, angband_term_name[0]);
3581 return 0;
3583 #endif /* FULLSCREEN_ONLY */
3584 #endif /* TERM_XTRA_RENAME_MAIN_WIN */
3586 default:
3587 return 1; /* Unsupported */
3592 #ifndef FULLSCREEN_ONLY
3595 /* Move (but don't necessarily display) the cursor */
3596 static errr Term_curs_acn(int x, int y)
3598 term_data *t = (term_data *)Term;
3600 if (t->cursor.visible)
3601 mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
3602 t->cursor.pos.x + 1, t->cursor.pos.y + 1);
3604 t->cursor.pos.x = x;
3605 t->cursor.pos.y = y;
3607 if (t->cursor.visible)
3608 mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
3609 t->cursor.pos.x + 1, t->cursor.pos.y + 1);
3611 return 0;
3617 | NB: these two are very simple since we use the Term's contents
3618 | directly to generate the r_data for ZapRedraw.
3621 /* Erase 'n' characters at (x,y) */
3622 static errr Term_wipe_acn(int x, int y, int n)
3624 mark_ood((term_data *)Term, x, y, x + n, y + 1);
3625 return 0;
3628 /* Write 'n' characters from 's' with attr 'a' at (x,y) */
3629 static errr Term_text_acn(int x, int y, int n, byte a, cptr s)
3631 mark_ood((term_data *)Term, x, y, x + n, y + 1);
3632 return 0;
3635 #endif /* !FULLSCREEN_ONLY */
3638 /* Initialise one of our terms */
3639 static void Term_init_acn(term *t)
3641 term_data *term = (term_data *)t;
3643 /* Ludicrous changed box settings :) */
3644 term->changed_box.min.x = 256;
3645 term->changed_box.min.y = 256;
3646 term->changed_box.max.x = 0;
3647 term->changed_box.max.y = 0;
3652 static void term_data_link(term_data *td, int k)
3654 term *t = &(td->t);
3656 /* Initialise the term */
3657 term_init(t, 80, 24, k);
3659 /* Set flags and hooks */
3660 t->attr_blank = TERM_WHITE;
3661 t->char_blank = ' ';
3663 /* Experiment (FS mode requires them) */
3664 t->always_text = TRUE;
3665 t->never_frosh = TRUE;
3666 /* Experiment (FS mode requires them) */
3668 #ifdef FULLSCREEN_ONLY
3669 t->wipe_hook = Term_wipe_acnFS;
3670 t->xtra_hook = Term_xtra_acnFS;
3671 t->curs_hook = Term_curs_acnFS;
3672 t->text_hook = Term_text_acnFS;
3673 #else
3674 t->init_hook = Term_init_acn;
3675 t->xtra_hook = Term_xtra_acn;
3676 t->wipe_hook = Term_wipe_acn;
3677 t->curs_hook = Term_curs_acn;
3678 t->text_hook = Term_text_acn;
3679 t->user_hook = Term_user_acn;
3680 #endif /* FULLSCREEN_ONLY */
3682 t->data = td;
3684 Term_activate(t);
3688 #ifndef FULLSCREEN_ONLY
3690 /* Open default windows (ie. as set in choices) at the appropriate sizes */
3691 static void show_windows(void)
3693 int i;
3694 for (i = MAX_TERM_DATA; i-- > 0;)
3696 if (!data[i].unopened)
3698 if (data[i].def_open)
3699 Window_Show(data[i].w, open_WHEREVER);
3701 else
3703 if (data[i].def_open)
3705 window_openblock ob;
3706 ob.window = data[i].w;
3707 ob.screenrect = data[i].def_pos;
3708 ob.scroll = data[i].def_scroll;
3709 ob.behind = -1;
3710 Wimp_OpenWindow(&ob);
3711 data[i].unopened = 0;
3719 | 'ref' is used to indicate whether this close is being forced by some other
3720 | part of the code (eg. the Windows> submenu code). This is used to modify
3721 | the 'adjust doesn't close other terms' behavior.
3724 static BOOL Hnd_MainClose(event_pollblock * pb, void *ref)
3726 int i;
3727 window_state ws;
3728 mouse_block mb;
3730 /* New in 1.08: don't close other Terms if closed with adjust */
3731 Wimp_GetPointerInfo(&mb);
3732 if (ref || mb.button.data.adjust)
3734 Wimp_CloseWindow(data[0].w);
3736 else
3738 /* Close all the terms, but mark the open ones as 'def_open' */
3739 for (i = 0; i < MAX_TERM_DATA; i++)
3741 Wimp_GetWindowState(data[i].w, &ws);
3742 if (!ws.flags.data.open)
3744 data[i].def_open = 0;
3746 else
3748 Wimp_CloseWindow(data[i].w);
3749 data[i].def_open = 1;
3754 return TRUE;
3758 static BOOL Hnd_PaletteChange(event_pollblock * pb, void *ref)
3760 cache_palette();
3761 return TRUE;
3766 static BOOL Hnd_ModeChange(event_pollblock * pb, void *ref)
3768 int i;
3769 Screen_CacheModeInfo();
3770 set_up_zrb_for_mode(); /* (re)set up the redraw block */
3771 cache_palette(); /* (re)cache the palette */
3772 cache_fonts(); /* (re)cache the fonts */
3774 /* Enforce sizes (eg. if screen_eig.y has changed) */
3775 for (i = 0; i < MAX_TERM_DATA; i++) force_term_resize(&(data[i]));
3777 return TRUE;
3783 static BOOL Hnd_Keypress(event_pollblock * pb, void *ref)
3785 static const char hex[] = "0123456789ABCDEF";
3786 int c = pb->data.key.code;
3787 /* Check whether this key was pressed in Term 0 */
3788 if (pb->data.key.caret.window == data[0].w)
3790 switch (c)
3792 case keycode_F12:
3793 case keycode_SHIFT_F12:
3794 case keycode_CTRL_F12:
3795 case keycode_CTRL_SHIFT_F12:
3796 /* Never intercept these */
3797 break;
3799 case 27: /* handle escape specially */
3800 if (Kbd_KeyDown(inkey_CTRL))
3802 ack_alarm();
3803 return TRUE;
3806 /* Send everything else onto the Term package */
3807 default:
3808 /* Take care of "special" keypresses */
3809 switch (c)
3811 case keycode_TAB:
3813 c = '\t';
3814 break;
3817 case keycode_PAGEUP:
3819 c = '9';
3820 break;
3823 case keycode_PAGEDOWN:
3825 c = '3';
3826 break;
3829 case keycode_COPY:
3831 c = '1';
3832 break;
3835 case keycode_HOME:
3837 c = '7';
3838 break;
3841 /* Pass to the angband engine */
3842 /* Allow shift & ctrl to modify the keypad keys */
3843 if (c >= '0' && c <= '9')
3845 kbd_modifiers m = Kbd_GetModifiers(FALSE);
3846 if (m.shift)
3848 c |= 0x800;
3850 if (m.ctrl)
3852 c |= 0x400;
3854 /* Could maybe add ALT as 0x1000 ??? */
3857 /* Keys >255 have to be send as escape sequences (31=escape) */
3858 if (c > 255 || c == 31)
3860 Term_keypress(31);
3861 Term_keypress(hex[(c & 0xf00) >> 8]);
3862 Term_keypress(hex[(c & 0x0f0) >> 4]);
3863 Term_keypress(hex[(c & 0x00f)]);
3864 c = 13;
3866 Term_keypress(c);
3867 key_pressed = 1;
3868 /*if ( c==27 ) { escape_pressed = 1; } */
3869 return TRUE;
3873 Wimp_ProcessKey(c);
3874 return TRUE;
3878 /*--------------------------------------------------------------------------*/
3879 /* Gamma correction window stuff */
3880 /*--------------------------------------------------------------------------*/
3882 static void redraw_gamma(window_redrawblock * rb, BOOL *more)
3884 int i, y, x, h, w;
3885 int bx, by;
3886 int dither;
3888 bx = Coord_XToScreen(GC_XOFF, (convert_block *) & (rb->rect));
3889 by = Coord_YToScreen(GC_YOFF, (convert_block *) & (rb->rect));
3891 h = GC_HEIGHT / 4;
3892 w = GC_WIDTH / 16;
3894 x = bx;
3896 while (*more)
3898 for (i = 0; i < 16; i++)
3900 y = by;
3901 for (dither = 0; dither < 2; dither++)
3903 /* Solid block: */
3904 ColourTrans_SetGCOL(palette[i], dither << 8, 0);
3905 GFX_RectangleFill(x, y, w, -h);
3906 y -= h;
3907 /* Dot on black: */
3908 ColourTrans_SetGCOL(palette[0], dither << 8, 0);
3909 GFX_RectangleFill(x, y, w, -h);
3910 ColourTrans_SetGCOL(palette[i], dither << 8, 0);
3911 GFX_RectangleFill(x + (w / 2) - 2, y - (h / 2), 2, 2);
3912 y -= h;
3914 x += w;
3916 Wimp_GetRectangle(rb, more);
3921 static void update_gamma(void)
3923 window_redrawblock rb;
3924 BOOL more;
3926 rb.window = gamma_win;
3927 rb.rect.min.x = GC_XOFF;
3928 rb.rect.min.y = GC_YOFF - GC_HEIGHT;
3929 rb.rect.max.y = GC_XOFF + GC_WIDTH + screen_delta.x;
3930 rb.rect.max.y = GC_YOFF + screen_delta.y;
3932 Wimp_UpdateWindow(&rb, &more);
3933 if (more) redraw_gamma(&rb, &more);
3938 static BOOL Hnd_RedrawGamma(event_pollblock * pb, void *ref)
3940 window_redrawblock rb;
3941 BOOL more;
3943 rb.window = pb->data.openblock.window;
3944 Wimp_RedrawWindow(&rb, &more);
3945 if (more) redraw_gamma(&rb, &more);
3947 return TRUE;
3951 static BOOL Hnd_GammaClick(event_pollblock * pb, void *ref)
3953 int up = (ref == 0);
3955 if (up)
3957 if (gamma < 900)
3959 gamma += 5;
3960 Icon_SetDouble(gamma_win, 0, (double) gamma / 100, 2);
3961 update_gamma();
3962 Term_xtra_acn_react();
3965 else
3967 if (gamma > 5)
3969 gamma -= 5;
3970 Icon_SetDouble(gamma_win, GAMMA_ICN, (double) gamma / 100, 2);
3971 update_gamma();
3972 Term_xtra_acn_react();
3976 /* Hack: if the user menu is active then force it to redraw */
3977 if (user_menu_active)
3979 Term_keypress(18);
3980 key_pressed = 1;
3983 return TRUE;
3987 | Reflect the current options in the gamma window
3989 static void set_gamma_window_state(void)
3991 if (minimise_memory) return;
3993 Icon_SetDouble(gamma_win, 0, (double) gamma / 100, 2);
3997 static void init_gamma_window(void)
3999 if (minimise_memory) return;
4001 gamma_win = Window_Create("gamma", template_TITLEMIN);
4002 Event_Claim(event_REDRAW, gamma_win, event_ANY, Hnd_RedrawGamma, 0);
4003 Event_Claim(event_CLICK, gamma_win, GAMMA_DOWN, Hnd_GammaClick, (void *)1);
4004 Event_Claim(event_CLICK, gamma_win, GAMMA_UP, Hnd_GammaClick, (void *)0);
4005 set_gamma_window_state();
4009 /*--------------------------------------------------------------------------*/
4010 /* Sound options window stuff */
4011 /*--------------------------------------------------------------------------*/
4013 static slider_info volume_slider;
4017 | Reflect the current sound config in the sound options window
4019 static void set_sound_window_state(void)
4021 if (minimise_memory) return;
4023 Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
4024 Slider_SetValue(&volume_slider, sound_volume, NULL, NULL);
4026 if (sound_volume > 127)
4027 volume_slider.colour.foreground = colour_RED;
4028 else
4029 volume_slider.colour.foreground = colour_GREEN;
4035 | The sound slider has been dragged, so update the sound volume setting
4037 static int update_volume_from_slider(slider_info *si, void *ref)
4039 sound_volume = Slider_ReadValue(si);
4041 if (sound_volume > 127)
4042 volume_slider.colour.foreground = colour_RED;
4043 else
4044 volume_slider.colour.foreground = colour_GREEN;
4046 return 0;
4051 | Handle redraw events for the sound options window
4053 static BOOL Hnd_RedrawSnd(event_pollblock * pb, void *ref)
4055 window_redrawblock redraw;
4056 BOOL more;
4058 redraw.window = pb->data.openblock.window;
4059 Wimp_RedrawWindow(&redraw, &more);
4061 while (more)
4063 Slider_Redraw(((slider_info *) ref), &redraw.cliprect);
4064 Wimp_GetRectangle(&redraw, &more);
4066 return (TRUE);
4071 | Handle clicks on the sound options window
4073 static BOOL Hnd_SndClick(event_pollblock * pb, void *ref)
4075 int icn = pb->data.mouse.icon;
4076 int adj = pb->data.mouse.button.data.adjust;
4077 slider_info *si = (slider_info *) ref;
4079 switch (icn)
4081 /* Bump arrows for the slider: */
4082 case SND_VOL_DOWN:
4083 adj = !adj;
4085 case SND_VOL_UP:
4086 adj = adj ? -1 : 1;
4087 sound_volume += adj;
4088 if (sound_volume < SOUND_VOL_MIN)
4090 sound_volume = SOUND_VOL_MIN;
4092 if (sound_volume > SOUND_VOL_MAX)
4094 sound_volume = SOUND_VOL_MAX;
4096 set_sound_window_state();
4097 break;
4099 /* The slider itself */
4100 case SND_VOL_SLIDER:
4101 Icon_ForceRedraw(sound_win, SND_VOL_SLIDER);
4102 Slider_Drag(si, NULL, NULL, NULL);
4103 break;
4105 /* The enable/disable icon */
4106 case SND_ENABLE:
4107 enable_sound = !enable_sound;
4108 Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
4109 if (enable_sound)
4111 initialise_sound();
4113 break;
4116 /* Hack: if the user menu is active then force it to redraw */
4117 if (user_menu_active)
4119 Term_keypress(18);
4120 key_pressed = 1;
4123 return TRUE;
4129 | Set the sound options window up, ready to rock :)
4131 static void init_sound_window(void)
4133 sound_win = Window_Create("sound", template_TITLEMIN);
4135 Event_Claim(event_REDRAW, sound_win, event_ANY, Hnd_RedrawSnd,
4136 (void *)&volume_slider);
4137 Event_Claim(event_CLICK, sound_win, event_ANY, Hnd_SndClick,
4138 (void *)&volume_slider);
4140 /* Set up the slider info */
4141 volume_slider.window = sound_win;
4142 volume_slider.icon = SND_VOL_SLIDER;
4143 volume_slider.limits.min = SOUND_VOL_MIN;
4144 volume_slider.limits.max = SOUND_VOL_MAX;
4145 volume_slider.colour.foreground = colour_GREEN;
4146 volume_slider.colour.background = colour_WHITE;
4147 volume_slider.border.x = 8;
4148 volume_slider.border.y = 8;
4149 volume_slider.update = update_volume_from_slider;
4151 set_sound_window_state();
4158 /*--------------------------------------------------------------------------*/
4171 | A font has been selected.
4172 | At this point, menu_term is a pointer to the term for which the
4173 | menu was opened.
4175 static void handle_font_selection(int *s)
4177 char name[260];
4178 os_error *e;
4179 char *r;
4180 menu_ptr mp = font_menu;
4181 int *mis;
4183 /* Follow the >s to the entry specified */
4184 for (mis = s; *mis != -1; mis++)
4185 mp = ((menu_item *) (mp + 1))[*mis].submenu.menu;
4188 | Now, check to see if we've hit a leaf entry.
4189 | NB: If the entry isn't a leaf entry then the first entry in its submenu
4190 | is used instead
4192 if (((int)mp) != -1)
4194 mis[0] = 0;
4195 mis[1] = -1;
4196 mp = ((menu_item *) (mp + 1))[0].submenu.menu;
4199 if (((int)mp) != -1)
4200 return;
4202 e = Wimp_DecodeMenu(font_menu, s, name);
4203 if (e)
4205 plog(e->errmess);
4206 return;
4209 /* Make sure that the string is NULL terminated */
4210 for (r = name; *r >= ' '; r++)
4212 *r = 0;
4214 attach_font_to_term(menu_term, name);
4215 mark_ood(menu_term, 0, 0, menu_term->t.wid, menu_term->t.hgt);
4216 refresh_window(menu_term);
4220 #endif /* FULLSCREEN_ONLY */
4225 static void load_choices(void)
4227 FILE *fp = NULL;
4228 char *cf;
4229 int i;
4230 char buffer[260];
4231 int choices_version = 0;
4233 cf = find_choices(FALSE);
4234 if (*cf)
4235 fp = fopen(cf, "r");
4237 /* Implement default choices */
4238 data[0].def_open = 1;
4239 data[0].unopened = 1; /* ie. force def_pos */
4240 data[0].def_pos.min.x = (screen_size.x - 1280) / 2;
4241 data[0].def_pos.max.x = (screen_size.x + 1280) / 2;
4242 data[0].def_pos.min.y = (screen_size.y - 768) / 2 - 32;
4243 data[0].def_pos.max.y = (screen_size.y + 768) / 2 - 32;
4244 data[0].def_scroll.x = data[0].def_scroll.y = 0;
4245 for (i = 1; i < MAX_TERM_DATA; i++)
4247 data[i].def_open = 0;
4248 data[i].unopened = 1; /* ie. force def_pos */
4249 data[i].def_pos.min.x = (screen_size.x - 1280) / 2;
4250 data[i].def_pos.max.x = (screen_size.x + 1280) / 2;
4251 data[i].def_pos.min.y = (screen_size.y - 768) / 2;
4252 data[i].def_pos.max.y = (screen_size.y + 768) / 2;
4253 data[i].def_scroll.x = data[i].def_scroll.y = 0;
4256 if (fp)
4258 const char *t_;
4259 char *o_;
4261 if (!fgets(buffer, sizeof(buffer), fp))
4263 fclose(fp);
4264 return;
4267 if (memcmp(buffer, "[Angband config, Musus' port]\n", 30) == 0)
4269 choices_version = 1;
4271 else if (memcmp(buffer, "[Angband config v2, Musus' port]\n", 33) == 0)
4273 choices_version = 2;
4276 if (choices_version == 0)
4278 fclose(fp);
4279 return;
4282 /* Load choices */
4283 while (fgets(buffer, sizeof(buffer), fp))
4285 t_ = strtok(buffer, " "); /* Term number (or keyword, "Gamma", etc.) */
4286 o_ = strtok(NULL, "\n"); /* argument string */
4287 if (!o_)
4289 o_ = "";
4290 } /* missing (or null) argument? */
4291 if (t_)
4293 if (!strcmp(t_, "Gamma"))
4294 gamma = atof(o_) * 100;
4295 else if (!strcmp(t_, "Monochrome"))
4296 force_mono = !strcmp(o_, "on");
4297 else if (!strcmp(t_, "Sound"))
4298 enable_sound = !strcmp(o_, "on");
4299 else if (!strcmp(t_, "Volume"))
4300 sound_volume = atoi(o_);
4301 else if (!strcmp(t_, "FullScreen"))
4302 start_fullscreen = !strcmp(o_, "on");
4303 else if (!strcmp(t_, "Hourglass"))
4304 use_glass = !strcmp(o_, "on");
4305 else if (!strcmp(t_, "HackFlush"))
4306 hack_flush = !strcmp(o_, "on");
4307 else if (!strcmp(t_, "AlarmTimeH"))
4308 alarm_h = atoi(o_);
4309 else if (!strcmp(t_, "AlarmTimeM"))
4310 alarm_m = atoi(o_);
4311 else if (!strcmp(t_, "AlarmText"))
4312 strcpy(alarm_message, o_);
4313 else if (!strcmp(t_, "AlarmBeep"))
4314 alarm_beep = !strcmp(o_, "on");
4315 else if (!strcmp(t_, "AlarmType"))
4317 int i;
4318 for (i = 0; i < 4; i++)
4319 if (!strcmp(alarm_types[i], o_))
4320 alarm_type = i;
4322 else if (isdigit((unsigned char)*t_))
4324 int t = atoi(t_);
4325 if (t >= 0 && t < MAX_TERM_DATA)
4327 char *f, *x0, *y0, *x1, *y1, *sx, *sy, *dx = NULL, *dy = NULL;
4328 o_ = strtok(o_, " "); /* first word */
4329 f = strtok(NULL, " "); /* font name */
4330 x0 = strtok(NULL, " "); /* x posn (min) */
4331 y0 = strtok(NULL, " "); /* y posn (min) */
4332 x1 = strtok(NULL, " "); /* x posn (max) */
4333 y1 = strtok(NULL, " "); /* y posn (max) */
4334 sx = strtok(NULL, " "); /* x scroll offset */
4336 if (choices_version == 1)
4338 sy = strtok(NULL, "\n"); /* y scroll offset */
4340 else
4342 sy = strtok(NULL, " "); /* y scroll offset */
4343 dx = strtok(NULL, " "); /* width */
4344 dy = strtok(NULL, " "); /* height */
4347 data[t].def_open = (t == 0) || atoi(o_);
4348 data[t].def_pos.min.x = atoi(x0);
4349 data[t].def_pos.min.y = atoi(y0);
4350 data[t].def_pos.max.x = atoi(x1);
4351 data[t].def_pos.max.y = atoi(y1);
4352 data[t].def_scroll.x = atoi(sx);
4353 data[t].def_scroll.y = atoi(sy);
4354 data[t].unopened = 1; /* ie. force def_pos */
4355 #ifndef FULLSCREEN_ONLY
4356 /* Ensure we have size before setting it. */
4357 if (dx && dy)
4359 term_change_size(&data[t], atoi(dx), atoi(dy));
4362 attach_font_to_term(&(data[t]), f);
4363 #endif /* FULLSCREEN_ONLY */
4368 fclose(fp);
4375 static void save_choices(void)
4377 FILE *fp = NULL;
4378 FILE *fpm = NULL;
4379 char *cf;
4380 int i;
4382 write_alarm_choices();
4384 cf = find_choices(TRUE);
4385 if (!*cf)
4387 plog("Failed to locate writable choices file!");
4388 return;
4391 fp = fopen(cf, "w");
4392 if (!fp)
4394 plog("Can't write choices file");
4395 return;
4398 fpm = fopen(find_choices_mirror(), "w");
4400 f2printf(fp, fpm, "[Angband config v2, Musus' port]\n");
4401 f2printf(fp, fpm, "Gamma %.2lf\n", (double) gamma / 100);
4402 f2printf(fp, fpm, "Monochrome %s\n", force_mono ? "on" : "off");
4403 f2printf(fp, fpm, "Sound %s\n", enable_sound ? "on" : "off");
4404 f2printf(fp, fpm, "Volume %d\n", sound_volume);
4405 f2printf(fp, fpm, "FullScreen %s\n", start_fullscreen ? "on" : "off");
4406 f2printf(fp, fpm, "Hourglass %s\n", use_glass ? "on" : "off");
4407 f2printf(fp, fpm, "HackFlush %s\n", hack_flush ? "on" : "off");
4409 for (i = 0; i < MAX_TERM_DATA; i++)
4411 window_state ws;
4412 Wimp_GetWindowState(data[i].w, &ws);
4413 f2printf(fp, fpm, "%d %d %s ", i, ws.flags.data.open,
4414 data[i].font->name);
4415 f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.x);
4416 f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.y);
4417 f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.x);
4418 f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.y);
4419 f2printf(fp, fpm, "%d %d ", ws.openblock.scroll.x,
4420 ws.openblock.scroll.y);
4421 f2printf(fp, fpm, "%d %d\n", data[i].t.wid, data[i].t.hgt);
4424 fclose(fp);
4426 if (fpm)
4428 fclose(fpm);
4433 | Update the Alarm choices file to reflect changed alarm settings.
4435 static void write_alarm_choices(void)
4437 FILE *fp;
4438 char *cf;
4440 /* Open the choices file for reading */
4441 cf = find_alarmfile(TRUE);
4442 if (!*cf)
4444 plog("Can't determine Alarm file location!");
4445 return;
4448 fp = fopen(cf, "w");
4449 if (!fp)
4451 plog("Can't write Alarm file");
4452 return;
4455 /* Write the new alarm options */
4456 fprintf(fp, "AlarmType %s\n", alarm_types[alarm_type]);
4457 fprintf(fp, "AlarmTimeH %d\n", alarm_h);
4458 fprintf(fp, "AlarmTimeM %d\n", alarm_m);
4459 fprintf(fp, "AlarmText %s\n", alarm_message);
4460 fprintf(fp, "AlarmBeep %s\n", alarm_beep ? "on" : "off");
4462 fclose(fp);
4466 | Read the Alarm choices file.
4468 static void read_alarm_choices(void)
4470 char buffer[260];
4471 FILE *fp;
4472 char *cf;
4474 cf = find_alarmfile(FALSE);
4475 if (!*cf)
4477 return;
4480 fp = fopen(cf, "r");
4481 if (fp)
4483 const char *t_, *o_;
4484 /* Load choices */
4485 while (fgets(buffer, sizeof(buffer), fp))
4487 t_ = strtok(buffer, " "); /* Keyword */
4488 o_ = strtok(NULL, "\n"); /* argument string */
4489 if (!o_)
4491 o_ = "";
4492 } /* missing (or null) argument? */
4493 if (t_)
4495 if (!strcmp(t_, "AlarmTimeH"))
4496 alarm_h = atoi(o_);
4497 else if (!strcmp(t_, "AlarmTimeM"))
4498 alarm_m = atoi(o_);
4499 else if (!strcmp(t_, "AlarmText"))
4500 strcpy(alarm_message, o_);
4501 else if (!strcmp(t_, "AlarmBeep"))
4502 alarm_beep = !strcmp(o_, "on");
4503 else if (!strcmp(t_, "AlarmType"))
4505 int i;
4506 for (i = 0; i < 4; i++)
4507 if (!strcmp(alarm_types[i], o_))
4508 alarm_type = i;
4512 fclose(fp);
4518 #ifndef FULLSCREEN_ONLY
4520 | Handle selections from the term menu(s)
4522 static BOOL Hnd_TermMenu(event_pollblock * pb, void *ref)
4524 mouse_block mb;
4525 int i;
4527 Wimp_GetPointerInfo(&mb);
4529 switch (pb->data.selection[0])
4531 case TERM_MENU_INFO: /* Info> */
4532 break;
4533 case TERM_MENU_FONT: /* Font> */
4534 /* Sub item selected? */
4535 if (pb->data.selection[1] == -1)
4537 break;
4539 handle_font_selection(pb->data.selection + 1);
4540 break;
4541 case TERM_MENU_WINDOWS: /* Windows> */
4542 if (pb->data.selection[1] == -1)
4544 break;
4546 i = pb->data.selection[1];
4548 window_state ws;
4549 Wimp_GetWindowState(data[i].w, &ws);
4550 if (ws.flags.data.open)
4552 if (!i)
4553 Hnd_MainClose(NULL, (void *)TRUE);
4554 else
4555 Window_Hide(data[i].w);
4557 else
4559 if (!i)
4561 show_windows();
4562 grab_caret();
4564 else
4566 if (!data[i].unopened)
4568 Window_Show(data[i].w, open_WHEREVER);
4570 else
4572 window_openblock ob;
4573 ob.window = data[i].w;
4574 ob.screenrect = data[i].def_pos;
4575 ob.scroll = data[i].def_scroll;
4576 ob.behind = -1; /* could use data[0].w; ? */
4577 Wimp_OpenWindow(&ob);
4578 data[i].unopened = 0;
4583 break;
4586 if (mb.button.data.adjust)
4588 set_up_term_menu(menu_term);
4589 Menu_ShowLast();
4592 return TRUE;
4601 | Handle selections from the iconbar menu
4603 static BOOL Hnd_IbarMenu(event_pollblock * pb, void *ref)
4605 mouse_block mb;
4606 Wimp_GetPointerInfo(&mb);
4608 switch (pb->data.selection[0])
4610 case IBAR_MENU_INFO: /* Info> */
4611 break;
4612 case IBAR_MENU_FULLSCREEN: /* Full screen */
4613 /* Do Full Screen mode */
4614 enter_fullscreen_mode();
4615 break;
4616 case IBAR_MENU_GAMMA: /* Gamma correction */
4617 break;
4618 case IBAR_MENU_SOUND: /* Sound */
4620 | enable_sound = !enable_sound;
4621 | if ( enable_sound ) { initialise_sound(); }
4622 | Menu_SetFlags( ibar_menu, IBAR_MENU_SOUND, enable_sound, 0 );
4623 | set_sound_window_state();
4625 break;
4626 case IBAR_MENU_WINDOWS: /* Windows> */
4628 | Hack: pass it off as the equivalent selection from
4629 | the term menu.
4631 pb->data.selection[0] = TERM_MENU_WINDOWS;
4632 return Hnd_TermMenu(pb, ref);
4633 break;
4634 case IBAR_MENU_SAVECHOICES: /* Save choices */
4635 save_choices();
4636 break;
4637 case IBAR_MENU_QUIT: /* Quit */
4638 if (game_in_progress && character_generated)
4640 if (inkey_flag)
4642 save_player(SAVE_PLAYER_PARAM);
4643 quit(NULL);
4645 else
4647 Msgs_Report(0, "err.nosave");
4650 break;
4653 if (mb.button.data.adjust)
4654 Menu_ShowLast();
4656 return TRUE;
4663 static BOOL Hnd_MenuSel(event_pollblock * pb, void *ref)
4665 if (menu_currentopen == ibar_menu)
4666 return Hnd_IbarMenu(pb, ref);
4667 else if (menu_currentopen == term_menu)
4668 return Hnd_TermMenu(pb, ref);
4669 return FALSE;
4673 static BOOL Hnd_IbarClick(event_pollblock * pb, void *ref)
4675 if (pb->data.mouse.button.data.menu)
4677 set_gamma_window_state();
4678 set_sound_window_state();
4680 /* Hack: shade the Save> option if appropriate */
4681 if (game_in_progress && character_generated)
4682 Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, PDEADCHK);
4683 else
4684 Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, TRUE);
4687 | Hack: set up the Term menu as if it was opened over the main
4688 | window (so that the Windows> submenu is set correctly)
4690 menu_term = (term_data *)&data[0];
4691 set_up_term_menu(menu_term);
4693 Menu_Show(ibar_menu, pb->data.mouse.pos.x, -1);
4694 return TRUE;
4697 if (pb->data.mouse.button.data.select)
4699 show_windows();
4700 grab_caret();
4701 return TRUE;
4704 if (pb->data.mouse.button.data.adjust)
4706 enter_fullscreen_mode();
4707 return TRUE;
4710 return FALSE;
4716 * Handler for NULL events (should this check the alarm in the desktop?
4718 static BOOL Hnd_null(event_pollblock *event, void *ref)
4720 /* Really no need to check the alarm more than once per second. */
4721 if (alarm_type && Time_Monotonic() > alarm_lastcheck + 100)
4723 check_alarm();
4726 return TRUE;
4733 | Handler for PreQuit messages (eg. at shutdown).
4735 static BOOL Hnd_PreQuit(event_pollblock * b, void *ref)
4737 BOOL shutdown = (b->data.message.data.words[0] & 1) == 0;
4738 task_handle originator = b->data.message.header.sender;
4739 unsigned int quitref;
4740 message_block mb;
4741 char buffer1[64];
4742 os_error e;
4743 int ok;
4745 if (!(game_in_progress && character_generated))
4746 return TRUE; /* ignore, we're OK to die */
4748 /* Stop the shutdown/quit */
4749 memcpy(&mb, &(b->data.message), 24);
4750 quitref = mb.header.yourref;
4751 mb.header.yourref = mb.header.myref;
4752 Wimp_SendMessage(event_ACK, &mb, originator, 0);
4755 | We handle this differently depending on the version of the Wimp;
4756 | newer versions give us much more flexibility.
4758 if (event_wimpversion < 350)
4761 | Older versions - use 'OK' and 'Cancel'.
4762 | There is no "Save & Quit" button.
4764 Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
4765 sprintf(buffer1, e.errmess, VARIANT);
4766 Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
4767 e.errmess, 260);
4768 e.errnum = 0;
4769 SWI(3, 2, SWI_Wimp_ReportError, &e, 3 | 16, buffer1, NULL, &ok);
4771 if (ok != 1)
4772 return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */
4774 else
4777 | Newer version: can add buttons to the dialog.
4778 | we add a 'Save and Quit' button to allow the shutdown to
4779 | continue /after/ saving.
4781 int flags;
4782 char buttons[64];
4784 Msgs_Lookup("err.shutbuts:Save & quit,Don't quit,Quit anyway",
4785 buttons, 64);
4786 Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
4787 sprintf(buffer1, e.errmess, VARIANT);
4788 Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
4789 e.errmess, 260);
4790 e.errnum = 0;
4792 flags = 0 | 16 | 256 | (4 << 9);
4794 SWI(6, 2, SWI_Wimp_ReportError, &e, flags, buffer1, ICONNAME, 0,
4795 buttons, NULL, &ok);
4797 if (ok == 4)
4798 return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */
4800 if (ok == 3)
4802 if (inkey_flag)
4804 save_player(SAVE_PLAYER_PARAM); /* Save & Quit */
4806 else
4808 Msgs_Report(0, "err.nosave");
4809 return FALSE;
4815 /* RO2 doesn't use the shudown flag */
4816 if (shutdown && event_wimpversion >= 300 && mb.header.size >= 24)
4818 key_block kb;
4819 kb.code = 0x1fc; /* restart shutdown sequence */
4820 Wimp_SendMessage(event_KEY, (message_block *) & kb, originator, 0);
4823 /* "Time... to die." */
4824 Event_CloseDown();
4825 exit(0);
4826 return TRUE; /* The one great certainty (sic) */
4829 #endif /* FULLSCREEN_ONLY */
4834 static void initialise_terms(void)
4836 char t[80];
4837 int i;
4839 #ifndef FULLSCREEN_ONLY
4840 if (!minimise_memory)
4842 /* Create a window for each term. Term 0 is special (no scroll bars) */
4843 data[0].w = Window_Create("angband", template_TITLEMIN);
4844 data[0].font = SYSTEM_FONT;
4845 data[0].def_open = 1;
4846 data[0].unopened = 1;
4847 sprintf(t, "%s %s", VARIANT, VERSION);
4848 Window_SetTitle(data[0].w, t);
4849 strncpy(data[0].name, VARIANT, 12);
4850 Event_Claim(event_KEY, data[0].w, event_ANY, Hnd_Keypress,
4851 (void *)&(data[0]));
4852 Event_Claim(event_REDRAW, data[0].w, event_ANY, Hnd_Redraw,
4853 (void *)&(data[0]));
4854 Event_Claim(event_CLICK, data[0].w, event_ANY, Hnd_TermClick,
4855 (void *)&(data[0]));
4856 Event_Claim(event_CLOSE, data[0].w, event_ANY, Hnd_MainClose, NULL);
4858 for (i = 1; i < MAX_TERM_DATA; i++)
4860 data[i].w = Window_Create("term", template_TITLEMIN);
4861 data[i].font = SYSTEM_FONT;
4862 data[i].def_open = 0;
4863 data[i].unopened = 1;
4864 #ifndef OLD_TERM_MENU
4865 sprintf(t, "%s (%s %s)", TERM_NAME(i), VARIANT, VERSION);
4866 #else
4867 sprintf(t, "Term-%d (%s %s)", i, VARIANT, VERSION);
4868 #endif
4869 Window_SetTitle(data[i].w, t);
4870 strncpy(data[i].name, t, 12);
4871 Event_Claim(event_CLICK, data[i].w, event_ANY, Hnd_TermClick,
4872 (void *)&(data[i]));
4873 Event_Claim(event_REDRAW, data[i].w, event_ANY, Hnd_Redraw,
4874 (void *)&(data[i]));
4877 #endif /* FULLSCREEN_ONLY */
4879 term_data_link(&(data[0]), 256);
4881 for (i = 1; i < MAX_TERM_DATA; i++)
4883 term_data_link(&(data[i]), 16);
4884 TERM(i) = &(data[i].t);
4887 TERM(0) = &(data[0].t);
4888 Term_activate(&(data[0].t));
4894 static unsigned int htoi(char *s)
4896 static const char hex[] = "0123456789ABCDEF";
4897 unsigned int v = 0;
4898 while (*s)
4900 char *m;
4901 int d = toupper((unsigned char)*s++);
4902 m = strchr(hex, d);
4903 if (!m)
4905 return v;
4907 v = (v << 4) + (m - hex);
4909 return v;
4912 static int read_unsigned(char *t)
4914 int r;
4915 if (SWI(2, 3, SWI_OS_ReadUnsigned, 2, t, NULL, NULL, &r))
4916 r = 0;
4917 return r;
4921 | Scan the string at 'n', replacing dodgy characters with underbars
4923 static void sanitise_name(char *n)
4925 for (; *n; n++)
4927 if (strchr("\"$%^&*\\\'@#.,", *n))
4928 *n = '_';
4934 | Ensure that the path to a given object exists.
4935 | Ie. if |p| = "a.b.c.d" then we attempt to
4936 | create directories a, a.b and a.b.c if they don't
4937 | already exist.
4938 | Note that 'd' may be absent.
4940 static int ensure_path(char *p)
4942 char tmp[260];
4943 char *l = tmp;
4945 while (*p)
4947 if (*p == '.')
4949 *l = 0;
4950 if (SWI(5, 0, SWI_OS_File, 8, tmp, 0, 0, 77))
4951 return 0; /* Eeek! */
4953 *l++ = *p++;
4956 return 1;
4961 * Set up the Scrap, Choices and Alarm paths, trying for
4962 * Choices:blah...,etc. by preference, but falling back on lib/xtra
4963 * if need be.
4965 static void init_paths(void)
4967 char tmp[512];
4968 char subpath[128];
4969 char *v;
4970 char *t;
4972 /* Form the sub-path we use for both Choices and Scrap dirs: */
4973 v = subpath + sprintf(subpath, "%s", VARIANT);
4974 sanitise_name(subpath);
4975 sprintf(v, ".%s", VERSION);
4976 sanitise_name(v + 1);
4978 /* Do the Scrap path first: */
4979 *scrap_path = 0;
4981 /* Try for Wimp$ScrapDir... */
4982 t = getenv("Wimp$ScrapDir");
4983 if (t && *t)
4985 sprintf(tmp, "%s.AngbandEtc.%s.", t, subpath);
4986 if (ensure_path(tmp))
4988 strcpy(scrap_path, tmp);
4992 /* Couldn't use Wimp$ScrapDir, so fall back on lib.xtra.scrap */
4993 if (!*scrap_path)
4995 sprintf(tmp, "%sxtra.scrap.", resource_path);
4996 if (ensure_path(tmp))
4998 strcpy(scrap_path, tmp);
5002 /* Now set up the Choices and Alarm files: */
5004 /* Read only Choices file is always lib.xtra.Choices */
5005 sprintf(choices_file[CHFILE_READ], "%sXtra.Choices", resource_path);
5006 /* Default writable Choices file is the same */
5007 strcpy(choices_file[CHFILE_WRITE], choices_file[CHFILE_READ]);
5008 /* No default mirror Choices file */
5009 strcpy(choices_file[CHFILE_MIRROR], "");
5011 /* Read only Alarm file is always lib.xtra.Alarm */
5012 sprintf(alarm_file[CHFILE_READ], "%sXtra.Alarm", resource_path);
5013 /* Default writable Alarm file is the same */
5014 strcpy(alarm_file[CHFILE_WRITE], alarm_file[CHFILE_READ]);
5016 /* Try to use Choices$Path, etc. for the others... */
5018 t = getenv("Choices$Write"); /* Ie. where choices should be written */
5019 if (t && *t)
5021 /* Choices file: */
5022 sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
5023 if (ensure_path(tmp))
5025 /* Use for writable file: */
5026 strcpy(choices_file[CHFILE_WRITE], tmp);
5027 /* Form 'mirror' filename: same path but with a fixed leafname */
5028 strcpy(v + 1, "Default");
5029 sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
5030 strcpy(choices_file[CHFILE_MIRROR], tmp);
5033 /* Alarm file (doesn't involve subpath) */
5034 sprintf(tmp, "%s.AngbandEtc.Global.Alarm", t);
5035 if (ensure_path(tmp))
5037 /* Use for read/writable file */
5038 strcpy(alarm_file[CHFILE_WRITE], tmp);
5050 * Return the appropriate (full) pathname.
5052 * For write ops, the read/write file is returned.
5054 * For read ops, either the read/write file, the mirror file,
5055 * or the read only file will be returned as appropriate.
5057 static char *find_choices(int write)
5059 if (write)
5060 return choices_file[CHFILE_WRITE];
5062 if (File_Size(choices_file[CHFILE_WRITE]) > 0)
5063 return choices_file[CHFILE_WRITE];
5065 if (File_Size(choices_file[CHFILE_MIRROR]) > 0)
5066 return choices_file[CHFILE_MIRROR];
5068 return choices_file[CHFILE_READ];
5071 static char *find_choices_mirror(void)
5073 return choices_file[CHFILE_MIRROR];
5076 static char *find_alarmfile(int write)
5078 if (write)
5079 return alarm_file[CHFILE_WRITE];
5081 if (File_Size(alarm_file[CHFILE_WRITE]) > 0)
5082 return alarm_file[CHFILE_WRITE];
5084 return alarm_file[CHFILE_READ];
5088 #ifdef HASNOCORE
5090 * Redefinable "core" action
5092 void (*core_aux)(cptr) = NULL;
5095 * Dump a core file, after printing a warning message
5096 * As with "quit()", try to use the "core_aux()" hook first.
5098 void core(cptr str)
5100 char *crash = NULL;
5102 /* Use the aux function */
5103 if (core_aux) (*core_aux)(str);
5105 /* Dump the warning string */
5106 if (str) plog(str);
5108 /* Attempt to Crash */
5109 (*crash) = (*crash);
5111 /* Be sure we exited */
5112 quit("core() failed");
5114 #endif
5120 int main(int argc, char *argv[])
5122 int i, j;
5123 int start_full = 0;
5124 char *arg_savefile = 0;
5125 char *t;
5126 #ifdef USE_DA
5127 int da_font = 1, da_game = 1;
5128 #endif
5130 atexit(final_acn); /* "I never did care about the little things." */
5132 START_HOURGLASS;
5134 /* Parse arguments */
5135 for (i = 1; i < argc; i++)
5137 if (argv[i][0] == '-')
5139 switch (tolower((unsigned char)argv[i][1]))
5141 case 'm':
5143 /* Minimise Memory */
5144 minimise_memory = 1;
5146 /* Break */
5147 break;
5149 case 'c': /* -c[a][s][f][<n>] */
5150 for (j = 2; argv[i][j]; j++)
5152 int on = isupper((unsigned char)argv[i][j]);
5154 switch (tolower((unsigned char)argv[i][j]))
5156 #ifdef ABBR_FILECACHE
5157 case 'a': abbr_filecache =
5159 break;
5160 case 'f': abbr_tmpfile =
5162 break;
5163 #endif
5164 #ifdef SMART_FILECACHE
5165 case 's': smart_filecache =
5167 break;
5168 #endif
5170 case 'p': flush_scrap =
5171 !on;
5172 break;
5174 default:
5175 if (isdigit((unsigned char)argv[i][j]))
5177 max_file_cache_size =
5178 atoi(argv[i] + j) << 10;
5179 while (isdigit((unsigned char)argv[i][++j]))
5181 if (max_file_cache_size <= 0)
5183 use_filecache = 0;
5185 j--;
5187 else
5189 fprintf(stderr, "Unrecognised option: -c%s",
5190 argv[i] + j);
5191 exit(EXIT_FAILURE);
5195 break;
5196 case 'w': /* -waitrelease */
5197 hack_flush = 1;
5198 break;
5199 case 's': /* -s<savefile> */
5200 if (argv[i][2])
5201 arg_savefile = argv[i] + 2;
5202 break;
5203 case 'f': /* -fullscreen */
5204 start_full = 1;
5205 break;
5206 case 'h': /* -hourglass */
5207 use_glass = 1;
5208 break;
5209 case 't': /* -T<filetype> */
5210 if (argv[i][2])
5211 vfiletype = htoi(argv[i] + 2);
5212 break;
5213 #ifdef USE_DA
5214 case 'd': /* -df, -dg, -dc or -d : disable DAs */
5215 switch (tolower((unsigned char)argv[i][2]))
5217 case 0: /* -d => disable both */
5218 da_font = da_game = 0;
5219 break;
5220 case 'f': /* -df => disable font only */
5221 da_font = 0;
5222 break;
5223 case 'g': /* -dg => disable game only */
5224 da_game = 0;
5225 break;
5227 break;
5228 #endif
5229 case '%': /* -%<debug_opts> */
5231 int v = read_unsigned(argv[i] + 2);
5232 log_g_malloc = v & 1;
5233 show_sound_alloc = v & 2;
5235 break;
5236 default:
5237 fprintf(stderr, "Unrecognised option: %s", argv[i]);
5238 exit(EXIT_FAILURE);
5243 /* 1.27 - new handling of -minimise-memory: */
5244 #ifndef FULLSCREEN_ONLY
5245 if (minimise_memory)
5246 #endif /* FULLSCREEN_ONLY */
5248 start_full = 1;
5249 fs_quit_key_text = "(fullscreen only mode)";
5251 #ifdef USE_DA
5252 init_memory(da_font, da_game); /* Set up dynamic areas, etc. if possible */
5254 /* Install memory allocation hooks */
5255 ralloc_aux = g_malloc;
5256 rnfree_aux = g_free;
5257 #endif
5259 /* Install replacement error reporting routines */
5260 quit_aux = quit_hook;
5261 plog_aux = plog_hook;
5262 core_aux = core_hook;
5264 #ifdef IS_SCTH
5265 /* Hook in the file modification hook in scth*/
5266 check_modification_date_hook = check_modification_date;
5267 #endif
5269 /* Expand the (Angband) resource path */
5270 t = getenv(RISCOS_VARIANT "$Path");
5271 if (!t || !*t) Msgs_ReportFatal(0, "A resources path could not be formed.");
5272 strcpy(resource_path, t);
5274 /* Decide where scrap, choices and alarm files live: */
5275 init_paths();
5277 /* Hack: if no savefile specified, use a default */
5278 if (!arg_savefile)
5280 arg_savefile = malloc(strlen(resource_path) + 32);
5281 if (!arg_savefile)
5283 Msgs_ReportFatal(0, "err.mem");
5285 sprintf(arg_savefile, "%s%s", resource_path, ">Save.Savefile");
5288 /* This crap appears here so that plog() will work properly before
5289 init_acn() is called... */
5290 Resource_Initialise(RISCOS_VARIANT);
5291 Msgs_LoadFile("Messages");
5293 #ifndef FULLSCREEN_ONLY
5294 if (!minimise_memory)
5296 Event_Initialise(RISCOS_VARIANT);
5298 /* Load Templates */
5299 Template_Initialise();
5300 Template_LoadFile("Templates");
5302 #endif /* FULLSCREEN_ONLY */
5304 Screen_CacheModeInfo();
5306 /* Initialise some ZapRedraw stuff */
5307 initialise_palette();
5308 initialise_fonts(); /* Set up the fonts */
5309 #ifndef FULLSCREEN_ONLY
5311 if (!minimise_memory)
5313 /* Initialise some Wimp specific stuff */
5314 init_gamma_window();
5315 init_sound_window();
5316 init_save_window();
5317 init_menus();
5319 ibar_icon = Icon_BarIcon(ICONNAME, iconbar_RIGHT);
5321 /* Global handlers */
5322 Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL);
5323 Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL);
5324 Event_Claim(event_GAINCARET, event_ANY, event_ANY, Hnd_Caret, (void *)1);
5325 Event_Claim(event_LOSECARET, event_ANY, event_ANY, Hnd_Caret, (void *)0);
5326 Event_Claim(event_MENU, event_ANY, event_ANY, Hnd_MenuSel, NULL);
5327 Event_Claim(event_CLICK, window_ICONBAR, ibar_icon, Hnd_IbarClick, NULL);
5328 Event_Claim(event_CLICK, event_ANY, event_ANY, Hnd_Click, NULL);
5329 EventMsg_Claim(message_PALETTECHANGE, event_ANY, Hnd_PaletteChange, NULL);
5330 EventMsg_Claim(message_MODECHANGE, event_ANY, Hnd_ModeChange, NULL);
5331 EventMsg_Claim(message_PREQUIT, event_ANY, Hnd_PreQuit, NULL);
5333 /* Initialise the sound stuff */
5334 initialise_sound();
5336 #endif /* FULLSCREEN_ONLY */
5338 /* Initialise some Angband stuff */
5339 initialise_terms();
5341 /* Set up the r_data buffer */
5342 initialise_r_data();
5344 load_choices();
5345 read_alarm_choices();
5347 init_file_paths(unixify_name(resource_path));
5349 START_HOURGLASS; /* Paranoia */
5351 /* Hack - override the saved options if -F was on the command line */
5352 start_fullscreen |= start_full;
5354 /* hack so that the cursor is yellow if undefined */
5355 if (palette[CURSOR_COLOUR] == palette[0])
5357 angband_color_table[CURSOR_COLOUR][1] = (CURSOR_RGB & 0xff00) >> 8;
5358 angband_color_table[CURSOR_COLOUR][2] = (CURSOR_RGB & 0xff0000) >> 16;
5359 angband_color_table[CURSOR_COLOUR][3] = (CURSOR_RGB & 0xff000000) >> 24;
5362 /* Catch nasty signals */
5363 signals_init();
5365 /* use pref-acn.prf */
5366 ANGBAND_SYS = "acn";
5368 #ifndef FULLSCREEN_ONLY
5369 if (start_fullscreen)
5371 #endif /* FULLSCREEN_ONLY */
5372 Event_Claim(event_NULL, event_ANY, event_ANY, Hnd_null, NULL);
5373 enter_fullscreen_mode();
5374 #ifndef FULLSCREEN_ONLY
5376 else
5378 START_HOURGLASS; /* Paranoia */
5379 Hnd_ModeChange(NULL, NULL); /* Caches the various fonts/palettes */
5380 show_windows();
5381 grab_caret();
5383 Event_Claim(event_NULL, event_ANY, event_ANY, Hnd_null, NULL);
5385 /* Wait for a null poll so that the windows can appear */
5388 Event_Poll();
5390 while (event_lastevent.type != event_NULL);
5392 #endif /* FULLSCREEN_ONLY */
5394 /* Initialise Angband */
5395 START_HOURGLASS; /* Paranoia */
5397 strncpy(savefile, unixify_name(arg_savefile), sizeof(savefile));
5398 savefile[sizeof(savefile) - 1] = '\0';
5400 use_sound = 1;
5401 init_angband();
5402 initialised = 1;
5403 game_in_progress = 1;
5404 pause_line(PAUSE_LINE_PARAM);
5405 flush();
5407 play_game(FALSE);
5409 if (fullscreen_mode) leave_fullscreen_mode();
5411 STOP_HOURGLASS;
5413 quit(NULL);
5415 return 0;
5417 debug("to stop the 'unused' warning :)");
5433 | We use this to keep the mouse in the same place on return from full-screen
5434 | mode.
5436 static wimp_point old_mouse_posn;
5438 /* Nasty hack to remember how big the main window is when not in fullscreen */
5439 static int main_wid, main_hgt;
5442 | Take a copy of the current mode descriptor/number and return either
5443 | a pointer to it (as an int) if it's a new mode, or the mode number.
5444 | NB: A static pointer is used and the descriptor returned is only
5445 | valid until the next call to this function.
5447 | Basically, a replacement for OS_Byte 135 / OS_ScreenMode1, IYSWIM.
5449 static int current_mode(void)
5451 static void *descriptor = NULL;
5452 int mode;
5453 int size;
5454 int i;
5455 int *vals;
5457 if (descriptor)
5459 free(descriptor);
5460 descriptor = NULL;
5463 SWI(1, 3, SWI_OS_Byte, 135, NULL, NULL, &mode);
5464 if (mode < 256)
5466 return mode;
5469 vals = (int *)(mode + 20);
5470 for (i = 0; vals[i] != -1; i += 2)
5473 size = 24 + 8 * i; /* Size of data */
5474 descriptor = malloc(size);
5475 if (!descriptor)
5477 core("Out of memory!");
5479 memcpy(descriptor, (void *)mode, size);
5481 return (int)descriptor;
5487 | Select the best mode we can for full screen.
5488 | Returns 12 for (low-res, ie. mode 12) or 27 for high-res,
5489 | or a pointer to a mode descriptor (as an int).
5491 static int select_fullscreen_mode(void)
5493 static struct
5495 int flags, x, y, l2bpp, hz, term;
5497 desc;
5498 int mode = 0;
5500 desc.flags = 1; /* format 0 */
5501 desc.x = 640;
5502 desc.y = 480; /* 640x480 */
5503 desc.l2bpp = 2; /* 16 colours */
5504 desc.hz = -1; /* best we can get */
5505 desc.term = -1; /* don't fuss about modevars */
5507 SWI(1, 1, SWI_OS_CheckModeValid, &desc, &mode);
5508 if (mode != (int)&desc)
5510 SWI(1, 1, SWI_OS_CheckModeValid, 27, /**/ &mode);
5511 if (mode != 27)
5513 SWI(1, 1, SWI_OS_CheckModeValid, 12, /**/ &mode);
5514 if (mode != 12)
5516 mode = 0;
5521 return mode;
5526 | Change screen mode
5528 static void change_screenmode(int to)
5530 if (SWI(2, 0, SWI_OS_ScreenMode, 0, to))
5532 if (to < 256)
5534 GFX_VDU(22);
5535 GFX_VDU(to);
5537 else
5539 /* Finished with my woman / cos she couldn't help me with ... */
5540 core("Eeek! mode isn't valid, but it /should/ be...");
5547 | Constrain the mouse pointer to a point - this means that the damn
5548 | hourglass won't move around with the mouse :)
5550 static void constrain_pointer(void)
5552 mouse_block ptr;
5553 wimp_rect r;
5554 int ys = screen_eig.y == 1 ? 32 : 64; /* Cope with dbl height glass */
5556 Screen_CacheModeInfo(); /* Make sure we know the screen size */
5557 r.min.x = r.max.x = screen_size.x - 32;
5558 r.min.y = r.max.y = screen_size.y - ys;
5560 /* Retrieve and store old (wimp) pointer position */
5561 Wimp_GetPointerInfo(&ptr);
5562 old_mouse_posn = ptr.pos;
5564 Pointer_RestrictToRect(r);
5566 /* Turn the pointer off also */
5567 SWI(2, 0, SWI_OS_Byte, 106, 0);
5570 static void release_pointer(void)
5572 wimp_rect r;
5574 r.min.x = r.max.x = old_mouse_posn.x;
5575 r.min.y = r.max.y = old_mouse_posn.y;
5577 Pointer_RestrictToRect(r);
5579 Pointer_Unrestrict();
5581 /* Turn the pointer back on also */
5582 SWI(2, 0, SWI_OS_Byte, 106, 1);
5587 | Convert a 1bpp bitmap into a 4bpp bitmap (bit flipped)
5589 static int byte_to_word_flipped(int b)
5591 int w;
5592 if (b & 128)
5594 w = 0xf0000000;
5596 else
5598 w = 0;
5600 if (b & 64)
5602 w |= 0x0f000000;
5604 if (b & 32)
5606 w |= 0x00f00000;
5608 if (b & 16)
5610 w |= 0x000f0000;
5612 if (b & 8)
5614 w |= 0x0000f000;
5616 if (b & 4)
5618 w |= 0x00000f00;
5620 if (b & 2)
5622 w |= 0x000000f0;
5624 if (b & 1)
5626 w |= 0x0000000f;
5628 return w;
5635 | try to load the fallback fullscreen font and convert it to 4bpp
5637 static int cache_zapfontHR(void)
5639 int handle;
5640 unsigned int extent;
5641 char buffer[260];
5642 struct
5644 char id[8];
5645 int w, h, f, l, r1, r2;
5647 zfh;
5648 int *op;
5649 char *ip;
5650 int l, i;
5652 /* Try to open the file */
5653 sprintf(buffer, "%s%s", resource_path, "xtra.FullScreen");
5654 handle = File_Open(buffer, (file_access) 0x4f);
5655 if (!handle)
5657 return 0;
5660 /* Check file's extent */
5661 extent = File_ReadExtent(handle);
5662 if (extent > sizeof(zfh) + 256 * 16)
5664 File_Close(handle);
5665 return 0;
5668 /* Load the header */
5669 if (myFile_ReadBytes(handle, &zfh, sizeof(zfh)))
5671 File_Close(handle);
5672 return 0;
5675 /* Check font size */
5676 if ((zfh.w != 8) || (zfh.h > 16))
5678 File_Close(handle);
5679 return 0;
5682 /* Load the 1bpp data */
5683 if (myFile_ReadBytes(handle, fullscreen_font, extent - sizeof(zfh)))
5685 File_Close(handle);
5686 return 0;
5689 File_Close(handle);
5691 l = zfh.l > 255 ? 255 : zfh.l;
5693 if (zfh.h > 8)
5695 op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 4);
5696 ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
5697 (zfh.f * zfh.h));
5698 while (l-- >= zfh.f)
5700 for (i = 0; i < zfh.h; i++)
5702 *--op = byte_to_word_flipped(*--ip);
5705 fullscreen_height = zfh.h;
5707 else
5709 op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 8);
5710 ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
5711 (zfh.f * zfh.h));
5712 while (l-- >= zfh.f)
5714 for (i = -zfh.h; i < zfh.h; i++)
5716 int t = byte_to_word_flipped(*--ip);
5717 *--op = t;
5718 *--op = t;
5721 fullscreen_height = zfh.h * 2;
5724 fullscreen_topline = TERM_TOPLINE_HR;
5725 fullscreen_topline += ((16 - fullscreen_height) * 13);
5727 return 1;
5734 static int cache_fs_fontHR(void)
5736 ZapFont *src = SYSTEM_FONT;
5737 int c;
5738 int *op;
5739 char *ip;
5741 /* Allocate the storage for the font */
5742 fullscreen_font = f_malloc(256 * 4 * 16);
5743 if (!fullscreen_font)
5745 return 0;
5747 op = (int *)fullscreen_font;
5749 /* Check to see if the main term's font is suitable (ie. 8x16 or 8x8) */
5750 if ((data[0].font->w == 8) && (data[0].font->h <= 16))
5751 src = data[0].font;
5754 | Hack: if we're forced to use the system font, try to load the
5755 | 'fullscreen' font from lib.xtra. If that fails, then I guess we're
5756 | stuck with the system font.
5759 if (src == SYSTEM_FONT)
5760 if (cache_zapfontHR())
5762 return 1;
5765 ip = (char *)(src->bpp_1);
5767 /* Now, create the font */
5768 if (src->h > 8)
5770 int e = src->h * (src->l > 256 ? 256 : src->l);
5771 op += (src->f * src->h);
5772 for (c = src->f * src->h; c < e; c++)
5773 *op++ = byte_to_word_flipped(*ip++);
5774 fullscreen_height = src->h;
5776 else
5778 int e = src->h * (src->l > 256 ? 256 : src->l);
5779 op += (src->f * src->h) * 2;
5780 for (c = src->f * src->h; c < e; c++)
5782 int t = byte_to_word_flipped(*ip++);
5783 *op++ = t;
5784 *op++ = t;
5786 fullscreen_height = src->h * 2;
5789 fullscreen_topline = TERM_TOPLINE_HR;
5790 fullscreen_topline += ((16 - fullscreen_height) * 13);
5792 return 1;
5799 static int cache_fs_fontLR(void)
5801 ZapFont *src = SYSTEM_FONT;
5802 int c, e;
5803 int *op;
5804 char *ip;
5806 /* Allocate the storage for the font */
5807 fullscreen_font = f_malloc(256 * 4 * 8);
5808 if (!fullscreen_font)
5810 return 0;
5812 op = (int *)fullscreen_font;
5814 /* Check to see if the main term's font is suitable (ie. 8x8) */
5815 if ((data[0].font->w == 8) && (data[0].font->h <= 8))
5816 src = data[0].font;
5818 ip = (char *)(src->bpp_1);
5820 /* Now, create the font */
5821 e = src->h * (src->l > 256 ? 256 : src->l);
5822 op += (src->f * src->h);
5823 for (c = src->f * src->h; c < e; c++)
5824 *op++ = byte_to_word_flipped(*ip++);
5826 fullscreen_height = src->h;
5827 fullscreen_topline = TERM_TOPLINE_LR;
5828 fullscreen_topline += ((8 - fullscreen_height) * 13);
5830 return 1;
5835 static void set_keys(int claim)
5837 static int old_c_state;
5838 static int old_f_state[8];
5839 int i;
5841 if (claim)
5843 /* Cursors/copy act as function keys */
5844 /* f0-f9, cursors, generate 0x80-0x8f */
5845 /* sh-f0-f9,cursors, generate 0x90-0x9f */
5846 /* ctrl f0-f9,cursors, generate 0xa0-0xaf */
5847 /* sh-c-f0-f9,cursors, generate 0xb0-0xbf */
5848 /* f10-f12 generate 0xca-0xcc */
5849 /* shift f10-f12 generate 0xda-0xdc */
5850 /* ctrl f10-f12 generate 0xea-0xec */
5851 /* ctrlshift f10-f12 generate 0xfa-0xfc */
5853 SWI(3, 2, SWI_OS_Byte, 4, 2, 0, /**/ NULL, &old_c_state);
5855 for (i = 0; i < 4; i++)
5857 SWI(3, 2, SWI_OS_Byte, 225 + i, 0x80 + (i * 0x10), 0, NULL,
5858 old_f_state + i);
5859 SWI(3, 2, SWI_OS_Byte, 221 + i, 0xc0 + (i * 0x10), 0, NULL,
5860 old_f_state + i + 4);
5863 else
5865 SWI(3, 0, SWI_OS_Byte, 4, old_c_state, 0);
5866 for (i = 0; i < 4; i++)
5868 SWI(3, 0, SWI_OS_Byte, 225 + i, old_f_state[i], 0);
5869 SWI(3, 0, SWI_OS_Byte, 221 + i, old_f_state[i + 4], 0);
5880 | Enter the full screen mode.
5882 | Full screen display uses either mode 27 (if supported) and 8x16 fonts
5883 | (or system font 'twiddled' to double height), or mode 12 (if mode 27
5884 | is unavailable) and the system font (or an 8x8 font).
5888 static void enter_fullscreen_mode(void)
5890 int vduvars[2] =
5891 { 149, -1 };
5892 int i;
5894 /* New in 1.18 - protect against 're-entracy' */
5895 if (fullscreen_font)
5896 return;
5898 /* Choose the mode we want */
5899 fullscreen_mode = select_fullscreen_mode();
5901 if (!fullscreen_mode)
5903 plog("Unable to select a suitable screen mode (27 or 12)");
5904 return;
5907 if (!((fullscreen_mode == 12) ? cache_fs_fontLR() : cache_fs_fontHR()))
5909 plog("Unable to cache a font for full screen mode");
5910 return;
5913 /* Read the current screen mode */
5914 /* SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &old_screenmode ); */
5915 old_screenmode = current_mode();
5917 STOP_HOURGLASS;
5919 /* Change to the chosen screen mode */
5920 change_screenmode(fullscreen_mode);
5922 /* Restrict the pointer */
5923 constrain_pointer();
5925 /* Remove the cursors */
5926 SWI(0, 0, SWI_OS_RemoveCursors);
5928 START_HOURGLASS;
5930 /* Get the base address of screen memory */
5931 SWI(2, 0, SWI_OS_ReadVduVariables, vduvars, vduvars);
5932 fullscreen_base = (int *)(vduvars[0]);
5934 /* Fudge the Term interface */
5935 for (i = 0; i < MAX_TERM_DATA; i++)
5937 term *t = &(data[i].t);
5938 t->wipe_hook = Term_wipe_acnFS;
5939 t->curs_hook = Term_curs_acnFS;
5940 t->text_hook = Term_text_acnFS;
5943 /* Grab the palette */
5944 Term_xtra_acn_reactFS(TRUE);
5946 /* Make sure that the keys work properly */
5947 set_keys(TRUE);
5949 main_wid = data[0].t.wid;
5950 main_hgt = data[0].t.hgt;
5951 term_change_size(&data[0], 80, 24);
5953 /* refresh the term */
5954 /*Term_activate( &(data[0].t) ); */
5955 redraw_areaFS(0, 0, 80, 24);
5956 if (data[0].cursor.visible)
5957 draw_cursor(data[0].cursor.pos.x, data[0].cursor.pos.y);
5959 /* Display a reminder of how to get back... */
5960 /* Hack: disable force_mono */
5961 i = force_mono;
5962 force_mono = 0;
5963 Term_text_acnFS(0, TIME_LINE, strlen(fs_quit_key_text), 8,
5964 fs_quit_key_text);
5965 force_mono = i;
5971 static void leave_fullscreen_mode(void)
5973 int i;
5975 /* New in 1.18 - protect against 're-entracy' */
5976 if (!fullscreen_font) return;
5978 /* Restore the screen mode */
5979 Wimp_SetMode(old_screenmode);
5981 /* Restore the Term interface */
5982 term_change_size(&data[0], main_wid, main_hgt);
5984 for (i = 0; i < MAX_TERM_DATA; i++)
5986 #ifndef FULLSCREEN_ONLY
5987 term *t = &(data[i].t);
5988 t->wipe_hook = Term_wipe_acn;
5989 t->curs_hook = Term_curs_acn;
5990 t->text_hook = Term_text_acn;
5991 #endif
5992 mark_ood(&(data[i]), 0, 0, data[i].t.wid, data[i].t.hgt);
5995 /* Deallocate the font */
5996 f_free(fullscreen_font);
5997 fullscreen_font = 0;
5998 fullscreen_mode = 0;
6000 STOP_HOURGLASS;
6002 /* Restore the pointer */
6003 release_pointer();
6005 START_HOURGLASS;
6007 /* Restore the various soft keys */
6008 set_keys(FALSE);
6010 #ifndef FULLSCREEN_ONLY
6011 /* Refresh the windows - this probably isn't necessary anyway */
6012 if (!minimise_memory) refresh_windows();
6013 #endif /* FULLSCREEN_ONLY */
6020 static void fs_writechars(int x, int y, int n, const char *chars, char attr)
6022 int *scr, *scrb;
6023 int *cdat;
6024 int j;
6025 unsigned int fgm;
6027 if (force_mono)
6029 if (attr != TERM_DARK)
6031 attr = TERM_WHITE;
6034 fgm = (unsigned int)zpalette[(unsigned int) attr];
6036 scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
6037 + x * 4 + 320 * fullscreen_topline);
6039 while (n--)
6041 scr = scrb++;
6042 cdat = (int *)(((int)fullscreen_font)
6043 + (*chars++) * (fullscreen_height << 2));
6044 for (j = 0; j < fullscreen_height; j++)
6046 *scr = *cdat++ & fgm;
6047 scr += 80;
6053 static void fs_writechar(int x, int y, char c, char attr)
6055 int *scrb;
6056 int *cdat;
6057 int j;
6058 unsigned int fgm;
6060 if (force_mono)
6062 if (attr != TERM_DARK)
6064 attr = TERM_WHITE;
6067 fgm = (unsigned int)zpalette[(unsigned int) attr];
6069 scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
6070 + x * 4 + 320 * fullscreen_topline);
6071 cdat = (int *)(((int)fullscreen_font) + (c * (fullscreen_height << 2)));
6072 for (j = 0; j < fullscreen_height; j++)
6074 *scrb = *cdat++ & fgm;
6075 scrb += 80;
6081 static void draw_cursorHR(int x, int y)
6083 ColourTrans_SetGCOL(cursor_rgb, 0, 0);
6084 GFX_Move(x * 16,
6085 959 - y * (fullscreen_height * 2) - fullscreen_topline * 2);
6086 GFX_DrawBy(14, 0);
6087 GFX_DrawBy(0, -(fullscreen_height * 2 - 2));
6088 GFX_DrawBy(-14, 0);
6089 GFX_DrawBy(0, fullscreen_height * 2 - 2);
6092 static void draw_cursorLR(int x, int y)
6094 ColourTrans_SetGCOL(cursor_rgb, 0, 0);
6095 GFX_Move(x * 16,
6096 1023 - y * (fullscreen_height * 4) - fullscreen_topline * 4);
6097 GFX_DrawBy(14, 0);
6098 GFX_DrawBy(0, -(fullscreen_height * 4 - 4));
6099 GFX_DrawBy(-14, 0);
6100 GFX_DrawBy(0, fullscreen_height * 4 - 4);
6107 static void draw_cursor(int x, int y)
6109 if (fullscreen_mode == 12)
6110 draw_cursorLR(x, y);
6111 else
6112 draw_cursorHR(x, y);
6117 static void redraw_areaFS(int x, int y, int w, int h)
6119 int i, j;
6120 for (j = y; j < y + h; j++)
6121 for (i = x; i < x + w; i++)
6122 fs_writechar(i, j, data[0].t.old->c[j][i], data[0].t.old->a[j][i]);
6127 static int wimp_code(int c)
6129 /* shift/ctrl keypad? */
6130 if (c >= '0' && c <= '9')
6132 kbd_modifiers m = Kbd_GetModifiers(FALSE);
6133 if (m.shift)
6135 c |= 0x800;
6137 if (m.ctrl)
6139 c |= 0x400;
6141 return c;
6143 if (c == 9)
6145 return 0x18a;
6146 } /* Tab */
6147 if (c <= 127)
6149 return c;
6150 } /* normal ASCII/ctrl */
6151 if (c >= 0x80 && c <= 0xff)
6153 return c + 0x100;
6154 } /* f0-f9, etc. */
6156 return -1; /* unknown */
6162 static void do_keypress(int code)
6164 static const char hex[] = "0123456789ABCDEF";
6166 if (code == KEYPRESS_QUIT && !minimise_memory)
6168 #ifdef FULLSCREEN_ONLY
6169 Sound_SysBeep();
6170 #else
6171 leave_fullscreen_mode();
6172 #endif
6173 return;
6176 if (code == 27)
6178 if (Kbd_KeyDown(inkey_CTRL))
6180 ack_alarm();
6181 return;
6185 if (code <= 255)
6187 Term_keypress(code);
6189 else
6191 Term_keypress(31);
6192 Term_keypress(hex[(code & 0xf00) >> 8]);
6193 Term_keypress(hex[(code & 0x0f0) >> 4]);
6194 Term_keypress(hex[(code & 0x00f)]);
6195 Term_keypress(13);
6202 static errr Term_xtra_acn_eventFS(int valid)
6204 int bh, bl = -1;
6205 int c;
6206 int w = -1;
6208 STOP_HOURGLASS;
6210 /* Loop if we want validation of the keypress */
6213 bored();
6215 /* Check if there are keypresses in the buffer */
6216 SWI(3, 3, SWI_OS_Byte, 128, 255, 0, NULL, &bl, &bh);
6217 bl = (bl & 0xff) | (bh << 8);
6219 if (bl)
6221 /* Read a keypress */
6222 SWI(0, 1, SWI_OS_ReadC, &c);
6224 /* If valid, process it */
6225 w = wimp_code(c);
6226 if (w >= 0) do_keypress(w);
6228 } while (valid && w == -1);
6230 START_HOURGLASS;
6232 return 0;
6238 * React to changes
6240 static errr Term_xtra_acn_reactFS(int force)
6242 unsigned int i;
6243 int p, r, g, b;
6245 static int old_gamma = -1;
6247 if (gamma != old_gamma)
6249 force = 1;
6250 old_gamma = gamma;
6253 /* Set the screen colours */
6254 for (i = 0; i < 16; i++)
6256 if (COLOUR_CHANGED(i) || force)
6258 r = (int)(255.0 *
6259 pow(angband_color_table[i][1] / 255.0, 1.0 / ((double) gamma / 100)));
6260 g = (int)(255.0 *
6261 pow(angband_color_table[i][2] / 255.0, 1.0 / ((double) gamma / 100)));
6262 b = (int)(255.0 *
6263 pow(angband_color_table[i][3] / 255.0, 1.0 / ((double) gamma / 100)));
6264 GFX_VDU(19);
6265 GFX_VDU(i);
6266 GFX_VDU(16);
6267 GFX_VDU(r);
6268 GFX_VDU(g);
6269 GFX_VDU(b);
6271 palette[i] = (b << 24) | (g << 16) | (r << 8);
6272 p = i;
6273 p |= (p << 4);
6274 p |= (p << 8);
6275 p |= (p << 16);
6276 zpalette[i] = p;
6278 a_palette[i][1] = angband_color_table[i][1];
6279 a_palette[i][2] = angband_color_table[i][2];
6280 a_palette[i][3] = angband_color_table[i][3];
6282 /* Find any higher colour numbers and make them "wrong" */
6283 for (p = 16; p < 256; p++)
6284 if ((zpalette[p] & 0xf) == i)
6285 a_palette[p][1] = angband_color_table[p][1] + 2;
6290 /* Go through the palette updating any changed values */
6291 for (i = 16; i < 256; i++)
6293 if (COLOUR_CHANGED(i) || force)
6295 r = (int)(255.0 *
6296 pow(angband_color_table[i][1] / 255.0, 1.0 / ((double) gamma / 100)));
6297 g = (int)(255.0 *
6298 pow(angband_color_table[i][2] / 255.0, 1.0 / ((double) gamma / 100)));
6299 b = (int)(255.0 *
6300 pow(angband_color_table[i][3] / 255.0, 1.0 / ((double) gamma / 100)));
6301 p = (b << 24) | (g << 16) | (r << 8);
6302 palette[i] = p;
6303 SWI(1, 1, SWI_ColourTrans_ReturnColourNumber, palette[i], &p);
6304 p |= (p << 4);
6305 p |= (p << 8);
6306 p |= (p << 16);
6307 zpalette[i] = p;
6308 a_palette[i][1] = angband_color_table[i][1];
6309 a_palette[i][2] = angband_color_table[i][2];
6310 a_palette[i][3] = angband_color_table[i][3];
6314 cursor_rgb = palette[CURSOR_COLOUR];
6316 return 0;
6320 static errr Term_curs_acnFS(int x, int y)
6322 if (Term == &(data[0].t))
6324 if (data[0].cursor.visible)
6325 redraw_areaFS(data[0].cursor.pos.x, data[0].cursor.pos.y, 1, 1);
6326 data[0].cursor.pos.x = x;
6327 data[0].cursor.pos.y = y;
6328 if (data[0].cursor.visible)
6329 draw_cursor(x, y);
6331 return 0;
6334 static errr Term_xtra_acn_clearFS(void)
6336 char e[80];
6337 int j;
6339 if (Term == &(data[0].t))
6341 for (j = 0; j < 80; j++)
6342 e[j] = ' ';
6344 GFX_Wait();
6346 for (j = 0; j < 24; j++)
6347 fs_writechars(0, j, 80, e, 0);
6350 return 0;
6357 static errr Term_wipe_acnFS(int x, int y, int n)
6359 if (Term == &(data[0].t))
6360 while (n--)
6361 fs_writechar(x++, y, ' ', 0);
6362 return 0;
6365 static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s)
6367 if (Term == &(data[0].t))
6368 fs_writechars(x, y, n, s, (char)a);
6369 return 0;
6374 static void bored()
6376 static unsigned int last;
6377 char ts[80];
6378 time_t ct;
6379 struct tm *lt;
6380 unsigned int l;
6381 int ofm;
6382 static int alarm_flash = 1;
6385 /* Really no need to check the alarm more than once per second. */
6386 if (alarm_type && Time_Monotonic() > alarm_lastcheck + 100)
6388 check_alarm();
6391 l = Time_Monotonic();
6392 if ((l - last) < (alarm_flash ? 25 : 50))
6394 return;
6396 last = l;
6398 time(&ct);
6399 lt = localtime(&ct);
6400 l = strftime(ts, 80, "%c %Z", lt);
6402 /* Hack: disable force_mono around printing the time */
6403 ofm = force_mono;
6404 force_mono = 0;
6406 /* Hack: Is the alarm supposed to be going off? */
6407 if (alarm_disp || alarm_flash)
6409 char blk[60];
6410 int c = 8;
6411 if (!alarm_disp)
6413 alarm_flash = 11;
6415 switch (alarm_flash / 2)
6417 case 4: sprintf(blk, "%-57s", alarm_cancel_text);
6418 break;
6419 case 5: sprintf(blk, "%-57s", fs_quit_key_text);
6420 break;
6421 default:
6422 c = alarm_flash & 1 ? TERM_RED : TERM_WHITE;
6423 sprintf(blk, "%02d:%02d %-51s", alarm_h, alarm_m,
6424 alarm_message);
6426 fs_writechars(0, TIME_LINE, 57, blk, c);
6427 if (++alarm_flash > 11)
6429 alarm_flash = 0;
6433 /* Display time */
6434 fs_writechar(79 - l, TIME_LINE, ' ', 0);
6435 fs_writechars(80 - l, TIME_LINE, l, ts, 8);
6437 force_mono = ofm;
6445 #ifdef USE_DA
6446 /*--------------------------------------------------------------------------*/
6447 /* (Simple) Heap management (using OS_Heap) */
6448 /*--------------------------------------------------------------------------*/
6450 typedef void *heap;
6452 static os_error *Heap_Initialise(heap h, size_t size)
6454 return SWI(4, 0, SWI_OS_Heap, 0, h, 0, size);
6457 static void *Heap_Claim(heap h, size_t size)
6459 void *fred;
6460 os_error *e;
6461 e = SWI(4, 3, SWI_OS_Heap, 2, h, 0, size, NULL, NULL, &fred);
6462 return e ? NULL : fred;
6465 static os_error *Heap_Release(heap h, void *block)
6467 return SWI(3, 0, SWI_OS_Heap, 3, h, block);
6470 static int Heap_ChangeHeapSize(heap h, int resize_by)
6472 int by;
6473 SWI(4, 4, SWI_OS_Heap, 5, h, 0, resize_by, 0, 0, 0, &by);
6474 return by;
6479 /*--------------------------------------------------------------------------*/
6480 /* Stuff below here is for using Dynamic areas (under RO3.5+) */
6481 /*--------------------------------------------------------------------------*/
6483 static int game_area = -1; /* The DA the game is using */
6484 static int font_area = -1; /* The DA the fonts are using */
6486 static void *game_area_base; /* base address of game area */
6487 static void *font_area_base; /* base address of font area */
6489 static int font_area_size; /* size of the fonts' DA */
6490 static int font_heap_size; /* size of the fonts' heap */
6491 static int game_area_size; /* size of the game's DA */
6492 static int game_heap_size; /* size of the game's heap */
6494 #define MAX_F_DA_SIZE (2<<20) /* Max size of font area (2Mb) */
6495 #define MAX_G_DA_SIZE (4<<20) /* Max size of game area (4Mb) */
6496 #define SHRINK_GRAN (4<<10) /* Try to recalaim wastage > this (4Kb) */
6500 | Free dynamic areas when we exit
6502 static void cleanup_memory(void)
6504 if (game_area != -1)
6506 SWI(2, 0, SWI_OS_DynamicArea, 1, game_area);
6507 game_area = -1;
6510 if (font_area != -1)
6512 SWI(2, 0, SWI_OS_DynamicArea, 1, font_area);
6513 font_area = -1;
6521 | Set up the memory allocation stuff.
6522 | We check to see if DAs are possible and if so initialise two:
6523 | one for the game's use (via the rnalloc() hooks) and one for
6524 | our own use (for fonts, etc).
6526 | Each area is created 16Kb in size, with a max size of 2/4Mb.
6528 | If 'daf' is TRUE, an area is created for the fonts.
6529 | If 'dag' is TRUE, an area is created for the game.
6531 static void init_memory(int daf, int dag)
6533 os_error *e = NULL;
6535 if (!daf)
6537 /* Paranoia */
6538 font_area = -1;
6539 font_area_base = 0;
6541 else
6543 e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */
6544 -1, /* Let OS allocate no. */
6545 16 << 10, /* Initial size */
6546 -1, /* Let OS allocate address */
6547 1 << 7, /* Cacheable, bufferable, RW */
6548 MAX_F_DA_SIZE, /* Max size */
6549 0, /* handler */
6550 0, /* handler workspace */
6551 VARIANT " font data", /* Name */
6552 /* */
6553 NULL, /* r0 */
6554 &font_area, /* area number allocated */
6555 NULL, /* r2 */
6556 &font_area_base /* base address of area */
6559 if (e)
6561 game_area = font_area = -1;
6562 game_area_base = font_area_base = 0; /* paranoia */
6563 return;
6565 else
6567 e = SWI(2, 3, SWI_OS_DynamicArea, 2, font_area,
6568 NULL, NULL, &font_area_size);
6569 if (e)
6571 Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6574 e = Heap_Initialise((heap) font_area_base, font_area_size);
6575 if (e)
6577 Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6579 font_heap_size = font_area_size;
6583 /* Make sure DA(s) are removed when we quit */
6584 atexit(cleanup_memory);
6586 if (!dag)
6588 /* Paranoia */
6589 game_area = -1;
6590 game_area_base = 0;
6592 else
6594 e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */
6595 -1, /* Let OS allocate no. */
6596 16 << 10, /* Initial size */
6597 -1, /* Let OS allocate address */
6598 1 << 7, /* Cacheable, bufferable, RW */
6599 MAX_G_DA_SIZE, /* Max size */
6600 0, /* handler */
6601 0, /* handler workspace */
6602 VARIANT " game data", /* Name */
6603 /* */
6604 NULL, /* r0 */
6605 &game_area, /* area number allocated */
6606 NULL, /* r2 */
6607 &game_area_base /* base address of area */
6610 if (e)
6612 game_area = -1;
6613 game_area_base = 0; /* paranoia */
6615 else
6617 e = SWI(2, 3, SWI_OS_DynamicArea, 2, game_area,
6618 NULL, NULL, &game_area_size);
6619 if (e)
6621 Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6624 e = Heap_Initialise((heap) game_area_base, game_area_size);
6625 if (e)
6627 Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
6629 game_heap_size = game_area_size;
6634 static int grow_dynamicarea(int area, int by)
6636 os_error *e;
6637 e = SWI(2, 2, SWI_OS_ChangeDynamicArea, area, by, /**/ NULL, &by);
6638 /* Can't check errors since a 'failed' shrink returns one... */
6639 return by;
6644 | Try to shrink the font-cache heap and area as much as possible.
6646 static void f_shrink_heap(void)
6648 int s;
6649 /* Shrink the heap as far as possible */
6650 font_heap_size -=
6651 Heap_ChangeHeapSize((heap) font_area_base, -MAX_F_DA_SIZE);
6652 /* Shrink the dynamic area if necessary */
6653 s = font_area_size - font_heap_size;
6654 if (s >= SHRINK_GRAN)
6655 font_area_size -= grow_dynamicarea(font_area, -s);
6659 | Allocate a block of memory in the font heap
6661 static void *f_malloc(size_t size)
6663 void *c;
6664 int s;
6665 if (font_area == -1)
6667 return malloc(size);
6669 c = Heap_Claim((heap) font_area_base, size);
6671 if (!c)
6673 /* The Claim failed. Try to grow the area by the size of the block */
6674 s = grow_dynamicarea(font_area, size + 64); /* 64 is overkill */
6675 if (!s)
6677 return NULL;
6679 font_area_size += s;
6680 s = font_area_size - font_heap_size;
6681 font_heap_size += Heap_ChangeHeapSize((heap) font_area_base, s);
6682 c = Heap_Claim((heap) font_area_base, size);
6683 if (c)
6685 f_shrink_heap();
6688 return c;
6693 | Free a block of memory in the font heap
6695 static void f_free(void *blk)
6697 os_error *e;
6698 if (font_area == -1)
6700 free(blk);
6701 return;
6703 e = Heap_Release((heap) font_area_base, blk);
6704 if (e)
6705 Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
6706 f_shrink_heap();
6714 | Allocate a block of memory in the game heap
6716 G_MALLOC_PROT
6718 void *c;
6719 int s;
6721 if (game_area == -1)
6723 return malloc((size_t) size);
6725 c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
6726 if (!c)
6728 /* The Claim failed. Try to grow the area by the size of the block */
6729 s = grow_dynamicarea(game_area, (size_t) size + 64); /* 64 is overkill */
6730 if (!s)
6732 return NULL;
6734 game_area_size += s;
6735 s = game_area_size - game_heap_size;
6736 game_heap_size += Heap_ChangeHeapSize((heap) game_area_base, s);
6737 c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
6740 if (c)
6742 strcpy((char *)c, "MUSH");
6743 c = (void *)(((int)c) + 4);
6746 if (log_g_malloc)
6747 fprintf(stderr, "ralloc(%ld) == %p\n", (long)size, c);
6749 return c;
6754 | Free a block of memory in the game heap
6756 | The 'len' is to be compatible with z-virt.c (we don't need/use it)
6757 | Returns NULL.
6759 G_FREE_PROT
6761 os_error *e;
6762 int s;
6764 if (game_area == -1)
6766 free(blk);
6767 return NULL;
6770 if (log_g_malloc)
6771 fprintf(stderr, "rnfree(%p)\n", blk);
6773 if (strncmp(((char *)blk) - 4, "MUSH", 4))
6774 core("game heap corrupt / bad attempt to free memory");
6776 blk = (void *)(((int)blk) - 4);
6778 e = Heap_Release((heap) game_area_base, blk);
6779 if (e)
6780 Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
6782 /* Shrink the heap as far as possible */
6783 game_heap_size -=
6784 Heap_ChangeHeapSize((heap) game_area_base, -MAX_G_DA_SIZE);
6786 /* Shrink the dynamic area if necessary */
6787 s = game_area_size - game_heap_size;
6788 if (s >= SHRINK_GRAN)
6789 game_area_size -= grow_dynamicarea(game_area, -s);
6791 return NULL;
6795 #endif /* USE_DA */
6799 /*--------------------------------------------------------------------------*/
6802 | New to 1.04: Sound support :)
6804 | We use the PlayIt module (for convenience).
6806 | The Lib/xtra/sound/sound.cfg file is used to map sample names onto
6807 | event names.
6809 | Since textual names are used in the .cfg file, we need to have a lookup
6810 | table to translate them into numbers. At present we use the
6811 | angband_sound_name array defined in variable.c
6813 | Since there can be multiple sounds for each event we need to use a
6814 | list to store them.
6817 /* NB: This will be clipped to 10 under RISC OS 2 */
6818 #define MAX_SAMPNAME_LEN 64
6824 | The list format:
6826 typedef struct samp_node
6828 char sample_name[MAX_SAMPNAME_LEN + 1]; /* Sample name */
6829 struct samp_node *next; /* -> next node */
6831 SampNode;
6833 typedef struct samp_info
6835 int samples; /* # samples for this event */
6836 SampNode *samplist; /* list of sample names */
6838 SampInfo;
6842 | Just need an array of SampInfos
6844 static SampInfo sample[SOUND_MAX];
6847 | This flag will only be set non-zero if the SampInfo array is
6848 | valid.
6850 static int sound_initd = 0;
6853 static void read_sound_config(void)
6855 int i;
6856 char buffer[2048];
6857 FILE *f;
6858 int max_sampname_len = truncate_names()? 10 : MAX_SAMPNAME_LEN;
6859 FILE *dbo = NULL;
6861 if (show_sound_alloc)
6863 sprintf(buffer, "%s%s", resource_path, "sndmap/out");
6864 dbo = fopen(buffer, "w");
6865 if (!dbo)
6867 core("can't create sndmap/out debugging file");
6871 if (!sound_initd)
6873 /* Initialise the sample array */
6874 for (i = 0; i < SOUND_MAX; i++)
6876 sample[i].samples = 0;
6877 sample[i].samplist = NULL;
6879 sound_initd = 1;
6881 else
6883 /* Deallocate the sample lists */
6884 for (i = 0; i < SOUND_MAX; i++)
6886 SampNode *si = sample[i].samplist;
6887 sample[i].samples = 0;
6888 sample[i].samplist = NULL;
6889 while (si)
6891 SampNode *ns = si->next;
6892 free(si);
6893 si = ns;
6899 /* Open the config file */
6900 sprintf(buffer, "%sSound:%s", RISCOS_VARIANT, "sound/cfg");
6901 f = fopen(buffer, "r");
6903 /* No cfg file => no sounds */
6904 if (!f)
6906 if (show_sound_alloc)
6908 fprintf(dbo, "** Can't open cfg file '%s'\n", buffer);
6909 fclose(dbo);
6911 return;
6914 /* Parse the file */
6915 while (fgets(buffer, sizeof(buffer), f))
6917 char *sample_name;
6918 int event_number;
6920 /* Skip comments and lines that begin with whitespace */
6921 if (*buffer == '#' || isspace((unsigned char)*buffer))
6923 continue;
6926 /* Hack: ignore any line beginning '[' (section marker) */
6927 if (*buffer == '[')
6929 continue;
6932 /* Place a NULL after the event name and find the first sample name */
6933 sample_name = buffer;
6934 while (*sample_name && !isspace((unsigned char)*sample_name))
6935 sample_name++;
6937 /* Bad line? */
6938 if (*sample_name == 0)
6940 continue;
6941 } /* just ignore it */
6943 /* Terminate the sample name */
6944 *sample_name++ = 0;
6946 /* Look up the event name to get the event number */
6947 for (event_number = SOUND_MAX - 1; event_number >= 0; event_number--)
6948 if (!strcmp(buffer, angband_sound_name[event_number]))
6949 break;
6951 /* No match -> just ignore the line */
6952 if (event_number < 0)
6954 if (show_sound_alloc)
6955 fprintf(dbo, "* Ignoring unknown event '%s'\n", buffer);
6956 continue;
6959 /* Find the = */
6960 while (*sample_name && *sample_name != '=')
6961 sample_name++;
6963 /* Bad line? */
6964 if (*sample_name == 0)
6966 continue;
6967 } /* just ignore it */
6969 /* Skip the '=' */
6970 sample_name++;
6974 | Now we find all the sample names and add them to the
6975 | appropriate list in the sample mapping array
6978 while (*sample_name)
6980 char *s;
6981 SampNode *sn;
6983 /* Find the start of the next word */
6984 while (isspace((unsigned char)*sample_name) && *sample_name)
6985 sample_name++;
6987 /* End of line? */
6988 if (!*sample_name)
6990 break;
6993 /* Find the end of the sample name */
6994 s = sample_name; /* start of the name */
6995 while (!isspace((unsigned char)*sample_name) && *sample_name)
6996 sample_name++;
6998 /* Hack: shorten sample names that are too long */
6999 if ((sample_name - s) > max_sampname_len)
7000 s[max_sampname_len] = ' ';
7002 /* Allocate a node in the sample list for the event */
7003 if ((sn = malloc(sizeof(SampNode))) == NULL)
7004 core("Out of memory (scanning sound.cfg)");
7006 /* Link the node to the list */
7007 sn->next = sample[event_number].samplist;
7008 sample[event_number].samplist = sn;
7010 /* Imcrement the sample count for that event */
7011 sample[event_number].samples++;
7014 | Copy the sample name into the node, converting it into
7015 | RISC OS style as we go.
7017 for (i = 0; !isspace((unsigned char)s[i]) && s[i]; i++)
7019 if (s[i] == '.')
7020 sn->sample_name[i] = '/';
7021 else if (s[i] == '/')
7022 sn->sample_name[i] = '.';
7023 else
7024 sn->sample_name[i] = s[i];
7027 | The sample name '*' is special and means "no new sound"
7028 | so don't store a filename for these mappings.
7030 if (i == 1 && sn->sample_name[0] == '*')
7032 i = 0;
7034 sn->sample_name[i] = 0;
7038 /* Close the file */
7039 fclose(f);
7041 if (show_sound_alloc)
7043 int i;
7044 SampNode *l;
7046 for (i = 0; i < SOUND_MAX; i++)
7048 fprintf(dbo, "\n\nEvent '%s'", angband_sound_name[i]);
7049 fprintf(dbo, " (%d sounds)\n", sample[i].samples);
7050 for (l = sample[i].samplist; l; l = l->next)
7051 fprintf(dbo, "\t%s\n", l->sample_name);
7053 fclose(dbo);
7061 | Try to make sure that PlayIt is loaded.
7062 | This requires AngSound rel. 4
7064 static void check_playit(void)
7066 if (SWI(2, 0, SWI_OS_Module, 18, "PlayIt"))
7068 int t;
7069 SWI(2, 1, SWI_OS_File, 17, "Angsound:LoadPlayIt", &t);
7070 if (t == 1)
7071 SWI(1, 0, SWI_OS_CLI,
7072 "RMEnsure PlayIt 0.00 Run AngSound:LoadPlayIt");
7078 static void initialise_sound(void)
7080 /* Load the configuration file */
7081 Hourglass_On();
7082 read_sound_config();
7083 check_playit();
7085 /* Set the sound hook */
7086 sound_hook = play_sound;
7088 Hourglass_Off();
7093 static void play_sample(char *leafname)
7095 char buffer[260];
7097 strcpy(buffer, "%playit_stop");
7099 if (!SWI(1, 0, SWI_OS_CLI, buffer))
7101 SWI(1, 0, SWI_PlayIt_Volume, sound_volume);
7102 sprintf(buffer, "%%playit_play %sSound:%s", RISCOS_VARIANT, leafname);
7103 SWI(1, 0, SWI_OS_CLI, buffer);
7106 return;
7111 static void play_sound(int event)
7113 /* Paranoia */
7114 if (!sound_initd || !enable_sound)
7115 return;
7117 /* Paranoia */
7118 if (event < 0 || event >= SOUND_MAX)
7119 return;
7121 /* msg_format("Sound '%s'",angband_sound_name[event]); */
7123 /* Choose a sample */
7124 if (sample[event].samples)
7126 int s = rand() % sample[event].samples;
7127 SampNode *sn = sample[event].samplist;
7128 while (s--)
7130 sn = sn->next;
7131 if (!sn)
7133 plog("Adny botched the sound config - please send him a copy of your sound/cfg file.");
7136 if (*(sn->sample_name))
7137 play_sample(sn->sample_name);
7144 /*--------------------------------------------------------------------------*/
7147 | This stuff is for the Term_user hook
7149 #ifdef ZANGBAND_TERM_PACKAGE
7150 #define PUT_FSTR put_fstr
7151 #define COL(colour) CLR_##colour
7152 #else
7153 #define PUT_FSTR display_line
7154 #define COL(colour) TERM_##colour
7156 static void display_line(int x, int y, int c, const char *fmt, ...)
7158 va_list ap;
7159 char buffer[260];
7161 va_start(ap, fmt);
7162 vstrnfmt(buffer, sizeof(buffer), fmt, ap);
7163 Term_putstr(x, y, -1, c, buffer);
7164 va_end(ap);
7167 #endif
7171 | Let the user change the alarm message
7173 static void do_alarm_message_input(int y)
7175 int k;
7176 int inspos = strlen(alarm_message);
7177 char old_message[52];
7179 strcpy(old_message, alarm_message);
7183 PUT_FSTR(26, y, COL(YELLOW), "%-51s", alarm_message);
7184 Term_gotoxy(26 + inspos, y);
7185 k = inkey();
7186 switch (k)
7188 case 21: /* ^U */
7189 *alarm_message = 0;
7190 inspos = 0;
7191 break;
7192 case 128: case 8: /* delete */
7193 if (inspos > 0)
7195 alarm_message[--inspos] = 0;
7197 break;
7198 case 27: /* escape */
7199 strcpy(alarm_message, old_message);
7200 k = 13;
7201 break;
7202 default:
7203 if (k > 31 && k < 127 && inspos < 50)
7205 alarm_message[inspos++] = k;
7206 alarm_message[inspos] = 0;
7210 while (k != 13);
7212 PUT_FSTR(26, y, COL(WHITE), "%-51s", alarm_message);
7216 #define tum_col(X) ((X) ? COL(L_BLUE) : COL(WHITE) )
7217 #define tum_onoff(X) ((X) ? "On " : "Off")
7219 static errr Term_user_acn(int n)
7221 bool cursor_state;
7222 int optn = 0;
7223 int k, adj;
7224 int redraw_mung = 0;
7225 int max_opt = 11;
7227 /* Will be true if the alarm choices need to be (re)saved */
7228 int alarm_modified = 0;
7231 | Hack: let the desktop front end know that
7232 | the user menu is active...
7234 user_menu_active = TRUE;
7238 | Hack: alarm type 1 /looks/ the same as type 3 but doesn't get
7239 | cancelled as a type 3 would. This allows alarms to go off and
7240 | be cancelled without affecting the alarm type whilst it's being
7241 | set up here.
7243 if (alarm_type == 3)
7245 alarm_type = 1;
7249 | Store the screen
7251 Term_activate(&(data[0].t));
7252 Term_save();
7253 Term_get_cursor(&cursor_state);
7254 Term_set_cursor(TRUE);
7258 redraw_mung = 0;
7259 Term_clear();
7260 PUT_FSTR(2, 1, COL(YELLOW), "%s %s", VARIANT, VERSION);
7261 PUT_FSTR(2, 2, COL(SLATE), "Front-end %s", PORTVERSION);
7262 PUT_FSTR(2, 4, COL(WHITE), "Use cursor up/down to select an option then cursor left/right to alter it.");
7263 PUT_FSTR(2, 5, COL(WHITE), "Hit 'S' to save these settings (alarm settings are saved automatically).");
7264 PUT_FSTR(2, 6, COL(WHITE), "Hit ESC to return to the game.");
7266 for (k = 0; k < 32; k++) Term_putch(31 + k + (k / 2), 8, k / 2, '#');
7270 PUT_FSTR(2, 8, tum_col(optn == 0),
7271 " Gamma correction : %i.%02i", gamma / 100, gamma % 100);
7272 PUT_FSTR(2, 9, tum_col(optn == 1),
7273 " Force monochrome : %s", tum_onoff(force_mono));
7274 PUT_FSTR(2, 10, tum_col(optn == 2),
7275 " Sound effects : %s", tum_onoff(enable_sound));
7276 PUT_FSTR(2, 11, tum_col(optn == 3),
7277 " Sound effect volume : ");
7278 PUT_FSTR(26, 11,
7279 sound_volume > 127 ? COL(RED) : tum_col(optn == 3),
7280 "%-3d", sound_volume);
7281 PUT_FSTR(30, 11, tum_col(optn == 3), "(127 = full volume)");
7282 PUT_FSTR(2, 12, tum_col(optn == 4),
7283 " Start fullscreen : %s",
7284 tum_onoff(start_fullscreen));
7285 PUT_FSTR(30, 12, tum_col(optn == 4),
7286 "(also selects fullscreen/desktop now)");
7287 PUT_FSTR(2, 13, tum_col(optn == 5),
7288 " Use hourglass : %s", tum_onoff(use_glass));
7289 PUT_FSTR(2, 14, tum_col(optn == 6),
7290 "'Hard' input flushing : %s", tum_onoff(hack_flush));
7292 PUT_FSTR(2, 16, tum_col(optn == 7),
7293 " Alarm type : %-20s",
7294 alarm_types[alarm_type]);
7295 PUT_FSTR(2, 17, COL(WHITE),
7296 " Time : ");
7297 PUT_FSTR(26, 17, tum_col(optn == 8), "%02d", alarm_h);
7298 PUT_FSTR(28, 17, COL(WHITE), ":");
7299 PUT_FSTR(29, 17, tum_col(optn == 9), "%02d", alarm_m);
7300 PUT_FSTR(2, 18, tum_col(optn == 10),
7301 " Message : %-51s", alarm_message);
7302 PUT_FSTR(2, 19, tum_col(optn == 11),
7303 " Beep : %s", tum_onoff(alarm_beep));
7305 #ifdef FE_DEBUG_INFO
7306 PUT_FSTR(2, 23, tum_col(optn == 23), "Show debug info");
7307 max_opt = 12;
7308 #endif
7310 switch (optn)
7312 case 12: Term_gotoxy(26, 23);
7313 break;
7314 case 11: Term_gotoxy(26, 19);
7315 break;
7316 case 10: Term_gotoxy(26, 18);
7317 break;
7318 case 9: Term_gotoxy(29, 17);
7319 break;
7320 case 8: Term_gotoxy(26, 17);
7321 break;
7322 case 7: Term_gotoxy(26, 16);
7323 break;
7324 default: Term_gotoxy(26, optn + 8);
7327 k = inkey();
7328 adj = (k == '4' || k == 'h') ? -1 : (k == '6' || k == 'l') ? 1 : 0;
7330 switch (k)
7332 case 18: /* Hack: force the screen to update */
7333 redraw_mung = 1;
7334 k = 27;
7335 break;
7336 case 's': case 'S':
7337 save_choices();
7338 PUT_FSTR(2, 23, COL(YELLOW), "Options saved. ");
7339 Term_fresh();
7340 Term_xtra(TERM_XTRA_DELAY, 750);
7341 Term_erase(2, 23, 60);
7342 break;
7343 case '8': case 'k':
7344 if (--optn < 0)
7346 optn = max_opt;
7348 break;
7349 case '2': case 'j':
7350 if (++optn > max_opt)
7352 optn = 0;
7354 break;
7355 case 13: case 32: case 't': /* Allow return, space and t to toggle some options */
7356 case '4': case 'h':
7357 case '6': case 'l':
7359 switch (optn)
7361 case 0: /* Gamma correction */
7362 gamma += adj * 5;
7363 if (gamma > 900)
7365 gamma = 900;
7367 if (gamma < 5)
7369 gamma = 5;
7371 Term_xtra(TERM_XTRA_REACT, 0);
7372 #ifndef FULLSCREEN_ONLY
7373 set_gamma_window_state();
7374 #endif /* FULLSCREEN_ONLY */
7375 /* flush(); */
7376 Term_fresh();
7377 break;
7378 case 1: /* Force monochrome */
7379 force_mono = !force_mono;
7380 if (fullscreen_font)
7381 redraw_areaFS(0, 0, 80, 24);
7382 else
7383 Term_xtra(TERM_XTRA_REACT, 0);
7384 /* flush(); */
7385 Term_fresh();
7386 break;
7387 case 2: /* Sound enable / disable */
7388 enable_sound = !enable_sound;
7389 #ifndef FULLSCREEN_ONLY
7390 set_sound_window_state();
7391 #endif /* FULLSCREEN_ONLY */
7392 if (enable_sound)
7394 initialise_sound();
7396 break;
7397 case 3: /* Sound volume */
7398 sound_volume += adj;
7399 if (sound_volume < SOUND_VOL_MIN)
7400 sound_volume = SOUND_VOL_MIN;
7401 if (sound_volume > SOUND_VOL_MAX)
7402 sound_volume = SOUND_VOL_MAX;
7403 #ifndef FULLSCREEN_ONLY
7404 set_sound_window_state();
7405 #endif /* FULLSCREEN_ONLY */
7406 break;
7407 case 4: /* Start fullscreen */
7408 start_fullscreen = !start_fullscreen;
7409 if (start_fullscreen)
7410 enter_fullscreen_mode();
7411 else if (!minimise_memory)
7412 leave_fullscreen_mode();
7413 break;
7414 case 5: /* Start fullscreen */
7415 use_glass = !use_glass;
7416 if (!use_glass)
7418 if (glass_on)
7420 Hourglass_Off();
7422 glass_on = 0;
7424 break;
7425 case 6: /* hack flush */
7426 hack_flush = !hack_flush;
7427 break;
7428 case 7: /* Alarm on/off */
7429 alarm_type += adj;
7430 if (adj)
7432 alarm_modified = 1;
7434 if (alarm_type > 2)
7436 alarm_type = 0;
7438 if (alarm_type < 0)
7440 alarm_type = 2;
7442 if (!alarm_type && alarm_disp)
7444 ack_alarm();
7445 } /* XXXXX Cancel an already active alarm? */
7446 break;
7447 case 8: /* Alarm hours */
7448 alarm_h += adj;
7449 if (adj)
7451 alarm_modified = 1;
7453 if (alarm_h < 0)
7455 alarm_h += 24;
7457 if (alarm_h > 23)
7459 alarm_h -= 24;
7461 if (alarm_disp)
7463 ack_alarm();
7465 break;
7466 case 9: /* Alarm minutes */
7467 alarm_m += adj;
7468 if (adj)
7470 alarm_modified = 1;
7472 if (alarm_m < 0)
7474 alarm_m += 60;
7476 if (alarm_m > 59)
7478 alarm_m -= 60;
7480 if (alarm_disp)
7482 ack_alarm();
7484 break;
7485 case 10:
7486 alarm_modified = 1;
7487 do_alarm_message_input(18);
7488 break;
7489 case 11:
7490 alarm_modified = 1;
7491 alarm_beep = !alarm_beep;
7492 break;
7493 case 12:
7494 show_debug_info();
7495 redraw_mung = 1;
7496 k = 27;
7497 break;
7502 while (k != 27);
7504 while (redraw_mung);
7506 /* Rehack the alarm type: */
7507 if (alarm_type == 1)
7509 alarm_type = 3;
7512 if (alarm_modified)
7514 write_alarm_choices();
7517 Term_set_cursor(cursor_state);
7519 /* Restore the screen */
7520 Term_load();
7523 | Hack: tell the desktop front end that we're done.
7525 user_menu_active = FALSE;
7527 return 0;
7533 /*--------------------------------------------------------------------------*/
7535 #ifdef USE_FILECACHE
7538 | 'Random' File-cacheing for *band.
7540 | Rewritten since as of Zang 225 the mechanism for handling
7541 | these files has changed dramatically and the old system
7542 | is no longer viable.
7544 | These new functions basically provide an alternative to the
7545 | normal my_fopen() (or fopen()) and my_fgets() functions.
7547 | To use the file caching it is therefore necessary to alter
7548 | files.c to call cached_fopen(), cached_fclose() and cached_fgets()
7549 | rather than the normal functions.
7551 | Note that these funtions will only work for files that are intended
7552 | to be read as a series of \n terminated lines of ASCII text using my_fgets().
7557 | Hack: use the game's dynamic area if possible:
7559 #define fc_malloc(X) (g_malloc(X))
7560 #define fc_free(X) (g_free(X,0))
7562 #ifndef ABBR_FILECACHE
7564 | Make these to do nothing. They'll never
7565 | be called anyway. Having them present makes
7566 | for neater code later on (ie. we use a variable
7567 | rather than the pre-processor to decide whether
7568 | to do compression).
7570 static int compress_string(char *os, char *s)
7572 core("main-acn internal logic error 001");
7573 return 0;
7575 static int decompress_string(char *d, char *s, int max_len)
7577 core("main-acn internal logic error 002");
7578 return 0;
7580 static int compressed_length(char *s)
7582 core("main-acn internal logic error 003");
7583 return 0;
7586 #else
7589 | When caching files we try to use some abbreviations.
7590 | We use both whole words and pairs of letters.
7591 | NB: For this to work, the file must contain only
7592 | 7 bit characters.
7594 static char *abbrv_w[] =
7596 /* These words all begin with a space */
7597 " of ", " the ", " you ", " to ", " a ", " says", " is ", " that ", " and ",
7598 " your ", " are ", " it ", " be ", " for ", " me", " will ", " in ",
7599 " not ", " this ", " have ", " can ", " on ", " my ", " with ", " say ",
7600 " all", " by ", " get ", " but ", " just ", " die", " as ", " time ",
7601 " if ",
7602 " like ",
7603 /* These words do not */
7604 "I ", "The ", "You ", "They ", "It ", "don", 0
7607 /* Number of words */
7608 #define FC_ABBRV_NUMWORDS 41
7610 /* Number of them that don't start with a space */
7611 #define FC_ABBRV_NONSPC 6
7614 | NB: No letter pair may start with \0.
7616 static char abbrv_lp[] =
7617 "e ttht s heiner aoure'\0, anonf sd y r ongofator.\0"
7618 "n arllstha wes m ieaisen bl yndtoo yometele d f hve"
7619 "ayuralitneelN: chig ilroassaseliti lraa otedbede 'ri" "..u nntno!'ee\0\0";
7623 | Compress the given string using the abbreviation tables above.
7624 | Returns compressed length *including* terminator (it may
7625 | be part of an abbreviation, you see...)
7626 | Note that we can compress the string in-place, ie. 'os' may be
7627 | the same as 's'.
7629 static int compress_string(char *os, char *s)
7631 char *o, *f, *d;
7632 int i;
7634 o = os;
7636 while (*s)
7638 int fw, lw;
7639 if (*s == ' ')
7641 fw = 0;
7642 lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7644 else
7646 fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7647 lw = FC_ABBRV_NUMWORDS;
7649 for (i = fw; i < lw; i++)
7651 d = abbrv_w[i]; /* Word to check against */
7652 for (f = s; *f && *f == *d; f++, d++)
7654 if (*d == 0) /* Match? */
7656 s = *f ? f : f - 1; /* Update string pointer */
7657 *o++ = 128 + i; /* store code */
7658 break; /* Quit looking for words */
7662 /* Do we need to check the letter pairs? */
7663 if (i == lw)
7665 for (i = 0; abbrv_lp[i]; i += 2)
7667 if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
7669 *o++ = 128 + FC_ABBRV_NUMWORDS + i / 2;
7670 /* NB: If the next character is the terminator then we're done. */
7671 if (!s[1])
7673 return (o - os);
7675 s += 2; /* Quit looking for letters */
7676 break;
7679 /* NB: This next check is only safe because no letter pair starts with a NULL */
7680 if (!abbrv_lp[i])
7681 *o++ = *s++;
7685 /* Don't forget that terminator! */
7686 *o++ = 0;
7688 return o - os;
7692 | As compress_string (above), but stores nothing and
7693 | only returns the length of the compressed string.
7695 static int compressed_length(char *s)
7697 char *f, *d;
7698 int i, l;
7700 l = 0;
7701 while (*s)
7703 int fw, lw;
7704 if (*s == ' ')
7706 fw = 0;
7707 lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7709 else
7711 fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
7712 lw = FC_ABBRV_NUMWORDS;
7714 for (i = fw; i < lw; i++)
7716 d = abbrv_w[i];
7717 for (f = s; *f && *f == *d; f++, d++)
7719 if (*d == 0) /* Match? */
7721 s = *f ? f : f - 1; /* Update string pointer */
7722 l++; /* increment output length */
7723 break; /* Quit looking for words */
7727 /* Do we need to check the letter pairs? */
7728 if (i == lw)
7730 for (i = 0; abbrv_lp[i]; i += 2)
7732 if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
7734 l++; /* increment output length */
7735 /* NB: If the next character is the terminator then we're done. */
7736 if (!s[1])
7738 return l;
7740 s += 2; /* Quit looking for letters */
7741 break;
7744 /* NB: This next check is only safe because no letter pair starts with a NULL */
7745 if (!abbrv_lp[i])
7747 l++;
7748 s++;
7752 /* Don't forget that terminator! */
7753 return l + 1;
7759 | Decompress the given string 's' into the buffer at 'd'.
7760 | At most, max_len characters (incl. \0 terminator) will be
7761 | written into d.
7762 | Returns the length of 's'.
7764 static int decompress_string(char *d, char *s, int max_len)
7766 char *os = s;
7768 while (max_len > 1)
7770 int nc = *s++; /* Get next character */
7772 if (nc < 128) /* Is it a plain character? */
7774 if (0 == (*d++ = nc))
7776 break;
7778 max_len--;
7780 else /* Abbreviation to expand. */
7782 if (nc >= FC_ABBRV_NUMWORDS + 128) /* Letter pair? */
7784 *d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2];
7785 if (0 ==
7786 (*d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2 + 1]))
7787 break;
7788 max_len -= 2;
7790 else /* It's a word */
7792 char *ws = abbrv_w[nc - 128];
7793 while (*ws && max_len > 1)
7795 *d++ = *ws++;
7796 max_len--;
7802 /* Skip over the rest of the abbreviated string if we ran out of space */
7803 if (max_len <= 1) /* Out of space? */
7805 int nc;
7806 *d = 0; /* Terminate */
7809 nc = *s++; /* Next char */
7810 if (nc >= 128 + FC_ABBRV_NUMWORDS) /* Ignore words */
7811 nc = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128) * 2) + 1]; /* Only check 2nd letter of pair */
7813 while (nc);
7815 return s - os; /* Length of abbreviated string */
7818 #endif /* ABBR_FILECACHE */
7821 /* Each entry in the cache looks like this: */
7822 typedef struct fce_
7824 char *name; /* canonical pathname of file */
7825 char *text; /* text (be it compressed or not) */
7826 char *eof; /* byte beyond the last byte of text */
7827 int used; /* access counter when the file was last used */
7828 int compressed; /* compression method, (ie. 0 for none or 1 for abbreviations) */
7830 FileCacheEntry;
7833 | The handles we chuck around are pointers to one of these structs.
7834 | Note that since we actually return (and take) |FILE*|s we just
7835 | compare the value of a |FILE*| with the limits of the array of
7836 | |CachedFileHandle|s to decide whether its 'ours' or a genuine
7837 | (ie. stdio) file handle. This /is/ pretty lame, I know...
7839 typedef struct cfh_
7841 char *ptr; /* sequential file pointer, as it were */
7842 FileCacheEntry *fce; /* ->the file-cache entry data */
7844 CachedFileHandle;
7846 #define MAX_OPEN_CACHED_FILES 16 /* We allow up to 16 of these files open at once */
7847 #define MAX_CACHE_ENTRIES 64 /* We allow up to 64 cache entries */
7849 static FileCacheEntry *file_cache; /* to be used as file_cache[MAX_CACHE_ENTRIES] */
7850 static CachedFileHandle *cached_file_handle; /* to be used as cached_file_handle[MAX_OPEN_CACHED_FILES] */
7851 static int file_cache_initd = 0; /* Is the cache initialised? */
7852 static int file_cache_size = 0; /* Total size of the cached files */
7853 static int fc_access_counter = 1; /* incremented on each cache access */
7855 | Pre-calculate max. possible value of a FILE* (ie. address in memory)
7856 | that could be a valid |CachedFileHandle*|.
7858 static FILE *max_cfh_addr; /* == (FILE*) (&(cached_file_handle[MAX_OPEN_CACHED_FILES-1])) */
7861 | Initialise the file cache
7863 static void init_file_cache(void)
7865 int i;
7867 /* Allocate storage */
7868 file_cache = fc_malloc(MAX_CACHE_ENTRIES * sizeof(FileCacheEntry));
7869 cached_file_handle =
7870 fc_malloc(MAX_OPEN_CACHED_FILES * sizeof(CachedFileHandle));
7872 if (!file_cache || !cached_file_handle)
7874 /* Disable file-caching */
7875 if (file_cache)
7877 fc_free(file_cache);
7879 if (cached_file_handle)
7881 fc_free(cached_file_handle);
7883 use_filecache = 0;
7885 else
7887 /* Initialise the cache */
7888 for (i = 0; i < MAX_CACHE_ENTRIES; i++)
7889 file_cache[i].name = NULL;
7890 for (i = 0; i < MAX_OPEN_CACHED_FILES; i++)
7891 cached_file_handle[i].fce = NULL;
7892 fc_access_counter = 1;
7893 file_cache_size = 0;
7894 max_cfh_addr =
7895 (FILE *)(&(cached_file_handle[MAX_OPEN_CACHED_FILES - 1]));
7897 file_cache_initd = 1;
7903 | Cache the specified file, returning either the cache entry
7904 | that it has been cached at, or NULL for failure.
7906 | Note that for the abbreviated file cache a temporary file
7907 | is used to allow the compression to be applied just once.
7908 | (otherwise it has to be done twice - once to determine the
7909 | eventual compressed size and once to actually store and compress
7910 | it).
7912 static FileCacheEntry *cache_file(char *name)
7914 int i, size = 0;
7915 FILE *fp;
7916 char buffer[1024];
7917 char *d;
7919 FILE *tf = NULL; /* Used if abbr_filecache and abbr_tmpfile are set */
7920 char cfn[1024]; /* Used if abbr_filecache and abbr_tmpfile are set */
7922 /* Find the first free slot in the cache */
7923 for (i = 0; i < MAX_CACHE_ENTRIES; i++)
7924 if (!file_cache[i].name)
7925 break;
7927 /* No more entries? */
7928 if (i >= MAX_CACHE_ENTRIES)
7930 return NULL;
7933 /* Set up the info on the file */
7934 if ((file_cache[i].name = string_make(name)) == NULL)
7936 return NULL;
7939 /* Open the file */
7940 fp = my_fopen(name, "r");
7941 if (!fp)
7943 fc_free(file_cache[i].name);
7944 file_cache[i].name = 0;
7945 return NULL;
7948 /* Open/create tempfile if need be: */
7949 if (abbr_filecache && abbr_tmpfile)
7951 /* Hack: Form the pathname of the cached compressed file (in canonical form) */
7952 sprintf(cfn, "%s%s", scrap_path,
7953 riscosify_name(name + strlen(resource_path)));
7954 /* Ensure that that particular directory exists... */
7955 ensure_path(cfn);
7956 /* Check whether cache file is out of date */
7957 if (file_is_newer(riscosify_name(name), cfn))
7959 tf = fopen(cfn, "wb");
7960 size = 0;
7962 else
7964 tf = fopen(cfn, "rb");
7965 if (tf)
7967 size = File_Size(cfn);
7972 /* If we don't have the cached file (but want it), compress the source text to it */
7973 if (tf)
7975 if (!size)
7977 int k;
7978 while (!my_fgets(fp, buffer, sizeof(buffer)))
7980 if (smart_filecache && (!*buffer || *buffer == '#'))
7981 continue;
7982 k = compress_string(buffer, buffer);
7983 if (fwrite(buffer, 1, k, tf) != k)
7985 fclose(tf);
7986 remove(cfn);
7987 core("error writing tempfile");
7989 size += k;
7991 fclose(tf);
7992 tf = fopen(cfn, "rb");
7995 else
7997 /* Count the number of bytes */
7998 while (!my_fgets(fp, buffer, sizeof(buffer)))
8000 if (smart_filecache && (!*buffer || *buffer == '#'))
8001 continue;
8002 if (abbr_filecache)
8003 size += compressed_length(buffer);
8004 else
8005 size += strlen(buffer) + 1;
8009 /* Close the (source) file */
8010 my_fclose(fp);
8012 /* Allocate enough storage for the text */
8013 file_cache[i].text = fc_malloc(size + 1L);
8014 if (!file_cache[i].text)
8016 fc_free(file_cache[i].name);
8017 file_cache[i].name = 0;
8018 if (tf)
8020 fclose(tf);
8022 return NULL;
8025 /* Do we have a tempfile to load? */
8026 if (tf)
8028 if (fread(file_cache[i].text, 1, size, tf) != size)
8029 core("error reading tempfile");
8030 fclose(tf);
8032 else
8034 /* Re-open the file... */
8035 fp = my_fopen(name, "r");
8036 if (!fp)
8038 fc_free(file_cache[i].name);
8039 fc_free(file_cache[i].text);
8040 file_cache[i].name = 0;
8041 return NULL;
8044 /* And read it into the buffer... */
8045 d = file_cache[i].text;
8046 while (!my_fgets(fp, buffer, sizeof(buffer)))
8048 if (smart_filecache && (!*buffer || *buffer == '#'))
8049 continue;
8050 if (abbr_filecache)
8051 d += compress_string(d, buffer);
8052 else
8054 strcpy(d, buffer);
8055 d += strlen(buffer) + 1;
8059 if ((d - file_cache[i].text) != size)
8061 debug("Calculated size is %d, pointer offset is %d", size,
8062 (d - file_cache[i].text));
8063 core("Cached file is larger than calculated!");
8066 /* Close the file */
8067 my_fclose(fp);
8070 /* Set up the 'last accessed' value, etc. */
8071 file_cache[i].used = fc_access_counter++;
8072 file_cache[i].eof = file_cache[i].text + size;
8073 file_cache[i].compressed = abbr_filecache;
8074 file_cache_size += size;
8076 /* Return success */
8077 return &(file_cache[i]);
8082 | Discard a file from the cache
8084 static void discard_cached_file(int i)
8086 if (!file_cache[i].name)
8088 return;
8089 } /* invalid request */
8090 fc_free(file_cache[i].text);
8091 fc_free(file_cache[i].name);
8092 file_cache_size -= (file_cache[i].eof) - (file_cache[i].text);
8093 file_cache[i].name = 0;
8098 | Attempt to flush as much of the cache as required
8099 | to bring it within the size limit.
8100 | If protect != 0 then that entry in the cache won't be flushed.
8102 static void flush_file_cache(FileCacheEntry * protect)
8104 int i, j, done;
8105 int oldest_u, oldest_e;
8106 FileCacheEntry *fce;
8107 int needed = (4 << 10); /* Hack: try to free at least 4K */
8109 done = (file_cache_size + needed) <= max_file_cache_size;
8111 while (!done)
8113 oldest_u = fc_access_counter;
8114 oldest_e = -1;
8116 fce = file_cache;
8117 /* Find oldest entry that isn't in use */
8118 for (i = 0; i < MAX_CACHE_ENTRIES; fce++, i++)
8120 if (fce == protect)
8122 continue;
8123 } /* Hack ;) */
8124 if (fce->name) /* Is this cache slot full? */
8126 for (j = 0; j < MAX_OPEN_CACHED_FILES; j++)
8127 if (cached_file_handle[j].fce == fce)
8128 break;
8129 if (j < MAX_OPEN_CACHED_FILES)
8130 continue; /* Cached file is still open */
8131 if (fce->used < oldest_u)
8133 oldest_e = i;
8134 oldest_u = file_cache[i].used;
8139 if (oldest_e < 0)
8140 done = 1; /* We can flush nothing more */
8141 else
8143 discard_cached_file(oldest_e);
8144 done = (file_cache_size + needed) <= max_file_cache_size;
8151 | Locate the specified file within the cache.
8152 | Returns NULL if the file is not cached
8154 static FileCacheEntry *find_cached_file(char *name)
8156 int i;
8157 FileCacheEntry *fce = file_cache;
8159 for (i = 0; i < MAX_CACHE_ENTRIES; i++, fce++)
8161 if (fce->name)
8162 if (streq(fce->name, name))
8163 return fce;
8165 return NULL;
8170 /*--------------------------------------------------------------------------*/
8171 /* Externally visible file cache stuff */
8172 /*--------------------------------------------------------------------------*/
8175 | Open a file...
8176 | Returns the file cache handle of the file, or NULL for failure.
8177 | Note that if mode is anything other than "r" the call defers to
8178 | my_fopen().
8180 | NB: The returned handle is almost certainly *NOT* a |FILE*|
8181 | (although it may be if the cache cannot accomodate the file).
8183 | Therefore, you *MUST* ensure that any file opened with cached_fopen()
8184 | is only ever accessed via cached_fgets() and cached_fclose().
8186 | Failure to do so will result in, ahem, unpleasantness. Extreme
8187 | unpleasantness.
8189 FILE *cached_fopen(char *name, char *mode)
8191 FileCacheEntry *fcs = NULL;
8192 int fch;
8194 if (strcmp(mode, "r") || !use_filecache)
8195 return my_fopen(name, mode);
8197 if (!file_cache_initd)
8199 init_file_cache();
8202 if (max_file_cache_size >= 0)
8204 /* Find a free cache entry */
8205 for (fch = 0; fch < MAX_OPEN_CACHED_FILES; fch++)
8206 if (!cached_file_handle[fch].fce)
8207 break;
8209 /* Out of handles? */
8210 if (fch >= MAX_OPEN_CACHED_FILES)
8211 return my_fopen(name, mode);
8213 /* Is the file already cached? */
8214 fcs = find_cached_file(name);
8215 if (!fcs)
8217 /* File wasn't cached, so cache it */
8218 flush_file_cache(NULL); /* Clean stuff out of the cache if need be */
8219 fcs = cache_file(name); /* Cache the new file */
8220 flush_file_cache(fcs); /* Flush, but keep the latest file */
8224 /* Did we fail to cache the file? */
8225 if (!fcs)
8227 return my_fopen(name, mode);
8230 /* File was cached OK */
8231 cached_file_handle[fch].ptr = fcs->text; /* Init sequential pointer */
8232 cached_file_handle[fch].fce = fcs; /* Cache block pointer */
8233 fcs->used = fc_access_counter++; /* Opening the file counts as an access */
8235 return (FILE *)(&cached_file_handle[fch]);
8240 | Close a file
8242 errr cached_fclose(FILE *fch_)
8244 CachedFileHandle *fch;
8246 /* Is the FILE* genuine? */
8247 if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
8248 return my_fclose(fch_);
8250 fch = (CachedFileHandle *) fch_;
8252 /* Check for "Ooopses": */
8253 if (fch->fce == NULL)
8254 core("cached_fclose called on a non-open file handle");
8256 flush_file_cache(NULL); /* Clean out the cache if need be */
8257 fch->fce = NULL; /* Mark file handle as inactive */
8259 return 0;
8264 | Do the my_fgets thing on a file
8266 errr cached_fgets(FILE *fch_, char *buffer, int max_len)
8268 CachedFileHandle *fch;
8269 char *eof;
8270 char *ptr;
8272 /* Is the FILE* genuine? */
8273 if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
8274 return my_fgets(fch_, buffer, max_len);
8276 fch = (CachedFileHandle *) fch_;
8278 /* Check for "Oopses": */
8279 if (!file_cache_initd)
8280 core("cached_fgets() on uninitialised file-cache");
8281 if (!fch->fce)
8282 core("cached_fgets called for a un-open file");
8284 eof = fch->fce->eof;
8285 ptr = fch->ptr;
8287 /* Out of bounds? */
8288 if (ptr >= eof)
8290 return 1;
8291 } /* Read failed */
8294 | Read the next line, up to \0 (which would have v=been \n in the original file),
8295 | or max_len-1 characters
8297 if (fch->fce->compressed)
8298 ptr += decompress_string(buffer, ptr, max_len);
8299 else
8301 if (eof - ptr < max_len)
8303 max_len = eof - ptr;
8305 for (; max_len >= 1; max_len--)
8306 if ((*buffer++ = *ptr++) == 0)
8307 break;
8308 *buffer = 0; /* terminate (paranoia) */
8311 /* Update sequential pointer */
8312 fch->ptr = ptr;
8314 return 0;
8317 #endif /* USE_FILECACHE */
8321 | This section deals with checking that the .raw files are up to date
8322 | wrt to the .txt files.
8324 | For this to work, the equivalent function (in init2.c) needs to be
8325 | #if-d out (and this function should be declared). You'll probably
8326 | also need to zap the UNIX #includes at the top of the file
8329 extern errr check_modification_date(int fd, cptr template_file)
8331 char raw_buf[1024];
8332 char txt_buf[1024];
8333 int i;
8334 os_error *e;
8336 /* Use OS_Args 7 to find out the pathname 'fd' refers to */
8337 e = SWI(6, 0, SWI_OS_Args,
8338 /* In: */
8339 7, /* Get path from filehandle */
8340 fd, /* file handle */
8341 raw_buf, /* buffer */
8342 0, 0, /* unused */
8343 1024 /* size of buffer */
8344 /* No output regs used */
8346 if (e)
8348 core(e->errmess);
8351 /* Build the path to the template_file */
8352 path_build(txt_buf, sizeof(txt_buf), ANGBAND_DIR_EDIT, template_file);
8354 i = file_is_newer(riscosify_name(txt_buf), raw_buf);
8356 return i;
8360 /* Alarm functions */
8361 static int alarm_ackd = 0; /* has the alarm been acknowledged? */
8362 static window_handle aw = 0; /* alarm window */
8365 | Is the alarm due to go off, ie. is it enabled, and if so
8366 | does the current time match the alarm time?
8368 static void check_alarm()
8370 time_t t;
8371 struct tm *lt;
8373 alarm_lastcheck = Time_Monotonic();
8375 time(&t);
8376 lt = localtime(&t);
8377 if (lt->tm_hour == alarm_h && lt->tm_min == alarm_m)
8379 if (!alarm_ackd) alarm_disp = 1;
8381 else
8383 alarm_ackd = 0;
8386 /* Hack: if the alarm has already been acknowledged then don't re-trigger it */
8387 if (alarm_ackd)
8389 alarm_disp = 0;
8392 /* Hack: if the alarm should make a noise, then make one: */
8393 if (alarm_disp && alarm_beep == 1)
8395 static unsigned int last_beep = 0;
8396 unsigned int t = Time_Monotonic();
8397 if (t > last_beep + 100)
8399 Sound_SysBeep();
8400 last_beep = t;
8405 | If we're in the desktop then fire the alarm off if need be.
8406 | If we aren't then do nothing - the fullscreen bored() function
8407 | will take care of the alarm.
8409 #ifndef FULLSCREEN_ONLY
8410 if (!fullscreen_font && alarm_disp)
8411 trigger_alarm_desktop();
8412 #endif /* FULLSCREEN_ONLY */
8416 static void ack_alarm(void)
8418 if (aw)
8420 Window_Delete(aw);
8421 aw = 0;
8423 alarm_ackd = 1;
8424 alarm_disp = 0;
8426 if (alarm_type == 3)
8428 /* One shot alarm */
8429 alarm_type = 0;
8430 write_alarm_choices();
8435 #ifndef FULLSCREEN_ONLY
8437 | Click in the (desktop) alarm window
8439 static BOOL Hnd_AlarmClick(event_pollblock * pb, void *ref)
8441 ack_alarm();
8442 return TRUE;
8447 | The alarm has gone off in the desktop
8449 static void trigger_alarm_desktop(void)
8451 char buffer[120];
8452 if (aw)
8453 return;
8455 aw = Window_Create("alarm", template_TITLEMIN);
8456 if (!aw) core("failed to create Alarm window!");
8458 sprintf(buffer, "Alarm from %s", VARIANT);
8459 Window_SetTitle(aw, buffer);
8460 Event_Claim(event_CLICK, aw, 0, Hnd_AlarmClick, NULL);
8461 Event_Claim(event_CLOSE, aw, event_ANY, Hnd_AlarmClick, NULL);
8463 Icon_printf(aw, 1, "An alarm was set for %02d:%02d", alarm_h, alarm_m);
8464 Icon_SetText(aw, 2, alarm_message);
8465 Window_Show(aw, open_CENTERED);
8468 #endif /* FULLSCREEN_ONLY */
8471 /*--------------------------------------------------------------------------*/
8473 #ifndef FE_DEBUG_INFO
8474 static void show_debug_info(void)
8476 core("main-acn internal logic error 004");
8478 #else
8480 static int debug_cx = 0;
8481 static int debug_cy = 0;
8482 static int debug_cl = COL(WHITE);
8483 static int debug_sl = 0;
8486 static void debug_cls(void)
8488 Term_clear();
8489 debug_cx = debug_cy = debug_sl = 0;
8492 static void debug_tcol(int c)
8494 debug_cl = c;
8498 static void debug_scroll(void)
8500 char **c = ((term_data *)Term)->t.scr->c; /* char array */
8501 byte **a = ((term_data *)Term)->t.scr->a; /* attr array */
8502 int cc;
8503 char tmp[82];
8504 int y, x, p;
8506 cc = a[1][0];
8508 for (y = 1; y < 23; y++)
8510 for (x = p = 0; x < 80; x++)
8512 if (a[y][x] != cc)
8514 tmp[p] = 0;
8515 Term_putstr(x - p, y - 1, p, cc, tmp);
8516 p = 0;
8517 cc = a[y][x];
8519 tmp[p++] = c[y][x];
8521 Term_putstr(x - p, y - 1, p, cc, tmp);
8524 Term_erase(0, 22, 80);
8528 static void debug_print_line(char *l)
8530 char *le;
8531 int cr = 0;
8533 /* Handle scrolling */
8534 if (debug_cy > 22)
8536 debug_cy = 22;
8537 if (--debug_sl < 0)
8539 int k;
8540 PUT_FSTR(0, 23, COL(YELLOW), "[RET one line, SPC one page]");
8543 k = inkey();
8545 while (k != 32 && k != 13);
8546 Term_erase(0, 23, 79);
8547 debug_sl = k == 32 ? 21 : 0;
8549 debug_scroll();
8552 /* Hack: check for NL */
8553 for (le = l; *le; le++)
8554 if (*le == '\n')
8556 cr = 1;
8557 break;
8560 /* display text */
8561 Term_putstr(debug_cx, debug_cy, le - l, debug_cl, l);
8563 /* move cursor */
8564 if (!cr)
8566 debug_cx += (le - l);
8567 if (debug_cx >= 80)
8569 cr = 1;
8572 if (cr)
8574 debug_cx = 0;
8575 debug_cy += 1;
8578 Term_gotoxy(debug_cx, debug_cy);
8583 static int debug_next_line(char *lb, char **t, int cx)
8585 int i = 0;
8586 char *lt = *t;
8588 if (!*lt)
8590 return -1;
8591 } /* Out of text */
8593 while (*lt && cx < 80)
8595 lb[i] = *lt++;
8597 if (lb[i] == '\n') /* New line */
8599 cx = 0; /* Cursor x will be 0 after displaying */
8600 i++; /* Keep the \n in the output */
8601 break; /* All done */
8603 else if (lb[i] == '\t') /* Tab */
8605 while (cx < 80)
8607 lb[i++] = ' ';
8608 cx++;
8609 if ((cx & 7) == 0)
8611 break;
8615 else /* Anything else */
8617 cx++;
8618 i++;
8622 lb[i] = 0; /* terminate line buffer */
8623 *t = lt; /* update text pointer */
8624 return cx; /* return cursor x after printing */
8627 static void debug_printf(char *fmt, ...)
8629 char buffer[1024];
8630 char line[82];
8631 va_list ap;
8632 char *p = buffer;
8634 va_start(ap, fmt);
8635 vstrnfmt(buffer, sizeof(buffer), fmt, ap);
8636 va_end(ap);
8638 /* Now split the string into display lines */
8639 while (debug_next_line(line, &p, debug_cx) >= 0)
8640 debug_print_line(line);
8644 static void debug_version_info(void)
8646 debug_tcol(COL(YELLOW));
8648 debug_printf("\n\nMisc. Info:\n");
8649 debug_tcol(COL(WHITE));
8650 debug_printf("\tVariant name = \"%s\"\n", VARIANT);
8651 debug_printf("\tFront-end version: %s\n", PORTVERSION);
8652 debug_printf("\tFront-end compiled: %s %s\n", __TIME__, __DATE__);
8653 debug_printf("\tCompile time flags:\n");
8655 #ifdef USE_FILECACHE
8656 debug_printf("\t\tUSE_FILECACHE\n");
8657 #endif
8659 #ifdef ABBR_FILECACHE
8660 debug_printf("\t\tABBR_FILECACHE\n");
8661 #endif
8663 #ifdef SMART_FILECACHE
8664 debug_printf("\t\tSMART_FILECACHE\n");
8665 #endif
8667 debug_tcol(COL(YELLOW));
8668 debug_printf("\nResource path:\n");
8669 debug_tcol(COL(WHITE));
8670 debug_printf("\t\"%s\"\n", resource_path);
8672 debug_tcol(COL(YELLOW));
8673 debug_printf("\nTempfile path:\n");
8674 debug_tcol(COL(WHITE));
8675 debug_printf("\t\"%s\"\n", scrap_path);
8676 debug_printf("\tScrapfiles are %s deleted at exit.\n",
8677 (flush_scrap ? "" : "NOT"));
8679 debug_tcol(COL(YELLOW));
8680 debug_printf("\nChoices files:\n");
8681 debug_tcol(COL(L_BLUE));
8682 debug_printf("\tDesired files:\n");
8683 debug_tcol(COL(WHITE));
8684 debug_printf("\tPrimary (r/w): \"%s\"\n", choices_file[CHFILE_WRITE]);
8685 debug_printf("\t Fallback (r): \"%s\"\n", choices_file[CHFILE_READ]);
8686 debug_printf("\t Mirror (r/w): \"%s\"\n", choices_file[CHFILE_MIRROR]);
8687 debug_tcol(COL(L_BLUE));
8688 debug_printf("\tActual files:\n");
8689 debug_tcol(COL(WHITE));
8690 debug_printf("\t Write: \"%s\"\n", find_choices(TRUE));
8691 debug_printf("\t Read: \"%s\"\n", find_choices(FALSE));
8693 debug_tcol(COL(YELLOW));
8694 debug_printf("\nAlarm files:\n");
8695 debug_tcol(COL(L_BLUE));
8696 debug_printf("\tDesired files:\n");
8697 debug_tcol(COL(WHITE));
8698 debug_printf("\tPrimary (r/w): \"%s\"\n", alarm_file[CHFILE_WRITE]);
8699 debug_printf("\t Fallback (r): \"%s\"\n", alarm_file[CHFILE_READ]);
8700 debug_tcol(COL(L_BLUE));
8701 debug_printf("\tActual files:\n");
8702 debug_tcol(COL(WHITE));
8703 debug_printf("\t Write: \"%s\"\n", find_alarmfile(TRUE));
8704 debug_printf("\t Read: \"%s\"\n", find_alarmfile(FALSE));
8705 #ifdef USE_DA
8706 debug_tcol(COL(YELLOW));
8707 debug_printf("\nDynamic areas:\n");
8708 debug_tcol(COL(WHITE));
8709 debug_printf("\tFontcache DA = %d\t", font_area);
8710 debug_printf("size = %d\theap size = %d\n", font_area_size, font_heap_size);
8711 debug_printf("\t ralloc DA = %d\t", game_area);
8712 debug_printf("size = %d\theap size = %d\n", game_area_size, game_heap_size);
8713 #endif
8717 static void debug_filecache_info(void)
8719 #ifndef USE_FILECACHE
8720 debug_tcol(COL(L_DARK));
8721 debug_printf("File cache disabled at compile time.\n");
8722 #else
8723 int j, k;
8724 int t, cs, ucs, cf;
8726 cf = cs = ucs = j = k = 0; /* To stop an usused warning if USE_FILECACHE is undefined */
8727 t = strlen(resource_path);
8729 if (!file_cache_initd)
8731 init_file_cache();
8732 } /* Paranoia */
8733 debug_tcol(COL(YELLOW));
8734 debug_printf("\nFilecache contents:\n");
8735 debug_tcol(COL(L_BLUE));
8736 debug_printf("Flags: Smart=%d; Abbrv=%d; Slave=%d; Enable=%d\n",
8737 smart_filecache, abbr_filecache, abbr_tmpfile, use_filecache);
8738 debug_tcol(COL(SLATE));
8739 if (smart_filecache || abbr_filecache)
8740 debug_printf("\t\t%3s %6s/%-6s %6s %6s Path (relative to lib/)\n",
8741 "Hnd", "Cache", "Disc", "Time", "Status");
8742 else
8743 debug_printf("\t\t%3s %6s %6s %6s Path (relative to lib/)\n", "Hnd",
8744 "Size", "Time", "Status");
8746 for (j = 0; j < MAX_CACHE_ENTRIES; j++)
8748 FileCacheEntry *fce = &(file_cache[j]);
8749 if (fce->name)
8751 cf++;
8752 debug_tcol(COL(L_GREEN));
8753 debug_printf("\t\t%3d ", j);
8754 debug_tcol(COL(L_UMBER));
8755 if (!smart_filecache && !abbr_filecache)
8756 debug_printf("%6d ", fce->eof - fce->text);
8757 else
8759 debug_printf("%6d/", fce->eof - fce->text);
8760 k = File_Size(riscosify_name(fce->name));
8761 debug_printf("%-6d ", k);
8762 if (k > 0)
8764 ucs += k;
8767 cs += fce->eof - fce->text;
8768 debug_printf("%6d ", fce->used);
8769 for (k = 0; k < MAX_OPEN_CACHED_FILES; k++)
8770 if (cached_file_handle[k].fce == fce)
8771 break;
8772 debug_tcol(COL(RED));
8773 debug_printf("%-6s ", k < MAX_OPEN_CACHED_FILES ? "Open" : "");
8774 debug_tcol(COL(L_UMBER));
8775 debug_printf("%s\n", fce->name + t);
8779 debug_tcol(COL(L_BLUE));
8780 debug_printf("\tTotal:\t%3d ", cf);
8781 if (ucs)
8782 debug_printf("%6d/%-6d\n", cs, ucs);
8783 else
8784 debug_printf("%6d\n", cs);
8785 debug_tcol(COL(BLUE));
8786 #endif /* USE_FILECACHE */
8790 static void show_debug_info(void)
8792 int k;
8793 /* blank the term */
8794 debug_cls();
8796 /* Repeatedly prompt for a command */
8799 debug_tcol(COL(VIOLET));
8800 debug_printf("\nInfo: (V)ersion, (F)ilecache, ESC=exit ");
8803 k = inkey();
8804 switch (k)
8806 case 'v': case 'V': debug_version_info();
8807 break;
8808 case 'f': case 'F': debug_filecache_info
8810 break;
8811 case 27: break;
8812 default: k = 0;
8815 while (!k);
8817 while (k != 27);
8819 Term_clear();
8822 #endif /* FE_DEBUG_INFO */
8824 #endif /* __riscos */