summaryrefslogtreecommitdiff
path: root/src/main-ros.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main-ros.c')
-rw-r--r--src/main-ros.c8670
1 files changed, 8670 insertions, 0 deletions
diff --git a/src/main-ros.c b/src/main-ros.c
new file mode 100644
index 00000000..124505d2
--- /dev/null
+++ b/src/main-ros.c
@@ -0,0 +1,8670 @@
+/*
+ * File: main-ros.c
+ *
+ * Abstract: Support for RISC OS versions of Angband, including support
+ * for multitasking and dynamic areas.
+ *
+ * Authors: Musus Umbra, Andrew Sidwell, Ben Harrison, and others.
+ *
+ * Licences: Angband licence.
+ */
+
+#ifdef __riscos
+
+#include "angband.h"
+
+/*
+ * Purpose: Support for RISC OS Angband 2.9.x onwards (and variants)
+ *
+ * NB: This code is still under continuous development - if you want to use
+ * it for your own compilation/variant, please contact me so that I can
+ * keep you up to date and give you support :)
+ *
+ * Prerequisites to compiling:
+ *
+ * DeskLib 2.30 or later (earlier versions may be OK though)
+ *
+ * An ANSI C compiler (tested with Acorn's C/C++ and GCC, but should be OK
+ * with any decent compiler)
+ *
+ * My binary distribution (for the templates and other bits)
+ *
+ * Note:
+ * The following symbols are *required* and *must* be defined properly.
+ */
+
+/*
+ * VARIANT & VERSION
+ * These two get variant and version data from Angband itself; older
+ * variants may not have these defined and will have to be altered.
+ */
+#define VARIANT "ToME"
+#define VERSION "2.2.2"
+
+/*
+ * PORTVERSION
+ * This is the port version; it appears in the infobox.
+ */
+#define PORTVERSION "1.29-dev (2003-08-06)"
+
+/*
+ * RISCOS_VARIANT
+ * This must match the entry in the !Variant Obey file, and it must only
+ * contain characters that are valid as part of a RISC OS path variable.
+ * [eg. "Yin-Yangband" is not okay, "EyAngband" is.]
+ */
+#define RISCOS_VARIANT "ToME"
+
+/*
+ * AUTHORS
+ * For the info box. [eg. "Ben Harrison"]
+ */
+#define AUTHORS "DarkGod & co."
+
+/*
+ * PORTERS
+ * For the info box. [eg. "Musus Umbra"]
+ */
+#define PORTERS "A. Sidwell"
+
+/*
+ * ICONNAME
+ * Iconbar icon sprite name eg. "!angband". Note that this must be a valid
+ * sprite name; it may need modifying for long variant names.
+ */
+#define ICONNAME "!"RISCOS_VARIANT
+
+/*
+ * PDEADCHK
+ * This should expand to an expression that is true if the player is dead.
+ * [eg. (p_ptr->is_dead) for Angband or (!alive || dead) for some Zangbands]
+ */
+
+#define PDEADCHK (!alive)
+
+/*
+ * The following symbols control the (optional) file-cache:
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * NB: Variants that don't repeatedly read any files whilst running
+ * (eg. vanilla, sang, etc) should NOT define USE_FILECACHE, etc. as
+ * it causes a non-negligable amount of code to be compiled in.
+ *
+ * NB: The file-cache functions require that some code in files.c is modified
+ * to use the cached_* functions. This should be utterly trivial.
+ *
+ * NB: The returned handle from cached_fopen() is almost certainly *NOT*
+ * a |FILE*| (although it may be if the cache cannot accomodate the file).
+ *
+ * Therefore, you *MUST* ensure that any file opened with cached_fopen()
+ * is only ever accessed via cached_fgets() and cached_fclose().
+ *
+ * Failure to do so will result in, ahem, unpleasantness. Extreme
+ * unpleasantness. "Him fall down, go boom."
+ *
+ * This /may/ change in the near future (ie. to apply caching in a
+ * transparent manner), so do keep a backup of files.c (and any other files
+ * you modify). You always keep backups anyway, don't you? Don't you?!
+ */
+
+/*
+ * USE_FILECACHE
+ * if defined then some caching functions will be compiled for use by the
+ * various get_rnd_line(), etc. in files.c. This could be used in a
+ * variety of places that read data repeatedly, but it's up to you to
+ * implement it then.
+ */
+
+/* #define USE_FILECACHE */
+
+/*
+ * SMART_FILECACHE
+ * This causes lines beginning with '#' (and blank lines) to be discarded
+ * when caching files. This should help Zangband 2.2.5+ but could cause
+ * trouble for other variants. If defined, then smart file caching will be
+ * on by default.
+ */
+
+/* #define SMART_FILECACHE */
+
+/*
+ * ABBR_FILECACHE
+ * ABBR_FILECACHE causes data read into file-cache to be compressed (using a
+ * simple set of abbreviations) by default. This can be overridden using a
+ * command line option. If this symbol is not defined then no compression
+ * code will be compiled and the user option will be ignored/unavailable.
+ */
+
+/* #define ABBR_FILECACHE */
+
+/*
+ * Note:
+ * The following symbols control debugging information.
+ */
+
+/*
+ * FE_DEBUG_INFO
+ * If defined, some functions will be compiled to display some info. on the
+ * state of the front-end (accessible) from the '!' user menu.
+ *
+ * NB: For actual releases you should NOT define this symbol since it causes
+ * a non-negligable amount of code/data to be sucked in.
+ */
+/* #define FE_DEBUG_INFO */
+
+/*
+ * USE_DA
+ * If defined, it enables the use of dynamic areas (these are still only
+ * used when the !Variant file allows it). It is likely that this option
+ * will eventually be removed altogether as there is no major advantege
+ * to using DAs over just using the Wimpslot.
+ */
+#define USE_DA
+
+
+/* Constants, etc. ---------------------------------------------------------*/
+
+/* Deal with any weird file-caching symbols */
+#ifndef USE_FILECACHE
+# undef ABBR_FILECACHE
+# undef SMART_FILECACHE
+#endif
+
+/* Maximum terminals */
+#define MAX_TERM_DATA 8
+
+/* Menu entry numbers */
+#define IBAR_MENU_INFO 0
+#define IBAR_MENU_SAVE 1
+#define IBAR_MENU_FULLSCREEN 2
+#define IBAR_MENU_GAMMA 3
+#define IBAR_MENU_SOUND 4
+#define IBAR_MENU_WINDOWS 5
+#define IBAR_MENU_SAVECHOICES 6
+#define IBAR_MENU_QUIT 7
+
+#define TERM_MENU_INFO 0
+#define TERM_MENU_SAVE 1
+#define TERM_MENU_FONT 2
+#define TERM_MENU_WINDOWS 3
+
+/* Icon numbers */
+#define SND_VOL_SLIDER 0
+#define SND_VOL_DOWN 1
+#define SND_VOL_UP 2
+#define SND_ENABLE 3
+
+#define GAMMA_ICN 0
+#define GAMMA_DOWN 1
+#define GAMMA_UP 2
+
+#define SAVE_ICON 2
+#define SAVE_PATH 1
+#define SAVE_OK 0
+#define SAVE_CANCEL 3
+
+/* Position and size of the colours strip in the gamma window */
+#define GC_XOFF 20
+#define GC_YOFF -14
+#define GC_WIDTH 512
+#define GC_HEIGHT 72
+
+/* Maximum and minimum allowed volume levels */
+#define SOUND_VOL_MIN 16
+#define SOUND_VOL_MAX 176
+
+/*--------------------------------------------------------------------------*/
+
+
+#undef rename
+#undef remove
+
+#include "Desklib:Event.h"
+#include "Desklib:EventMsg.h"
+#include "Desklib:Template.h"
+#include "Desklib:Window.h"
+#include "Desklib:Handler.h"
+#include "Desklib:Screen.h"
+#include "Desklib:Menu.h"
+#include "Desklib:Msgs.h"
+#include "Desklib:Icon.h"
+#include "Desklib:Resource.h"
+#include "Desklib:SWI.h"
+#include "Desklib:Time.h"
+#include "Desklib:Sound.h"
+#include "Desklib:KeyCodes.h"
+#include "Desklib:Kbd.h"
+#include "Desklib:GFX.h"
+#include "Desklib:ColourTran.h"
+#include "Desklib:Error.h"
+#include "Desklib:Coord.h"
+#include "Desklib:Slider.h"
+#include "Desklib:Hourglass.h"
+#include "Desklib:Save.h"
+#include "Desklib:Sprite.h"
+#include "Desklib:KernelSWIs.h"
+#include "DeskLib:Filing.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <time.h>
+#include <math.h>
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | We use the hourglass around calls to Wimp_Poll in an attempt to stop
+ | users thinking that the game has 'hung'.
+ | Kamband/Zangband and the Borg in particular can have quite long delays at
+ | times.
+ */
+#define Start_Hourglass \
+ { if ( use_glass && !glass_on ) { glass_on=1; Hourglass_Start(50); } }
+#define Stop_Hourglass \
+ { if ( glass_on ) { glass_on=0; Hourglass_Off(); } }
+
+
+/*--------------------------------------------------------------------------*/
+/* Types */
+/*--------------------------------------------------------------------------*/
+
+/*
+ | A ZapRedraw block
+ */
+typedef struct
+{
+ union
+ {
+ unsigned int value;
+ struct
+ {
+ unsigned int vdu:1;
+ unsigned int double_height:1;
+ unsigned int extension:1;
+ unsigned int padding:29;
+ }
+ bits;
+ }
+ r_flags;
+
+ int r_minx; /* min x of redraw in pixels from LHS, incl */
+ int r_miny; /* min y of redraw in pixels from top, incl */
+ int r_maxx; /* max x of redraw in pixels from LHS, excl */
+ int r_maxy; /* max y of redraw in pixels from top, excl */
+
+ void *r_screen; /* DSA: address of screen to write to (0=>read) */
+ int r_bpl; /* DSA: bytes per raster line */
+
+ int r_bpp; /* log base 2 of bits per pixel */
+ int r_charw; /* width of a character in pixels */
+ int r_charh; /* height of a character in pixels */
+ void *r_caddr; /* DSA: ->character cache | VDU: ->font name */
+ int r_cbpl; /* DSA: #bytes/character line | VDU: x OS offset */
+ int r_cbpc; /* DSA: #bytes/character | VDU: y OS offset */
+
+ int r_linesp; /* line spacing (pixels) */
+
+ void *r_data; /* -> text to display */
+ int r_scrollx; /* see Redraw dox */
+ int r_scrolly; /* see Redraw dox */
+
+ void *r_palette; /* -> palette lookup table */
+ int r_for; /* foreground colour at start of line */
+ int r_bac; /* background colour at start of line */
+
+ void *r_workarea; /* -> word aligned workspace */
+
+ int r_magx; /* log2 x OS coords per pixel */
+ int r_magy; /* log2 y OS coords per pixel */
+
+ int r_xsize; /* width of screen in pixels */
+ int r_ysize; /* height of screen in pixels */
+
+ int r_mode; /* current screen mode */
+}
+ZapRedrawBlock;
+
+
+/*
+ | We cache font data using an array of 'font handles' (since there is a
+ | known maximum no. of fonts required).
+ | This is what a font 'handle' looks like:
+ */
+typedef struct
+{
+ char *name; /* font name */
+ int usage; /* usage count */
+ int w, h; /* width, height */
+ int f, l; /* first and last character defined */
+ void *bpp_1; /* source bitmap */
+ void *bpp_n; /* bitmap for the current screen mode */
+}
+ZapFont;
+
+/*
+ | A struct to hold all the data relevant to a term window
+ */
+typedef struct
+{
+ term t; /* The Term itself */
+ window_handle w; /* Window handle */
+ ZapFont *font; /* Font */
+ wimp_box changed_box; /* Area out of date */
+ struct
+ {
+ wimp_point pos; /* Cursor position */
+ BOOL visible; /* visibility flag */
+ }
+ cursor;
+ char name[12]; /* Name to give menus opened from the term */
+ int def_open; /* Open by default? */
+ wimp_box def_pos; /* default position */
+ wimp_point def_scroll; /* default scroll offset */
+ int unopened; /* Has this window not been opened yet? */
+}
+term_data;
+
+
+
+/*--------------------------------------------------------------------------*/
+/* ZapRedraw SWI numbers */
+/*--------------------------------------------------------------------------*/
+
+#define SWI_ZapRedraw_ 0x48480
+#define SWI_ZapRedraw_RedrawArea (SWI_ZapRedraw_ + 0x00)
+#define SWI_ZapRedraw_GetPaletteEntry (SWI_ZapRedraw_ + 0x01)
+#define SWI_ZapRedraw_RedrawRaster (SWI_ZapRedraw_ + 0x02)
+#define SWI_ZapRedraw_ConvertBitmap (SWI_ZapRedraw_ + 0x03)
+#define SWI_ZapRedraw_PrepareDataLine (SWI_ZapRedraw_ + 0x04)
+#define SWI_ZapRedraw_AddCursor (SWI_ZapRedraw_ + 0x05)
+#define SWI_ZapRedraw_FindCharacter (SWI_ZapRedraw_ + 0x06)
+#define SWI_ZapRedraw_MoveBytes (SWI_ZapRedraw_ + 0x07)
+#define SWI_ZapRedraw_CachedCharSize (SWI_ZapRedraw_ + 0x08)
+#define SWI_ZapRedraw_ConvBitmapChar (SWI_ZapRedraw_ + 0x09)
+#define SWI_ZapRedraw_CreatePalette (SWI_ZapRedraw_ + 0x0a)
+#define SWI_ZapRedraw_InsertChar (SWI_ZapRedraw_ + 0x0b)
+#define SWI_ZapRedraw_ReadSystemChars (SWI_ZapRedraw_ + 0x0c)
+#define SWI_ZapRedraw_ReverseBitmaps (SWI_ZapRedraw_ + 0x0d)
+#define SWI_ZapRedraw_ReadVduVars (SWI_ZapRedraw_ + 0x0e)
+#define SWI_ZapRedraw_GetRectangle (SWI_ZapRedraw_ + 0x0f)
+#define SWI_ZapRedraw_AddVduBitmaps (SWI_ZapRedraw_ + 0x10)
+#define SWI_ZapRedraw_CacheFontChars (SWI_ZapRedraw_ + 0x11)
+#define SWI_ZapRedraw_SpriteSize (SWI_ZapRedraw_ + 0x12)
+#define SWI_ZapRedraw_RedrawWindow (SWI_ZapRedraw_ + 0x13)
+
+
+/*
+ | Other SWI numbers that aren't defined in DeskLib's SWI.h:
+ */
+#define SWI_OS_ScreenMode 0x65
+#define SWI_OS_DynamicArea 0x66
+#define SWI_ColourTrans_ReturnColourNumber 0x40744
+#define SWI_Wimp_ReportError 0x400df
+#define SWI_PlayIt_Volume 0x4d146
+
+
+
+/*--------------------------------------------------------------------------*
+ | File scope variables |
+ *--------------------------------------------------------------------------*/
+static int ftype = 0xffd; /* hack so saved games get the right type */
+static int filehandle[16]; /* we keep track of open files with this */
+static int openfiles = 0; /* how many files are currently open */
+
+/*
+ | Paths we use...
+ */
+static char resource_path[260] = ""; /* Path pointng to "!Angband.Lib." */
+static char scrap_path[260] = ""; /* Path to create scrap files on */
+static char choices_file[3][260] =
+{ "", "", "" }; /* Choices paths (read/write, mirror, read) */
+static char alarm_file[2][260] =
+{ "", "" }; /* Alarm choices paths (read/write, mirror, read) */
+/*
+ | So we can use something more meaningful later...
+ | NB: Mirror is only meaningful for Choices and we don't
+ | even reserve space for alarm_file[CHFILE_MIRROR].
+ */
+#define CHFILE_WRITE 0
+#define CHFILE_READ 1
+#define CHFILE_MIRROR 2
+
+/*
+ | Other 'globals':
+ */
+static int initialised = 0; /* Used to determine whether to try to save */
+static int game_in_progress = 0; /* if Quit (or core() is called), etc. */
+
+static byte a_palette[256][4]; /* a copy of the raw Angband palette */
+static unsigned int palette[256]; /* palette as gamma'd bbggrrxx words */
+static unsigned int zpalette[256]; /* And our version for ZapRedraw */
+static double gamma = 1.0; /* assume gamma of 1.0 if unspecified */
+
+static int enable_sound = 0; /* enable sound FX */
+static int sound_volume = 127; /* Full volume */
+static int force_mono = 0; /* force monochrome */
+static int start_fullscreen = 0; /* start up full screen (added in 1.18) */
+static int hack_flush = 0; /* Should TERM_XTRA_FLUSH wait for all keys to be released? */
+static int flush_scrap = 1; /* Should any scrapfiles (incl. filecache) be deleted at exit? */
+static int max_file_cache_size = 64 << 10;
+static unsigned int vfiletype;
+static int allow_iclear_hack = 0; /* Allow the hideously evil Iclear workaround thing */
+static int alarm_type = 0; /* is there an alarm set? */
+static int alarm_h = 0, alarm_m = 0; /* alarm time (midnight) */
+static char alarm_message[80] = "Time for bed!"; /* the message to give */
+static int alarm_disp = 0; /* is the alarm being displayed? */
+static int alarm_beep = 0; /* should be beep? */
+static const char *alarm_types[] =
+{ "Off", "On (one-shot)", "On (repeating)", "On (one-shot)" };
+static unsigned int alarm_lastcheck = 0;
+
+
+/* A little macro to save some typing later: */
+#define COLOUR_CHANGED(x) \
+ ( (angband_color_table[x][1]!=a_palette[x][1]) || \
+ (angband_color_table[x][2]!=a_palette[x][2]) || \
+ (angband_color_table[x][3]!=a_palette[x][3]) )
+
+static int got_caret = 0; /* Do we own the caret? */
+static int key_pressed = 0; /* 'Key has been pressed' Flag */
+static int use_glass = 1; /* use the hourglass between WimpPolls? */
+static int glass_on = 1; /* is the hourglass on? */
+static int user_menu_active = FALSE; /* set to TRUE when the user menu is active */
+
+/* Font system variables */
+static ZapFont fonts[MAX_TERM_DATA + 1]; /* The +1 is for the system font */
+
+/* The system font is always font 0 */
+#define SYSTEM_FONT (&(fonts[0]))
+
+/* Term system variables */
+static term_data data[MAX_TERM_DATA]; /* One per term */
+
+#ifndef FULLSCREEN_ONLY
+static char r_data[24 * (80 * 5 + 4) + 25 * 4]; /* buffer for ZapRedraw data */
+
+/* Wimp variables */
+static icon_handle ibar_icon; /* Iconbar icon handle */
+static window_handle info_box; /* handle of the info window */
+static window_handle gamma_win; /* gamma correction window */
+static window_handle sound_win; /* sound options window */
+static window_handle save_box; /* The savebox */
+static menu_ptr ibar_menu; /* Iconbar menu */
+static menu_ptr term_menu; /* Term window menu */
+static menu_ptr wind_menu; /* windows (sub) menu */
+static menu_ptr font_menu; /* Font (sub)menu */
+
+static save_saveblock *saveblk = NULL; /* For the save box */
+
+/* List of Wimp messages we want to be given */
+static int message_list[] =
+{
+ message_MODECHANGE,
+ message_PALETTECHANGE,
+
+ /* For the savebox */
+ message_MENUWARN,
+ message_DATASAVEACK,
+
+ message_PREQUIT,
+ 0
+};
+
+
+static term_data *menu_term; /* term the last menu was opened for */
+
+#endif /* FULLSCREEN_ONLY */
+
+static ZapRedrawBlock zrb; /* a redraw block */
+
+/* Cursor colour */
+#define CURSOR_COLOUR 255 /* Cursor's Angband colour */
+#define CURSOR_RGB 0x00ffff00 /* if undefined, use bbggrrxx */
+
+static int cursor_rgb = -1; /* colour to use for cursor */
+
+static int fullscreen_mode = 0; /* screen mode in use */
+static int old_screenmode = 0; /* Mode we started out in */
+static int *fullscreen_font = 0; /* font data for fullscreen use */
+static int *fullscreen_base = 0; /* base address of screen */
+static int fullscreen_height; /* height of the fullscreen font */
+static int fullscreen_topline; /* raster offset of fullscreen */
+
+#define KEYPRESS_QUIT 0x1cc /* F12 gets back to the desktop */
+#define TERM_TOPLINE_HR 32 /* vertical pixel offset in mode 27 */
+#define TERM_TOPLINE_LR 16 /* vertical pixel offset in mode 12 */
+#define TIME_LINE 26 /* Line to display the clock on */
+
+/* text to display at the bottom left of the fullscreen display */
+static const char *fs_quit_key_text = "Press f12 to return to the desktop";
+static const char *alarm_cancel_text = "(Press ^Escape to cancel the alarm)";
+
+/* Debugging flags, etc. */
+static int log_g_malloc = 0; /* Log calls to ralloc, etc */
+static int show_sound_alloc = 0; /* Log sound mappings, etc */
+
+/* Activate file caching? */
+#ifdef USE_FILECACHE
+static int use_filecache = TRUE;
+#else
+static int use_filecache = FALSE;
+#endif
+
+/* Cripple some things to save memory */
+static int minimise_memory = 0;
+
+/* Forward declarations of some of the Full Screen Mode stuff */
+static void enter_fullscreen_mode(void);
+static void leave_fullscreen_mode(void);
+static void set_keys(int claim);
+
+/* Forwards declarations of the sound stuff */
+static void initialise_sound(void);
+static void play_sound(int event);
+
+/* Forward declarations of Term hooks, etc. */
+static void Term_init_acn(term *t);
+static errr Term_user_acn(int n);
+
+#ifndef FULLSCREEN_ONLY
+static errr Term_curs_acn(int x, int y);
+static errr Term_text_acn(int x, int y, int n, byte a, cptr s);
+static errr Term_xtra_acn(int n, int v);
+static errr Term_wipe_acn(int x, int y, int n);
+static errr Term_xtra_acn_check(void);
+static errr Term_xtra_acn_event(void);
+static errr Term_xtra_acn_react(void);
+#endif /* FULLSCREEN_ONLY */
+
+static errr Term_curs_acnFS(int x, int y);
+static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
+static errr Term_xtra_acnFS(int n, int v);
+static errr Term_wipe_acnFS(int x, int y, int n);
+static errr Term_xtra_acn_checkFS(void);
+static errr Term_xtra_acn_clearFS(void);
+static errr Term_xtra_acn_eventFS(void);
+static errr Term_xtra_acn_reactFS(int force);
+
+#ifdef USE_DA
+/* Forward declarations of the memory stuff */
+static void init_memory(int, int);
+#endif
+
+/* Forward declarations of the alarm stuff */
+static void check_alarm(void);
+#ifndef FULLSCREEN_ONLY
+static void trigger_alarm_desktop(void);
+#endif /* FULLSCREEN_ONLY */
+static void ack_alarm(void);
+static void write_alarm_choices(void);
+static void read_alarm_choices(void);
+
+
+/* This just shows some debugging info (if enabled with FE_DEBUG_INFO) */
+static void show_debug_info(void);
+
+
+
+/* File-caching functions (if enabled at compile time) */
+#ifdef USE_FILECACHE
+FILE *cached_fopen(char *name, char *mode);
+errr cached_fclose(FILE *fch);
+errr cached_fgets(FILE *fch, char *buffer, int max_len);
+#endif
+
+/*
+ | These functions act as malloc/free, but (if possible) using memory
+ | in the 'Game' Dynamic Area created by init_memory()
+ | We attach these functions to the ralloc_aux and rnfree_aux hooks
+ | that z-virt.c provides.
+ */
+#ifdef USE_DA
+static vptr g_malloc(huge size);
+static vptr g_free(vptr blk, huge);
+#else
+ #define g_malloc(size) malloc(size);
+ #define g_free(block, size) free(block);
+#endif
+
+/*
+ | These functions act as malloc/free, but (if possible) using memory
+ | in the 'Fonts' Dynamic Area created by init_memory()
+ */
+#ifdef USE_DA
+static void* f_malloc(size_t size);
+static void f_free(void *blk);
+#else
+ #define f_malloc(size) malloc(size);
+ #define f_free(block) free(block);
+#endif
+
+/*
+ | These two functions perpetrate great evil to stop IClear from mucking
+ | with the cursor keys in fullscreen mode.
+ */
+static void iclear_hack(void);
+static void remove_iclear_hack(void);
+
+/*
+ | We use this to locate the choices file(s)...
+ */
+static char *find_choices(int write);
+static char *find_choices_mirror(void);
+static char *find_alarmfile(int write);
+
+
+
+/*
+ | This function is supplied as a wrapper to the save_player function.
+ |
+ | Its purpose is to change the filename that the game will be saved with
+ | the leafname "!!PANIC!!" so that panic saves that break the savefile
+ | won't overwrite the original savefile.
+ |
+ | To get this to work, you'll need to ammend files.c and change the call
+ | to save_player in the panic save function(s) (search for "panic save")
+ | to a call to save_player_panic_acn. You can declare a prototype for
+ | the function if you like.
+ */
+
+extern int save_player_panic_acn(void)
+{
+ char *e, *l;
+
+ /* Find the final / in the savefile name */
+ for (l = e = savefile; *e; e++)
+ if (*e == '/')
+ {
+ l = e + 1;
+ }
+
+ /* Write over the current leaf with the special panic one */
+ strcpy(l, "!!PANIC!!");
+
+ /* save the game */
+ return save_player();
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* Error reporting, etc. */
+/*--------------------------------------------------------------------------*/
+
+
+/* Tell the user something important */
+static void plog_hook(cptr str)
+{
+ Msgs_Report(1, "err.plog", str);
+}
+
+/* Tell the user something, then quit */
+static void quit_hook(cptr str)
+{
+ /* str may be null */
+ if (str) Msgs_Report(1, "err.quit", str);
+ exit(0);
+}
+
+/* Tell the user something then crash ;) */
+static void core_hook(cptr str)
+{
+ Msgs_Report(1, "err.core", str);
+
+ if (game_in_progress && character_generated)
+ save_player_panic_acn();
+
+ quit(NULL);
+}
+
+static void debug(const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[260];
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+ plog(buffer);
+}
+
+
+
+/*
+static void oserror_handler( int sig )
+{
+ core(_kernel_last_oserror()->errmess);
+}
+*/
+
+
+/*--------------------------------------------------------------------------*/
+/* File handling */
+/*--------------------------------------------------------------------------*/
+
+static int myFile_Open(const char *name, int mode)
+{
+ int handle;
+ if (SWI(2, 1, SWI_OS_Find, mode, name, /**/ &handle))
+ return 0;
+ return handle;
+}
+
+static int myFile_Size(const char *name)
+{
+ int size, type;
+ if (SWI(2, 5, SWI_OS_File, 17, name, /**/ &type, 0, 0, 0, &size))
+ return -2;
+ return type ? size : -1;
+}
+
+static os_error *myFile_Close(const int handle)
+{
+ return SWI(2, 0, SWI_OS_Find, 0, handle);
+}
+
+static os_error *myFile_Seek(const int handle, const int offset)
+{
+ return SWI(3, 0, SWI_OS_Args, 1, handle, offset);
+}
+
+static int myFile_WriteBytes(const int handle, const void *buf, const int n)
+{
+ int ntf;
+ if (SWI(4, 4, SWI_OS_GBPB, 2, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
+ return n;
+ return ntf;
+}
+
+static int myFile_ReadBytes(const int handle, void *buf, const int n)
+{
+ int ntf;
+ if (SWI(4, 4, SWI_OS_GBPB, 4, handle, buf, n, /**/ NULL, NULL, NULL, &ntf))
+ return n;
+ return ntf;
+}
+
+static os_error *myFile_SetType(const char *n, const int type)
+{
+ return SWI(3, 0, SWI_OS_File, 18, n, type);
+}
+
+
+static int myFile_Extent(const int handle)
+{
+ int ext;
+ if (SWI(2, 3, SWI_OS_Args, 2, handle, /**/ NULL, NULL, &ext))
+ return -1;
+ return ext;
+}
+
+
+/*
+ | Determine if one file is newer than another.
+ |
+ | The filenames should be specified in RISC OS style.
+ |
+ | Returns -1 if 'a' is newer than 'b'.
+ */
+static int file_is_newer(const char *a, const char *b)
+{
+ os_error *e;
+ struct
+ {
+ unsigned int msw;
+ unsigned int lsw;
+ }
+ a_time;
+ struct
+ {
+ unsigned int msw;
+ unsigned int lsw;
+ }
+ b_time;
+ int a_type, b_type;
+
+ /* Get the datestamp of the 'a' file */
+ e = SWI(2, 4, SWI_OS_File,
+ /* In */
+ 17, (int)a,
+ /* Out */
+ &a_type, /* object type */
+ NULL, &(a_time.msw), /* Load Addr */
+ &(a_time.lsw) /* Exec Addr */
+ );
+ if (e)
+ {
+ core(e->errmess);
+ }
+
+
+ /* Get the datestamp of the 'b' file */
+ e = SWI(2, 4, SWI_OS_File,
+ /* In */
+ 17, (int)b,
+ /* Out */
+ &b_type, /* object type */
+ NULL, &(b_time.msw), /* Load Addr */
+ &(b_time.lsw) /* Exec Addr */
+ );
+ if (e)
+ {
+ core(e->errmess);
+ }
+
+ /* If 'b' doesn't exist then 'b' is OOD. */
+ if (!b_type)
+ {
+ return -1;
+ }
+ /* If 'a' doesn't exist then 'b' isn't OOD. (?) */
+ if (!a_type)
+ {
+ return 0;
+ }
+
+ /* Compare the timestamps (assume that the files are typed) */
+ if ((a_time.msw & 0xff) >= (b_time.msw & 0xff))
+ if ((a_time.lsw) > (b_time.lsw))
+ return -1; /* OOD */
+ return 0; /* Not OOD */
+}
+
+
+/*
+ | As fprintf, but outout to all files (if their handles are non zero).
+ | NB: void type.
+ */
+static void f2printf(FILE *a, FILE *b, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[2048];
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+ if (a)
+ {
+ fprintf(a, buffer);
+ }
+ if (b)
+ {
+ fprintf(b, buffer);
+ }
+ va_end(ap);
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Clean up (ie. close files, etc). */
+/*--------------------------------------------------------------------------*/
+
+static void final_acn(void)
+{
+ int i;
+
+ for (i = 0; i < openfiles; i++)
+ myFile_Close(filehandle[i]);
+
+ if (fullscreen_mode)
+ {
+ /* Restore the screen mode */
+ Wimp_SetMode(old_screenmode);
+
+ /* Restore the various soft keys */
+ set_keys(FALSE);
+
+ /*
+ | Hack: Early WIMP versions do the "Press SPACE" thing, or something
+ | odd. It's bloody annoying, whatever it is...
+ */
+ if (event_wimpversion < 300)
+ Wimp_CommandWindow(-1);
+ }
+
+ if (flush_scrap && *scrap_path)
+ {
+ char tmp[512];
+ strcpy(tmp, scrap_path);
+ tmp[strlen(tmp) - 1] = 0; /* Remove trailing dot */
+ SWI(4, 0, SWI_OS_FSControl, 27, tmp, 0, 1); /* ie. "*Wipe <scrapdir> r~c~v~f" */
+ }
+
+#ifdef FULLSCREEN_ONLY
+ Wimp_CommandWindow(-1);
+#endif /* FULLSCREEN_ONLY */
+
+ Stop_Hourglass;
+}
+
+
+/*--------------------------------------------------------------------------*
+ | Various UNIX-like support funtions |
+ *--------------------------------------------------------------------------*/
+
+/*
+ | Hack: determine whether filenames should be truncated to 10 chars or not.
+ |
+ | Needed since RO2 (and RO3 with Truncate configured off) will return
+ | errors instead of automatically truncating long filenames.
+ */
+static int truncate_names(void)
+{
+ int r1, r2;
+
+ /* First, check the OS version */
+ OS_Byte(osbyte_READOSIDENTIFIER, 0x00, 0xff, &r1, &r2);
+
+ /* Assume that we need to truncate if running under RO2 */
+ if (r1 == 0xa1 || r1 == 0xa2)
+ return TRUE;
+
+ /* Okay, so we've got RO3 (or later), so check the CMOS RAM */
+ OS_Byte(osbyte_READCMOSRAM, 28, 0, &r1, &r2);
+
+ /* Bit 0 of byte 28 is the Truncate flag */
+ return !(r2 & 1);
+}
+
+
+/*
+ | The PathName translation is now done by two separate functions:
+ | unixify_name() and riscosify_name().
+ |
+ | This is done because only the UNIX=>RISCOS translation should
+ | ever affect the length of the leafname (ie. by truncating it to
+ | 10 chars if necessary).
+ |
+ | Note that the two functions are identical but for the truncation
+ | check so all that's really been done is that translate_name() now
+ | takes an extra argument: 'trunc' that controls whether truncation
+ | is applied, and riscosify and unixify just call translate_name().
+ */
+static char *translate_name(const char *path, int trunc)
+{
+ static char buf[260];
+ char c, *p;
+
+ /* Copy 'path' into 'buf', swapping dots and slashes */
+ p = buf; /* Output position */
+ do
+ {
+ c = *path++;
+ if (c == '/')
+ c = '.';
+ else if (c == '.')
+ c = '/';
+ *p++ = c;
+ }
+ while (c); /* Terminator /is/ copied */
+
+ /*
+ | When saving a game, the old game is renamed as
+ | "SavedGame.old", the new one is saved as "SavedGame.new",
+ | "SavedGame.old" is deleted, "SavedGame.new" is renamed
+ | as "SavedGame". This will go wrong on a Filecore based filing
+ | system if the saved game has a leafname > 8 chars.
+ */
+
+ if ((p = strstr(buf, "/old")) == NULL)
+ {
+ p = strstr(buf, "/new");
+ }
+ if (!p)
+ {
+ ftype = 0xffd;
+ }
+ else
+ {
+ char *q = strrchr(buf, '.');
+ if (q)
+ if (p - q > 6)
+ {
+ memmove(q + 6, p, 5);
+ }
+ ftype = vfiletype;
+ }
+
+ /*
+ | Hack: Do we need to truncate the leafname?
+ */
+ if (trunc)
+ {
+ if (truncate_names())
+ {
+ char *a, *b;
+ /*
+ | Assume that only the leafname needs attention
+ | (this should be true for any variant)
+ */
+ for (a = b = buf; *a; a++)
+ if (*a == '.')
+ b = a + 1;
+ /*
+ | Now b points to the start of the leafname.
+ | If the leafname is >10 chars, write over the 10th with a
+ | terminator.
+ */
+ if (strlen(b) > 10)
+ {
+ b[10] = 0;
+ };
+ }
+ }
+
+ return buf;
+}
+
+
+extern char *riscosify_name(const char *path)
+{
+ return translate_name(path, TRUE);
+}
+
+static char *unixify_name(const char *path)
+{
+ return translate_name(path, FALSE);
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+
+/* Open a file [as fopen()] but translate the requested filename first */
+
+FILE *my_fopen(const char *f, const char *m)
+{
+ FILE *fp;
+ char *n = riscosify_name(f); /* translate for RO */
+
+ /* Try to open the file */
+ fp = fopen(n, m);
+
+ /* If it succeded and the file was opened for binary output
+ | then set the type according to the 'ftype' hack.
+ | NB: This will fail on some filing systems.
+ */
+
+ if (fp && strstr(m, "wb"))
+ {
+ myFile_SetType(n, ftype);
+ }
+
+ return fp;
+}
+
+
+
+
+/* Close a file, a la fclose() */
+
+errr my_fclose(FILE *fp)
+{
+ /* Close the file, return 1 for an error, 0 otherwise */
+ return fclose(fp) ? 1 : 0;
+}
+
+
+/* Open/Create a file */
+
+int fd_make(cptr file, int mode)
+{
+ char *real_path;
+ int handle;
+
+ /* Translate the filename into a RISCOS one */
+ real_path = riscosify_name(file);
+
+ /* Try to OPENOUT the file (no path, error if dir or not found) */
+ handle = myFile_Open(real_path, 0x8f);
+
+ /* Check for failure */
+ if (!handle)
+ {
+ return -1;
+ }
+
+ /* Try to set the filetype according to the ftype hack */
+ myFile_SetType(real_path, ftype);
+
+ /* We keep track of up to 16 open files at any given time */
+ if (openfiles < 16)
+ filehandle[openfiles++] = handle;
+
+ return (handle);
+}
+
+
+/* Delete a file [as remove()] */
+errr fd_kill(cptr file)
+{
+ return remove(riscosify_name(file)) ? 1 : 0;
+}
+
+
+/* Rename a file [as rename()] */
+errr fd_move(cptr old, cptr new)
+{
+ char new_[260];
+ strcpy(new_, riscosify_name(new));
+ return rename(riscosify_name(old), new_) ? 1 : 0;
+}
+
+/* Open a file */
+int fd_open(cptr path, int flags)
+{
+ int handle = 0;
+ char *real_path = riscosify_name(path);
+
+ switch (flags & 0x0f)
+ {
+ case O_RDONLY: /* Read only */
+ handle = myFile_Open(real_path, 0x4f);
+ break;
+ case O_WRONLY: /* Write only */
+ case O_RDWR: /* Read/Write */
+ handle = myFile_Open(real_path, 0xcf);
+ }
+
+ /* Check for failure */
+ if (!handle)
+ {
+ return (-1);
+ }
+
+ /* Keep track of upto 16 open files... */
+ if (openfiles < 16)
+ filehandle[openfiles++] = handle;
+
+ return (handle);
+}
+
+
+/* Close a file opened with fd_make or fd_open */
+errr fd_close(int handle)
+{
+ int i;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+
+ /* Try to close the file */
+ if (myFile_Close(handle))
+ {
+ return 1;
+ }
+
+ /* Mark the file as closed in our array of file handles */
+ openfiles--;
+ /* Find the entry in the array (if it exists) */
+ for (i = 0; i < 16; i++)
+ if (filehandle[i] == handle)
+ {
+ break;
+ }
+ /* Shuffle the remaining entries down */
+ for (; i < openfiles; i++)
+ filehandle[i] = filehandle[i + 1];
+
+ return 0; /* Sucess */
+}
+
+
+
+/* Read some bytes from a file */
+errr fd_read(int handle, char *buf, huge nbytes)
+{
+ int unread;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+ unread = myFile_ReadBytes(handle, buf, (int)nbytes);
+
+ return unread ? 1 : 0;
+}
+
+
+/* Write some bytes to a file */
+errr fd_write(int handle, const char *buf, huge nbytes)
+{
+ int unwritten;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+ unwritten = myFile_WriteBytes(handle, (const void *)buf, (int)nbytes);
+
+ return unwritten ? 1 : 0;
+}
+
+
+/* Seek in a file */
+errr fd_seek(int handle, huge offset)
+{
+ os_error *e;
+
+ if (handle <= 0)
+ {
+ return -1;
+ } /* Illegal handle */
+ e = myFile_Seek(handle, (int)offset);
+
+ return e ? 1 : 0;
+}
+
+
+/* RISC OS provides no file locking facilities, so: */
+errr fd_lock(int handle, int what)
+{
+ return 0;
+}
+
+
+/* Get a temporary filename */
+errr path_temp(char *buf, int max)
+{
+
+ /*
+ | New in 1.25 - use the scrap path we decided on earlier, or
+ | fall back on tmpnam() if that fails for some reason.
+ */
+ if (*scrap_path)
+ {
+ time_t t;
+ int m;
+ char tmp[512];
+
+ time(&t);
+ for (m = 0; m < 80; m++)
+ {
+ sprintf(tmp, "%s0x%08x", scrap_path, (int)t + m);
+ if (myFile_Size(tmp) == -1)
+ {
+ break;
+ }
+ }
+
+ if (m < 80)
+ {
+ strncpy(buf, unixify_name(tmp), max);
+ return 0;
+ }
+ }
+
+ strncpy(buf, unixify_name(tmpnam(NULL)), max);
+ return 0;
+}
+
+
+
+/*
+ * Create a new path by appending a file (or directory) to a path
+ *
+ * This requires no special processing on simple machines, except
+ * for verifying the size of the filename, but note the ability to
+ * bypass the given "path" with certain special file-names.
+ *
+ * Note that the "file" may actually be a "sub-path", including
+ * a path and a file.
+ *
+ * Note that this function yields a path which must be "parsed"
+ * using the "parse" function above.
+ */
+errr path_build(char *buf, int max, cptr path, cptr file)
+{
+ /* Special file */
+ if (file[0] == '~')
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Absolute file, on "normal" systems */
+ else if (prefix(file, PATH_SEP) && !streq(PATH_SEP, ""))
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* No path given */
+ else if (!path[0])
+ {
+ /* Use the file itself */
+ strnfmt(buf, max, "%s", file);
+ }
+
+ /* Path and File */
+ else
+ {
+ /* Build the new path */
+ strnfmt(buf, max, "%s%s%s", path, PATH_SEP, file);
+ }
+
+ /* Success */
+ return (0);
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Font Functions */
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Cache the system font as fonts[0]
+ | Returns 1 for sucess or 0 for failure.
+ | NB: The n_bpp data is *not* cached, just the 1bpp data and font info.
+ | Also, the usage is never affected.
+ */
+static int cache_system_font(void)
+{
+ ZapFont *sys = SYSTEM_FONT;
+ ZapRedrawBlock zrb;
+ char work_area[16];
+ int i;
+
+ /* Cache the system font (as fonts[0]) */
+ if (sys->bpp_1)
+ {
+ f_free(sys->bpp_1);
+ sys->bpp_1 = 0;
+ }
+ sys->bpp_1 = f_malloc(8 * 256); /* 2K */
+ if (!sys->bpp_1)
+ {
+ return 0;
+ }
+
+ /* Mung so that undefined characters show up as inverted ?s */
+ work_area[3] = '?';
+ SWI(2, 0, SWI_OS_Word, 10, work_area + 3);
+ for (i = 4; i < 12; i++)
+ work_area[i] ^= 255; /* invert colours */
+ SWI(4, 0, SWI_ZapRedraw_ReverseBitmaps, 0, work_area + 4, work_area + 4, 8);
+ for (i = 0; i < 0x20; i++)
+ memcpy(((char *)sys->bpp_1) + i * 8, work_area + 4, 8);
+
+ /* Read the system font */
+ zrb.r_workarea = work_area;
+ SWI(2, 0, SWI_ZapRedraw_ReadSystemChars, sys->bpp_1, &zrb);
+
+ /* Set up some little bits of info */
+ sys->name = (char *) "<System>";
+ sys->w = sys->h = 8;
+ sys->f = 0;
+ sys->l = 255;
+
+ return 1;
+}
+
+
+
+/*
+ | Prepare the font system
+ */
+static void initialise_fonts(void)
+{
+ /* Initialise the array */
+ memset(fonts, 0, sizeof(fonts)); /* Clear to zeroes */
+
+ /* Cache the system font */
+ cache_system_font();
+ fonts[0].usage = 0; /* No users */
+}
+
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Find a font (by name) in the array.
+ | Returns 0 if the font isn't loaded, or a ZapFont* for it if it is.
+ */
+static ZapFont *find_font_by_name(char *name)
+{
+ int i;
+ for (i = 0; i <= MAX_TERM_DATA; i++)
+ if (fonts[i].name)
+ if (!strcmp(fonts[i].name, name))
+ return &(fonts[i]);
+ return NULL;
+}
+
+/*
+ | Find a free slot in the fonts array
+ */
+static ZapFont *find_free_font(void)
+{
+ int i;
+ for (i = 1; i <= MAX_TERM_DATA; i++)
+ if (!fonts[i].name)
+ {
+ return &(fonts[i]);
+ }
+ return NULL;
+}
+
+
+
+/*
+ | Load a font from disc and set up the header info, etc.
+ | NB: doesn't cache the nbpp data, just the 1bpp data.
+ | (Sets usage to 1)
+ | Returns NULL if failed.
+ */
+static ZapFont *load_font(char *name, ZapFont *f)
+{
+ int handle, extent;
+ char path[260];
+ struct
+ {
+ char id[8];
+ int w, h, f, l, r1, r2;
+ }
+ header;
+ char *font_path;
+ char *t;
+ char *real_name = name; /* need to preserve this */
+
+ /*
+ | 1.10 - the first element of the name determines the path to load
+ | the font from.
+ */
+
+ /* The font paths start <RISCOS_VARIANT>$ */
+ t = path + sprintf(path, "%s$", RISCOS_VARIANT);
+
+ /* Copy the path specifier and move 'name' past it */
+ for (; *name != '.'; *t++ = *name++) ;
+
+ /* After this, the name now points to the font name proper */
+ name++;
+
+ /* Append the end of the path name */
+ strcpy(t, "$FontPath");
+
+ /* Get the path setting */
+ font_path = getenv(path);
+ if (!font_path || !*font_path)
+ strcpy(path, "null:$.");
+ else
+ {
+ strcpy(path, font_path);
+ for (t = path; *t > ' '; t++)
+ ;
+ if (t[-1] != '.' && t[-1] != ':')
+ {
+ *t++ = '.';
+ }
+ *t = 0;
+ }
+ strcat(path, name);
+
+
+ /* Open the file */
+ handle = myFile_Open(path, 0x4f);
+ if (!handle)
+ {
+ return NULL;
+ }
+
+ /* Read the header */
+ if (myFile_ReadBytes(handle, &header, sizeof(header)))
+ {
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Check that it's a zapfont */
+ if (strncmp(header.id, "ZapFont\r", 8))
+ {
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Calculate the size of the 1bpp data */
+ extent = myFile_Extent(handle) - sizeof(header);
+
+ /* Allocate the storage for the 1bpp data */
+ f->bpp_1 = f_malloc(extent);
+ if (!f->bpp_1)
+ {
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Load the 1bpp data */
+ if (myFile_ReadBytes(handle, f->bpp_1, extent))
+ {
+ f_free(f->bpp_1);
+ f->bpp_1 = 0;
+ myFile_Close(handle);
+ return NULL;
+ }
+
+ /* Close the file and set the header, etc. */
+ myFile_Close(handle);
+ f->name = f_malloc(strlen(real_name) + 1);
+ if (!f->name)
+ {
+ f_free(f->bpp_1);
+ f->bpp_1 = 0;
+ return NULL;
+ }
+
+ strcpy(f->name, real_name);
+ f->w = header.w;
+ f->h = header.h;
+ f->f = header.f;
+ f->l = header.l;
+ f->usage = 1;
+
+ return f;
+}
+
+
+
+
+/*
+ | Cache a font at a suitable number of bpp for the current mode
+ | Returns 0 for failure, 1 for sucess.
+ | If the call fails then the font's bpp_n entry will be NULL.
+ */
+static int cache_font_for_mode(ZapFont *f)
+{
+ ZapRedrawBlock b;
+ char work_area[128];
+ int size;
+
+ if (!f)
+ {
+ return 0;
+ }
+ if (!f->bpp_1)
+ {
+ return 0;
+ }
+
+ b.r_workarea = work_area;
+ SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
+
+ b.r_workarea = work_area; /* Paranoia */
+ b.r_charh = f->h;
+ b.r_charw = f->w;
+ SWI(4, 4, SWI_ZapRedraw_CachedCharSize, b.r_bpp, 0, f->w, f->h,
+ NULL, NULL, &(b.r_cbpl), &(b.r_cbpc));
+
+ size = 256 * b.r_cbpc;
+ if (f->bpp_n)
+ {
+ f_free(f->bpp_n);
+ f->bpp_n = NULL;
+ }
+ f->bpp_n = f_malloc(size);
+ if (!f->bpp_n)
+ {
+ return 0;
+ }
+
+ b.r_workarea = work_area; /* Paranoia */
+ b.r_caddr = f->bpp_n;
+ SWI(5, 0, SWI_ZapRedraw_ConvertBitmap, 0, &b, 0, 255, f->bpp_1);
+
+ return 1;
+}
+
+
+
+/*
+ | Stop using a font.
+ | If the font's usage drops to zero then the font data is purged.
+ */
+static void lose_font(ZapFont *f)
+{
+ if (--f->usage)
+ {
+ /*debug("Losing font %s (still cached)",f->name); */
+ return;
+ }
+ /*debug("Losing font %s (no longer in use)",f->name); */
+ f_free(f->name);
+ f_free(f->bpp_1);
+ if (f->bpp_n)
+ {
+ f_free(f->bpp_n);
+ }
+ memset(f, 0, sizeof(ZapFont));
+}
+
+
+/*
+ | Get a font.
+ */
+static ZapFont *find_font(char *name)
+{
+ ZapFont *f;
+
+ /* Check to see if it's already loaded */
+ f = find_font_by_name(name);
+ if (f)
+ {
+ /*debug("Find font %s (already cached)",name); */
+ f->usage++;
+ if (f == SYSTEM_FONT)
+ {
+ if (!cache_system_font())
+ core("Failed to cache system font!");
+ if (!cache_font_for_mode(SYSTEM_FONT))
+ core("Failed to cache system font!");
+ }
+ return f;
+ }
+
+ /* Ok, now check to see if there's a free slot for it */
+ f = find_free_font();
+ if (!f)
+ {
+ return NULL;
+ } /* Oh dear :( */
+
+ /* Load the font */
+ /*debug("Find font %s (loading)",name); */
+ f = load_font(name, f);
+ if (f)
+ {
+ if (!cache_font_for_mode(f))
+ return NULL;
+ return f;
+ }
+ return NULL;
+}
+
+
+
+
+/*
+ | Cache the n_bpp data for all the active fonts (including system)
+ */
+static void cache_fonts(void)
+{
+ int i;
+ for (i = 0; i <= MAX_TERM_DATA; i++)
+ if (fonts[i].name)
+ if (!cache_font_for_mode(&(fonts[i])))
+ core("Failed to (re)cache font tables");
+}
+
+
+
+
+
+
+typedef struct
+{
+ int load, exec, size, attr, type;
+ char name[4]; /* Actual size is unknown */
+}
+osgbpb10_block;
+
+
+static char *leafname(char *path)
+{
+ char *s = path + strlen(path);
+ while (--s > path)
+ if (*s == '.' || *s == ':')
+ {
+ return s + 1;
+ }
+ return path;
+}
+
+/*
+ | NB: This function is recursive.
+ */
+static menu_ptr make_zfont_menu(char *dir)
+{
+ int entries, entry;
+ int read, offset;
+ unsigned int max_width;
+ menu_ptr m;
+ menu_item *mi;
+ char *temp;
+ osgbpb10_block *item_info;
+ char buffer[1024]; /* 1Kb buffer */
+
+ /* Count the entries in the directory */
+ entries = read = offset = 0;
+ while (offset != -1)
+ {
+ if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0,
+ NULL, NULL, NULL, &read, &offset))
+ {
+ offset = -1;
+ read = 0;
+ }
+ entries += read;
+ }
+
+ if (!entries)
+ {
+ return NULL;
+ }
+
+ /* Allocate a big enough area of storage for the number of entries */
+ m = f_malloc(sizeof(menu_block) + entries * sizeof(menu_item));
+ if (!m)
+ {
+ return NULL;
+ }
+ memset(m, 0, sizeof(menu_block) + entries * sizeof(menu_item));
+
+ /* Set up the menu header */
+ strncpy(m->title, leafname(dir), 12);
+ m->titlefore = 7;
+ m->titleback = 2;
+ m->workfore = 7;
+ m->workback = 0;
+ m->height = 44;
+ m->gap = 0;
+ mi = (menu_item *) (((int)m) + sizeof(menu_block));
+ max_width = strlen(m->title);
+
+ entry = 0;
+
+ /* Read the entries */
+ read = offset = 0;
+ while (offset != -1)
+ {
+ if (SWI(7, 5, SWI_OS_GBPB, 10, dir, buffer, 77, offset, 1024, 0,
+ NULL, NULL, NULL, &read, &offset))
+ {
+ offset = -1;
+ read = 0;
+ /*free(m);return NULL; */
+ }
+
+ item_info = (osgbpb10_block *) buffer;
+
+ /* Create a menu item for each entry read (if it fits) */
+ while (read-- > 0)
+ {
+ switch (item_info->type)
+ {
+ case 1: /* File */
+ if ((item_info->load & 0xffffff00) == 0xfffffd00)
+ {
+ /* Data file */
+ mi[entry].submenu.value = -1;
+ mi[entry].iconflags.data.text = 1;
+ mi[entry].iconflags.data.filled = 1;
+ mi[entry].iconflags.data.foreground = 7;
+ mi[entry].iconflags.data.background = 0;
+ strncpy(mi[entry].icondata.text, item_info->name, 12);
+ if (strlen(mi[entry].icondata.text) > max_width)
+ max_width = strlen(mi[entry].icondata.text);
+ entry++;
+ }
+ break;
+ case 2: /* Directory */
+ case 3: /* Image */
+ {
+ menu_ptr sub;
+ char new_path[260];
+ if (strchr(":.", dir[strlen(dir) - 1]))
+ sprintf(new_path, "%s%s", dir, item_info->name);
+ else
+ sprintf(new_path, "%s.%s", dir, item_info->name);
+ sub = make_zfont_menu(new_path);
+ if (sub)
+ {
+ /* Add the submenu */
+ mi[entry].submenu.menu = sub;
+ mi[entry].iconflags.data.text = 1;
+ mi[entry].iconflags.data.filled = 1;
+ mi[entry].iconflags.data.foreground = 7;
+ mi[entry].iconflags.data.background = 0;
+ strncpy(mi[entry].icondata.text, item_info->name, 12);
+ if (strlen(mi[entry].icondata.text) > max_width)
+ max_width = strlen(mi[entry].icondata.text);
+ entry++;
+ }
+ }
+ break;
+ }
+ temp = ((char *)item_info) + 20;
+ while (*temp++) ;
+ item_info = (osgbpb10_block *) ((((int)temp) + 3) & ~3);
+ }
+ }
+
+ if (entry)
+ {
+ m->width = (max_width + 2) * 16;
+ mi[entry - 1].menuflags.data.last = 1;
+ /*
+ | We could possibly realloc() the storage to fit the
+ | actual no. of entries read, but this is probably more
+ | trouble than it's worth.
+ */
+ }
+ else
+ {
+ /*
+ | No point in returning an empty menu.
+ */
+ f_free(m);
+ m = NULL;
+ }
+
+ return m;
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Initialise the palette stuff
+ */
+static void initialise_palette(void)
+{
+ memset(a_palette, 0, sizeof(a_palette));
+ memset(palette, 0, sizeof(palette));
+ memset(zpalette, 0, sizeof(zpalette));
+}
+
+
+
+#ifndef FULLSCREEN_ONLY
+
+/*
+ | Cache the ZapRedraw palette
+ */
+static void cache_palette(void)
+{
+ static ZapRedrawBlock b;
+ char workspace[128];
+ int i;
+
+ static double old_gamma = -1.0;
+
+ /* Idiocy check: */
+ if (gamma < 0.01)
+ {
+ plog("Internal error: Attempt to apply zero gamma - recovering...");
+ gamma = 1.00;
+ }
+
+ if (gamma != old_gamma)
+ {
+ memset(a_palette, 0, sizeof(a_palette));
+ old_gamma = gamma;
+ }
+
+ /* Go through the palette updating any changed values */
+ for (i = 0; i < 256; i++)
+ {
+ if (COLOUR_CHANGED(i))
+ {
+ int r, g, b;
+ r = (int)(255.0 *
+ pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
+ g = (int)(255.0 *
+ pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
+ b = (int)(255.0 *
+ pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
+ palette[i] = (b << 24) | (g << 16) | (r << 8);
+ a_palette[i][1] = angband_color_table[i][1];
+ a_palette[i][2] = angband_color_table[i][2];
+ a_palette[i][3] = angband_color_table[i][3];
+ }
+ }
+
+ cursor_rgb = palette[CURSOR_COLOUR];
+
+ /* Cache the ZapRedraw palette for it */
+ b.r_workarea = workspace;
+ if (b.r_mode != screen_mode)
+ SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &b);
+ SWI(5, 0, SWI_ZapRedraw_CreatePalette, 2, &b, palette, zpalette, 256);
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Functions for dealing with the SaveBox
+ */
+
+/*
+ | Create the window and claim various handlers for it
+ */
+static void init_save_window(void)
+{
+ /* Create the window */
+ save_box = Window_Create("save", template_TITLEMIN);
+
+ /* Set the file icon */
+ Icon_printf(save_box, SAVE_ICON, "file_%03x", vfiletype);
+
+}
+#endif /* FULLSCREEN_ONLY */
+
+/*
+ | Hack: can't use Str.h without defining HAS_STRICMP. Rather than
+ | require that the header files are altered we simply provide our
+ | own strnicmp() function.
+ */
+static int my_strnicmp(const char *a, const char *b, int n)
+{
+ int i;
+
+ n--;
+
+ for (i = 0; i <= n; i++)
+ {
+ if (tolower((unsigned char)a[i]) != tolower((unsigned char)b[i]))
+ return tolower((unsigned char)a[i]) - tolower((unsigned char)b[i]);
+
+ if (a[i] == '\0')
+ break;
+ }
+
+ return 0;
+}
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | This is the handler called when a 'save' occurrs.
+ | All it does is to update the game's own savefile setting and
+ | then (if possible) save the character.
+ */
+static BOOL SaveHnd_FileSave(char *filename, void *ref)
+{
+ char old_savefile[1024];
+
+ /* Hack: refuse to save if the character is dead */
+ if (PDEADCHK)
+ {
+ Msgs_Report(0, "err.cheat");
+ return FALSE;
+ }
+
+ /* Hack: disallow saves to <Wimp$Scrap>* */
+ if (!my_strnicmp("<wimp$scrap>", filename, 12))
+ {
+ Msgs_Report(0, "err.scrap");
+ return FALSE;
+ }
+
+ /* Preserve the old path, in case something goes wrong... */
+ strcpy(old_savefile, savefile);
+
+ /* Set the new path */
+ strcpy(savefile, unixify_name(filename));
+
+ /* Try a save (if sensible) */
+ if (game_in_progress && character_generated)
+ {
+ if (!save_player())
+ {
+ Msgs_Report(0, "err.save", filename);
+ strcpy(savefile, old_savefile);
+ return FALSE; /* => failure */
+ }
+ }
+
+ /* Set the pathname icon */
+ Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
+
+ /* Kill the menu */
+ Wimp_CreateMenu((menu_block *) - 1, -1, -1);
+
+ return TRUE; /* => Success */
+}
+
+
+
+/*
+ | Init the handlers for the savebox (eg. as a result of a menuwarning
+ | being received for the savebox)
+ */
+
+static void init_savehandlers(void)
+{
+ if (saveblk)
+ {
+ Save_ReleaseSaveHandlers(saveblk);
+ saveblk = 0;
+ }
+
+ saveblk = Save_InitSaveWindowHandler(save_box, /* Window handle */
+ TRUE, /* it's part of a menu */
+ FALSE, /* not a window */
+ FALSE, /* Don't auto release the handlers */
+ SAVE_ICON, /* The file icon */
+ SAVE_OK, /* The OK icon */
+ SAVE_CANCEL, /* The cancel icon */
+ SAVE_PATH, /* The pathname icon */
+ SaveHnd_FileSave, /* Handler to "save the file" */
+ NULL, /* No RAM transfer support */
+ NULL, /* No 'result handler' */
+ 100 << 10, /* Est. size (irelevant anyway) */
+ vfiletype, /* filetype (irelevant) */
+ NULL /* ref */
+ );
+}
+
+
+/*
+ | Handle a MenuWarning message for the savebox
+ */
+static BOOL Hnd_SaveWarning(event_pollblock * pb, void *ref)
+{
+ os_error *e;
+
+ init_savehandlers();
+
+ /* Set the pathname */
+ Icon_printf(save_box, SAVE_PATH, "%s", riscosify_name(savefile));
+
+ /* Open the submenu */
+ e = Wimp_CreateSubMenu((menu_block *) save_box,
+ pb->data.message.data.menuwarn.openpos.x,
+ pb->data.message.data.menuwarn.openpos.y);
+
+ if (e)
+ {
+ Msgs_ReportFatal(0, "err.swi", __LINE__, e->errmess);
+ }
+
+ return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+
+/*
+ | Initialise the r_data array
+ | Mainly we just set up the line offset pointers and make sure that the
+ | lines themselves are 'safe' by writing end-of-line codes to them.
+ */
+static void initialise_r_data(void)
+{
+ int *lo = (int *)r_data;
+ char *ld;
+ int j;
+ for (j = 0; j < 24; j++)
+ {
+ lo[j] = 25 * 4 + (80 * 5 + 4) * j; /* Offset of line */
+ ld = r_data + lo[j];
+ *ld++ = 0; /* 0,2 == */
+ *ld = 2; /* end of line */
+ }
+ lo[j] = 0; /* Terminate line index */
+}
+
+
+
+/*
+ | Create the r_data array for a term
+ | This is typically quite fast (1ms or so on a RPC700)
+ | so we don't bother caching r_data for each term or using the
+ | 'frosh' concept.
+ */
+static void make_r_data(term_data *t)
+{
+ char **c = t->t.old->c; /* char array [24][80] */
+ byte **a = t->t.old->a; /* attr array [24][80] */
+ char *o;
+ int i, j, cf;
+
+/* New code: */
+
+ o = r_data + 25 * 4; /* First byte of r_data after line index */
+
+ if (force_mono)
+ {
+ for (j = 0; j < 24; j++)
+ {
+ /* Set up the line offset entry */
+ ((int *)r_data)[j] = o - r_data;
+
+ for (i = 0; i < 80; i++)
+ *o++ = a[j][i] != TERM_DARK ? c[j][i] : ' ';
+ /* 0,2 => end of line */
+ *o++ = 0;
+ *o++ = 2;
+ }
+ }
+ else
+ {
+ for (j = 0; j < 24; j++)
+ {
+ /* Set up the line offset entry */
+ ((int *)r_data)[j] = o - r_data;
+
+ /* Each line starts in white */
+ cf = TERM_WHITE;
+
+ for (i = 0; i < 80; i++)
+ {
+ if (a[j][i] != cf)
+ {
+ /* 0,6 => change FG */
+ *o++ = 0;
+ *o++ = 6;
+ cf = *o++ = a[j][i];
+ }
+ *o++ = c[j][i];
+ }
+ /* 0,2 => end of line */
+ *o++ = 0;
+ *o++ = 2;
+ }
+ }
+}
+
+
+/*
+ | Set up 'zrb' for the current screen mode.
+ */
+static void set_up_zrb_for_mode(void)
+{
+ static char work_area[4096];
+ zrb.r_workarea = work_area;
+ zrb.r_palette = zpalette;
+ zrb.r_linesp = 0;
+ zrb.r_for = TERM_WHITE;
+ zrb.r_bac = 0;
+ zrb.r_data = r_data;
+ SWI(2, 0, SWI_ZapRedraw_ReadVduVars, 0, &zrb);
+}
+
+
+
+/*
+ | Set up the ZapRedrawBlock ready to redraw term 't'
+ | (caches the r_data as part of the process)
+ */
+static void set_up_zrb(term_data *t)
+{
+ int fw, fh;
+
+ zrb.r_flags.value = 0;
+
+ /* Set font info up */
+ fw = t->font->w;
+ fh = t->font->h;
+ SWI(4, 4, SWI_ZapRedraw_CachedCharSize, zrb.r_bpp, 0, fw, fh,
+ NULL, NULL, &(zrb.r_cbpl), &(zrb.r_cbpc));
+ zrb.r_caddr = (void *)(((int)t->font->bpp_n) - (t->font->f * zrb.r_cbpc));
+
+ zrb.r_charw = fw; /* Character size in pixels */
+ zrb.r_charh = fh;
+
+ if (t->font == SYSTEM_FONT)
+ zrb.r_flags.bits.double_height = screen_eig.y == 1;
+ else
+ zrb.r_flags.bits.double_height = 0;
+
+ make_r_data(t); /* Cache the r_data */
+}
+
+
+
+
+
+static void redraw_window(window_redrawblock * rb, BOOL *more, term_data *t)
+{
+ int cx, cy, cw, ch;
+
+ /* set GCOL for cursor colour */
+ if (t->cursor.visible)
+ {
+ cw = zrb.r_charw << screen_eig.x;
+ ch = -(zrb.r_charh << screen_eig.y);
+ if (zrb.r_flags.bits.double_height)
+ {
+ ch *= 2;
+ }
+ cx = t->cursor.pos.x * cw;
+ cy = t->cursor.pos.y * ch;
+ cx += (rb->rect.min.x - rb->scroll.x);
+ cy += (rb->rect.max.y - rb->scroll.y);
+ cw -= (1 << screen_eig.x);
+ ch += (1 << screen_eig.y);
+ cy -= (1 << screen_eig.y);
+ }
+
+ while (*more)
+ {
+ SWI(2, 0, SWI_ZapRedraw_GetRectangle, rb, &zrb);
+ SWI(2, 0, SWI_ZapRedraw_RedrawArea, NULL, &zrb);
+ if (t->cursor.visible)
+ {
+ ColourTrans_SetGCOL(cursor_rgb, 0, 0);
+ GFX_Move(cx, cy);
+ GFX_DrawBy(cw, 0);
+ GFX_DrawBy(0, ch);
+ GFX_DrawBy(-cw, 0);
+ GFX_DrawBy(0, -ch);
+ }
+ Wimp_GetRectangle(rb, more);
+ }
+}
+
+
+
+
+
+static BOOL Hnd_Redraw(event_pollblock * pb, void *ref)
+{
+ term_data *t = (term_data *)ref;
+ window_redrawblock rb;
+ BOOL more;
+
+ rb.window = t->w;
+ Wimp_RedrawWindow(&rb, &more);
+
+ set_up_zrb(t);
+
+ redraw_window(&rb, &more, t);
+
+ return TRUE;
+}
+
+
+
+
+
+static void refresh_window(term_data *t)
+{
+ window_redrawblock rb;
+ BOOL more;
+ int fw, fh;
+
+ if ((t->changed_box.min.x >= t->changed_box.max.x) ||
+ (t->changed_box.min.y >= t->changed_box.max.y))
+ return;
+
+ set_up_zrb(t);
+
+ fw = zrb.r_charw << screen_eig.x;
+ fh = -(zrb.r_charh << screen_eig.y);
+ if (zrb.r_flags.bits.double_height)
+ {
+ fh *= 2;
+ }
+
+ rb.window = t->w;
+ rb.rect.min.x = fw * t->changed_box.min.x;
+ rb.rect.max.x = fw * t->changed_box.max.x;
+
+ rb.rect.max.y = fh * t->changed_box.min.y;
+ rb.rect.min.y = fh * t->changed_box.max.y;
+
+ Wimp_UpdateWindow(&rb, &more);
+ redraw_window(&rb, &more, t);
+
+ t->changed_box.min.x = t->changed_box.min.y = 255;
+ t->changed_box.max.x = t->changed_box.max.y = 0;
+}
+
+
+
+static void refresh_windows(void)
+{
+ int i;
+ window_info info;
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ info.window = data[i].w;
+ Wimp_GetWindowInfo(&info);
+ if (info.block.flags.data.open)
+ refresh_window(&(data[i]));
+ }
+}
+
+
+/*
+ | Set the size of a window.
+ | If the window grows but has no scroll bars then it is re-sized to
+ | the new extent. If it shrinks then it is resized regardless.
+ |
+ | If the window isn't open then it is opened behind the backwindow,
+ | resized and then closed again... I /did/ have a reason for doing this
+ | rather than simply recreating the window at the new size, but for the
+ | life of me I can't remember what it was...
+ */
+static void set_window_size(window_handle w, int width, int height)
+{
+ window_state ws;
+ int reclose;
+/*
+ * int cw,ch;
+ */
+
+ Wimp_GetWindowState(w, &ws);
+ Window_SetExtent(w, 0, -height, width, 0);
+
+ reclose = !ws.flags.data.open;
+ if (!(ws.flags.value & (0xf << 27)))
+ {
+ if (reclose)
+ {
+ ws.openblock.behind = -3;
+ Wimp_OpenWindow(&(ws.openblock));
+ }
+ /* Removed - caused "pixel creep" :)
+ * cw = ws.openblock.screenrect.max.x;
+ * ch = ws.openblock.screenrect.max.y;
+ * cw -= ws.openblock.screenrect.min.x;
+ * ch -= ws.openblock.screenrect.min.y;
+ * ws.openblock.screenrect.min.x -= ((width-cw)/2)-(1<<screen_eig.x);
+ * ws.openblock.screenrect.min.y -= ((height-ch)/2)-(1<<screen_eig.y);
+ */
+ ws.openblock.screenrect.max.x = ws.openblock.screenrect.min.x + width;
+ ws.openblock.screenrect.max.y = ws.openblock.screenrect.min.y + height;
+ Wimp_OpenWindow(&(ws.openblock));
+ if (reclose)
+ {
+ Wimp_CloseWindow(w);
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*
+ | Change the size of a window to suit the font displayed in it
+ */
+static void resize_term_for_font(term_data *t)
+{
+ int fw, fh;
+ set_up_zrb(t);
+
+ fw = zrb.r_charw << screen_eig.x;
+ fh = zrb.r_charh << screen_eig.y;
+ if (zrb.r_flags.bits.double_height)
+ {
+ fh *= 2;
+ }
+
+ /* Caulculate new size */
+ fw *= 80;
+ fh *= 24;
+
+ set_window_size(t->w, fw, fh);
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+static BOOL Hnd_Caret(event_pollblock * pb, void *ref)
+{
+ if (ref)
+ got_caret = 1;
+ else
+ got_caret = 0;
+ return TRUE;
+}
+
+
+
+
+/*
+ | Attach a (named) font to the specified term.
+ | If 'font' is NULL then the system font is attached.
+ | The bpp_n data is calculated if necessary
+ | returns:
+ | 1 => the font was attached OK
+ | 0 => the system font was substituted
+ */
+static int attach_font_to_term(term_data *t, char *font)
+{
+ if (t->font != SYSTEM_FONT)
+ {
+ lose_font(t->font);
+ }
+ if (font)
+ {
+ t->font = find_font(font);
+ }
+ if (!t->font)
+ {
+ t->font = SYSTEM_FONT;
+ if (font)
+ {
+ Msgs_Report(1, "err.font_l", font);
+ }
+ }
+ else
+ {
+ if (!t->font->bpp_n)
+ {
+ lose_font(t->font);
+ t->font = SYSTEM_FONT;
+ if (font)
+ {
+ Msgs_Report(1, "err.font_c", font);
+ }
+ }
+ }
+ resize_term_for_font(t);
+ return !(t->font == SYSTEM_FONT);
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+
+
+/*
+ | Create a menu of all the (probable!) fonts in the specified location
+ | NB: Any file of type 'data' is considered a font.
+ |
+ | Subdirectories are recursively searched.
+ |
+ | 1.10 - Uses <variant>$FontPaths to get a (space separated) list of paths
+ | to search. For each path name, the menu text will be the name and the
+ | path searched will be <variant>$<name>$FontPath
+ |
+ | Eg. (for angband):
+ | Angband$FontPaths Zap Angband
+ | Angband$Zap$FontPath ZapFonts:
+ | Angband$Angband$FontPath Angband:xtra.fonts.
+ */
+static void make_font_menu(void)
+{
+ char *t;
+ char buffer[260];
+ char menu_buffer[260];
+ int paths;
+ int i;
+ unsigned int max_width;
+ const char *path[64]; /* pointers to path names */
+ menu_item *mi;
+
+ font_menu = NULL;
+
+ /* Get the path (ie. dir) to look under */
+ t = getenv(RISCOS_VARIANT "$FontPaths");
+
+ /* Hack: cope if the path isn't set */
+ if (!t)
+ {
+ t = "";
+ }
+
+ strcpy(buffer, t);
+
+ /*
+ | Count how many paths there are, build an array of pointers to them
+ | and terminate them in the buffer
+ */
+ paths = 1; /* including the system font fake path '<System>' */
+ for (t = buffer; *t; t++)
+ {
+ if (*t == ' ')
+ {
+ *t = 0;
+ }
+ else
+ {
+ if (t == buffer || !t[-1])
+ {
+ path[paths] = t;
+ paths++;
+ }
+ }
+ }
+
+ /*
+ | Create the menu
+ */
+ path[0] = SYSTEM_FONT->name;
+
+ font_menu = f_malloc(sizeof(menu_block) + paths * sizeof(menu_item));
+ if (!font_menu)
+ {
+ core("Out of memory (building font menu)");
+ }
+ memset(font_menu, 0, sizeof(menu_block) + paths * sizeof(menu_item));
+
+ strncpy(font_menu->title, "Fonts", 12);
+ font_menu->titlefore = 7;
+ font_menu->titleback = 2;
+ font_menu->workfore = 7;
+ font_menu->workback = 0;
+ font_menu->height = 44;
+ font_menu->gap = 0;
+ max_width = strlen(font_menu->title);
+
+ mi = (menu_item *) (font_menu + 1);
+
+ for (i = 0; i < paths; i++)
+ {
+ mi[i].submenu.value = -1;
+ mi[i].iconflags.data.text = 1;
+ mi[i].iconflags.data.filled = 1;
+ mi[i].iconflags.data.foreground = 7;
+ mi[i].iconflags.data.background = 0;
+ strncpy(mi[i].icondata.text, path[i], 12);
+ if (strlen(mi[i].icondata.text) > max_width)
+ max_width = strlen(mi[i].icondata.text);
+ }
+ font_menu->width = (max_width + 2) * 16;
+ mi[i - 1].menuflags.data.last = 1;
+
+ /*
+ | Hack: add a dotted line after the system font entry if appropriate
+ */
+ if (paths > 1) mi[0].menuflags.data.dotted = 1;
+
+ /*
+ | Iterate over the paths, building the appropriate submenus
+ */
+ for (i = 1; i < paths; i++)
+ {
+ menu_ptr sub_menu = NULL;
+
+ sprintf(menu_buffer, "%s$%s$FontPath", RISCOS_VARIANT, path[i]);
+ t = getenv(menu_buffer);
+ /* Hack: cope if the path isn't defined */
+ if (!t)
+ {
+ t = "";
+ }
+
+ /* Fudge so that the fontpath can be a path, not just a dir. */
+ strcpy(menu_buffer, t);
+ for (t = menu_buffer; *t > ' '; t++)
+ ;
+ if (t[-1] == '.')
+ {
+ t--;
+ }
+ *t = 0;
+
+ /* Build the menu. Don't bother if the path variable was empty */
+ if (*menu_buffer)
+ sub_menu = make_zfont_menu(menu_buffer);
+
+ if (!sub_menu)
+ {
+ mi[i].iconflags.data.shaded = 1;
+ }
+ else
+ {
+ mi[i].submenu.menu = sub_menu;
+ /* Override the title of the 'root' sub-menu */
+ strncpy(sub_menu->title, path[i], 12);
+ /* Add the submenu to the main menu */
+ }
+ }
+
+ return;
+}
+
+/* ----------------------------------------------- musus, xxxx-xx-xx ---
+ * Create and set up the infobox.
+ * --------------------------------------------------------------------- */
+static void create_info_box(void)
+{
+ info_box = Window_Create("info", template_TITLEMIN);
+ Icon_printf(info_box, 0, "%s %s", VARIANT, VERSION);
+ Icon_SetText(info_box, 2, AUTHORS);
+ Icon_SetText(info_box, 3, PORTERS);
+ Icon_SetText(info_box, 7, PORTVERSION);
+
+ return;
+}
+
+/*
+ | Create the various menus
+ */
+static void init_menus(void)
+{
+ char buffer1[256];
+ char buffer2[32];
+ char *o;
+
+ create_info_box(); /* For the Info> entries */
+ make_font_menu(); /* Make the fonts menu */
+
+ Msgs_Lookup("menu.ibar:Info|>Save As|Full screen,Gamma correction,Sound,"
+ "Windows|Save choices|Quit (& save)", buffer1, 256);
+ ibar_menu = Menu_New(VARIANT, buffer1);
+ if (!ibar_menu) core("Can't create Iconbar menu!");
+
+ Msgs_Lookup("menu.term:Info|>Save As|Font,Windows", buffer1, 256);
+ term_menu = Menu_New(VARIANT, buffer1);
+ if (!term_menu) core("Can't create Term menu!");
+
+#ifndef OLD_TERM_MENU
+ o = buffer1;
+ o += sprintf(buffer1, "%s|", VARIANT);
+ o += sprintf(o, "%s,%s,%s|", angband_term_name[1], angband_term_name[2],
+ angband_term_name[3]);
+ sprintf(o, "%s,%s,%s,%s", angband_term_name[4], angband_term_name[5],
+ angband_term_name[6], angband_term_name[7]);
+#else
+ Msgs_printf(buffer1, "menu.windows:%s|Term-1 (Mirror),Term-2 (Recall),"
+ "Term-3 (Choice)|Term-4,Term-5,Term-6,Term-7", VARIANT);
+#endif
+ Msgs_Lookup("menu.winT:Windows", buffer2, 32);
+ wind_menu = Menu_New(buffer2, buffer1);
+ if (!wind_menu)
+ {
+ core("Can't create Windows menu!");
+ }
+
+ /* Now attach the various submenus to where they belong */
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_INFO, (menu_ptr) info_box);
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_GAMMA, (menu_ptr) gamma_win);
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_SOUND, (menu_ptr) sound_win);
+ Menu_AddSubMenu(ibar_menu, IBAR_MENU_WINDOWS, (menu_ptr) wind_menu);
+ Menu_AddSubMenu(term_menu, TERM_MENU_INFO, (menu_ptr) info_box);
+ Menu_AddSubMenu(term_menu, TERM_MENU_WINDOWS, wind_menu);
+
+ /* Add the savebox */
+ Menu_Warn(ibar_menu, IBAR_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL);
+ Menu_Warn(term_menu, TERM_MENU_SAVE, TRUE, Hnd_SaveWarning, NULL);
+
+ if (font_menu)
+ /* add the submenu */
+ Menu_AddSubMenu(term_menu, TERM_MENU_FONT, font_menu);
+ else
+ /* If the font menu is buggered, shade its entry */
+ /* unticked, shaded */
+ Menu_SetFlags(term_menu, TERM_MENU_FONT, FALSE, TRUE);
+}
+
+
+
+
+static void grab_caret(void)
+{
+ caret_block cb;
+ cb.window = data[0].w;
+ cb.icon = -1;
+ cb.height = 1 << 25; /* Invisible */
+ Wimp_SetCaretPosition(&cb);
+}
+
+
+
+
+/*
+ | (Recursively) clear all ticks from the specified menu
+ */
+static void clear_all_menu_ticks(menu_ptr mp)
+{
+ menu_item *mi = (menu_item *) (mp + 1);
+
+ do
+ {
+ if (mi->menuflags.data.ticked)
+ mi->menuflags.data.ticked = 0;
+ if (mi->submenu.value != -1)
+ clear_all_menu_ticks(mi->submenu.menu);
+ mi++;
+ }
+ while (mi[-1].menuflags.data.last != 1);
+}
+
+
+
+
+
+
+/*
+ | Set the font menu's ticks to match the specifed font name.
+ |
+ | fm is the (sub) menu to scan down (recursing into it's submenus (if any))
+ | fn is the font name to match
+ | prefix is the menu text to be prepended to the menu entries due to
+ | previous menus (eg. "08x16" will cause fn="08x16.fred" to match menu
+ | entry "fred".
+ |
+ | NB: recursive.
+ |
+ */
+
+static void set_font_menu_ticks(menu_ptr fm, char *fn, const char *prefix)
+{
+ char buffer[260];
+ char *b_leaf; /* -> menu 'leaf' text in buffer */
+ int pl; /* prefix string length */
+ menu_item *mi = (menu_item *) (fm + 1);
+
+ strcpy(buffer, prefix);
+ pl = strlen(buffer);
+ b_leaf = buffer + pl;
+
+ do
+ {
+ /* Check for (substring) match */
+ strncpy(b_leaf, mi->icondata.text, 12);
+
+ /* Is it a sub-menu? */
+ if (mi->submenu.value == -1)
+ {
+ /* No - must be an exact match */
+ mi->menuflags.data.ticked = !strcmp(buffer, fn);
+ }
+ else
+ {
+ /* Yes - must be a partial match (with a dot on :) */
+ strcat(b_leaf, ".");
+ mi->menuflags.data.ticked =
+ !strncmp(buffer, fn, pl + strlen(b_leaf));
+ if (mi->menuflags.data.ticked)
+ set_font_menu_ticks(mi->submenu.menu, fn, buffer);
+ else
+ clear_all_menu_ticks(mi->submenu.menu);
+ }
+
+ /* Next item */
+ mi++;
+ }
+ while (mi[-1].menuflags.data.last != 1); /* Until finished */
+}
+
+
+
+
+
+
+
+
+
+
+
+/*
+ | Set ticks, etc. in the term_menu to reflect the current state of the
+ | term 't'
+ */
+static void set_up_term_menu(term_data *t)
+{
+ int i;
+ menu_ptr mp;
+ menu_item *mi;
+ window_state ws;
+
+ /* First of all, set up menu title to be the term's title */
+ strncpy(term_menu->title, t->name, 12);
+
+ /* Now set the ticks in the Windows> submenu (cuz it's easy) */
+ mp = wind_menu; /* Windows submenu */
+ mi = (menu_item *) (mp + 1); /* First entry */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Wimp_GetWindowState(data[i].w, &ws);
+ mi[i].menuflags.data.ticked = ws.flags.data.open;
+ }
+
+ /*
+ | Now, the tricky bit: find out which font is selected in this
+ | term and tick it in the menu. Untick all the rest.
+ */
+ set_font_menu_ticks(font_menu, t->font->name, "");
+
+ /* Shade the 'Save>' entry if saving isn't possible (yet) */
+ if (game_in_progress && character_generated)
+ Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, PDEADCHK);
+ else
+ Menu_SetFlags(term_menu, TERM_MENU_SAVE, 0, TRUE);
+
+}
+
+
+
+/*
+ | Generic 'click' handler for windows - turns a drag on the window
+ | into a move-window style drag.
+ */
+static BOOL Hnd_Click(event_pollblock * pb, void *ref)
+{
+ if (pb->data.mouse.button.data.dragselect ||
+ pb->data.mouse.button.data.dragadjust)
+ {
+ drag_block b;
+ b.window = pb->data.mouse.window;
+ b.screenrect.min.x = b.screenrect.min.y = 0;
+ b.screenrect.max.x = screen_size.x;
+ b.screenrect.max.y = screen_size.y;
+ b.type = drag_MOVEWINDOW;
+ Wimp_DragBox(&b);
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ | Handle a click on a Term window.
+ */
+static BOOL Hnd_TermClick(event_pollblock * pb, void *ref)
+{
+ term_data *t = (term_data *)ref;
+
+ if (pb->data.mouse.button.data.menu)
+ {
+ menu_term = t;
+ set_up_term_menu(t);
+ Menu_Show(term_menu, pb->data.mouse.pos.x - 32,
+ pb->data.mouse.pos.y + 32);
+ }
+ else
+ {
+ grab_caret();
+ Hnd_Click(pb, ref);
+ }
+
+ return TRUE;
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+static void mark_ood(term_data *t, int minx, int miny, int maxx, int maxy)
+{
+ if (t->changed_box.min.x > minx)
+ t->changed_box.min.x = minx;
+ if (t->changed_box.max.x < maxx)
+ t->changed_box.max.x = maxx;
+ if (t->changed_box.min.y > miny)
+ t->changed_box.min.y = miny;
+ if (t->changed_box.max.y < maxy)
+ t->changed_box.max.y = maxy;
+}
+
+
+#ifndef FULLSCREEN_ONLY
+
+/* Check for an event (ie. key press) */
+static errr Term_xtra_acn_check(void)
+{
+ static int last_poll = 0;
+ unsigned int curr_time;
+ int bh, bl;
+
+ /*
+ | Only poll the wimp if there's something in the keyboard buffer
+ | or every 10cs. This is presumably so that we process as many
+ | keypresses as possible before any other application gets a go.
+ */
+
+ /* Check the kbd buffer */
+ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
+ bl = (bl & 0xff) + (bh << 8);
+
+ /* Check how long it is since we last polled */
+ curr_time = Time_Monotonic();
+
+ if ((bl > 0 && got_caret) || ((curr_time - last_poll) > 9))
+ {
+ last_poll = curr_time;
+ Stop_Hourglass;
+ Event_Poll();
+ Start_Hourglass;
+ }
+
+ /*
+ | This allows the user to interrupt the borg.
+ */
+ if (key_pressed) return key_pressed = 0;
+
+ return 1;
+}
+
+
+/*
+ | Wait for an event (ie. keypress)
+ | Note that we idle poll once a second to allow us to implement the
+ | alarm system.
+ */
+static errr Term_xtra_acn_event(void)
+{
+ Stop_Hourglass;
+
+ while (!key_pressed && !fullscreen_font)
+ {
+ Event_PollIdle(100);
+ }
+ Start_Hourglass;
+
+ return key_pressed = 0;
+}
+
+
+
+/* React to changes (eg. palette change) */
+static errr Term_xtra_acn_react(void)
+{
+ int c;
+
+ cache_palette();
+
+ /* Mark the entirety of each window as out of date */
+ for (c = 0; c < MAX_TERM_DATA; c++)
+ mark_ood(&data[c], 0, 0, 80, 24);
+
+ /* Force a redraw of the windows */
+ refresh_windows();
+
+ /* Success */
+ return 0;
+}
+
+
+
+/* Do various things to a term */
+static errr Term_xtra_acn(int n, int v)
+{
+ term_data *t = (term_data *)Term;
+
+ switch (n)
+ {
+ case TERM_XTRA_CLEAR: /* Clear the Term */
+ /*for ( i=0; i<Term->hgt; i++ )
+ t->froshed[i] = 1; */
+ mark_ood(t, 0, 0, Term->wid, Term->hgt);
+ /*refresh_window( t ); - NB: Term isn't actually cleared yet! */
+ /* Success */
+ return 0;
+
+ case TERM_XTRA_EVENT: /* Wait/check for an event */
+ if (v)
+ return Term_xtra_acn_event();
+ else
+ return Term_xtra_acn_check();
+
+ case TERM_XTRA_BORED: /* Bored */
+ return Term_xtra_acn_check();
+
+ case TERM_XTRA_FLUSH: /* Flush input */
+ if (got_caret)
+ {
+ /* 1.21 - Hack: wait until no keys are pressed */
+ if (hack_flush)
+ for (v = 0; v != 0xff;)
+ SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
+ SWI(3, 0, SWI_OS_Byte, 21, 0, 0); /* Flush Kbd buffer */
+ }
+ return 0;
+
+ case TERM_XTRA_FRESH: /* Flush output */
+ refresh_window(t);
+ return 0;
+
+ case TERM_XTRA_FROSH: /* Ensure line 'v' is plotted */
+ /* Doesn't do anything */
+ return 0;
+
+ case TERM_XTRA_SHAPE: /* Set cursor visibility */
+ t->cursor.visible = v ? TRUE : FALSE;
+ mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
+ t->cursor.pos.x + 1, t->cursor.pos.y + 1);
+ refresh_window(t); /* needed? */
+ return 0;
+
+ case TERM_XTRA_NOISE: /* Make a beep */
+ Sound_SysBeep();
+ return 0;
+
+ case TERM_XTRA_REACT: /* React to, eg. palette changes */
+ return Term_xtra_acn_react();
+
+ case TERM_XTRA_DELAY: /* Delay for 'v' ms */
+ if (v > 0)
+ {
+ unsigned int start = Time_Monotonic();
+ v = (v + 5) / 10; /* Round to nearest cs */
+ GFX_Wait();
+ while ((Time_Monotonic() - start) < v)
+ ;
+ }
+ return 0;
+
+ case TERM_XTRA_SOUND: /* Play a sound :) */
+ if (enable_sound)
+ {
+ play_sound(v);
+ }
+ return 0;
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ filing_dirdata directory;
+ filing_direntry *entry;
+
+ scansubdir_max = 0;
+
+ if (Filing_OpenDir(riscosify_name(scansubdir_dir), &directory, sizeof(filing_direntry), readdirtype_DIRENTRY) != NULL)
+ {
+ Error_Report(0, "Couldn't open directory \"%s\"", riscosify_name(scansubdir_dir));
+ return 0;
+ }
+
+ while ((entry = Filing_ReadDir(&directory)) != NULL)
+ {
+ if (entry->objtype == filing_DIRECTORY)
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->name);
+ ++scansubdir_max;
+ }
+ }
+
+ Filing_CloseDir(&directory);
+
+ return 0;
+ }
+
+ /* Return current "time" in milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ Term_xtra_long = Time_Monotonic() * 100;
+
+ return 0;
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ Window_SetTitle(data[0].w, angband_term_name[0]);
+ return 0;
+ }
+
+ default:
+ return 1; /* Unsupported */
+ }
+}
+
+
+
+/* Move (but don't necessarily display) the cursor */
+static errr Term_curs_acn(int x, int y)
+{
+ term_data *t = (term_data *)Term;
+
+ if (t->cursor.visible)
+ mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
+ t->cursor.pos.x + 1, t->cursor.pos.y + 1);
+
+ t->cursor.pos.x = x;
+ t->cursor.pos.y = y;
+
+ if (t->cursor.visible)
+ mark_ood(t, t->cursor.pos.x, t->cursor.pos.y,
+ t->cursor.pos.x + 1, t->cursor.pos.y + 1);
+
+ return 0;
+}
+
+
+
+/*
+ | NB: these two are very simple since we use the Term's contents
+ | directly to generate the r_data for ZapRedraw.
+ */
+
+/* Erase 'n' characters at (x,y) */
+static errr Term_wipe_acn(int x, int y, int n)
+{
+ mark_ood((term_data *)Term, x, y, x + n, y + 1);
+ return 0;
+}
+
+/* Write 'n' characters from 's' with attr 'a' at (x,y) */
+static errr Term_text_acn(int x, int y, int n, byte a, cptr s)
+{
+ mark_ood((term_data *)Term, x, y, x + n, y + 1);
+ return 0;
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+/* Initialise one of our terms */
+static void Term_init_acn(term *t)
+{
+ term_data *term = (term_data *)t;
+
+ /* Ludicrous changed box settings :) */
+ term->changed_box.min.x = 256;
+ term->changed_box.min.y = 256;
+ term->changed_box.max.x = 0;
+ term->changed_box.max.y = 0;
+}
+
+
+
+static void term_data_link(term_data *td, int k)
+{
+ term *t = &(td->t);
+
+ /* Initialise the term */
+ term_init(t, 80, 24, k);
+
+ /* Set flags and hooks */
+ t->attr_blank = TERM_WHITE;
+ t->char_blank = ' ';
+
+ /* Experiment (FS mode requires them) */
+ t->always_text = TRUE;
+ t->never_frosh = TRUE;
+ /* Experiment (FS mode requires them) */
+
+#ifdef FULLSCREEN_ONLY
+ t->wipe_hook = Term_wipe_acnFS;
+ t->xtra_hook = Term_xtra_acnFS;
+ t->curs_hook = Term_curs_acnFS;
+ t->text_hook = Term_text_acnFS;
+#else
+ t->init_hook = Term_init_acn;
+ t->xtra_hook = Term_xtra_acn;
+ t->wipe_hook = Term_wipe_acn;
+ t->curs_hook = Term_curs_acn;
+ t->text_hook = Term_text_acn;
+ t->user_hook = Term_user_acn;
+#endif /* FULLSCREEN_ONLY */
+
+ t->data = td;
+
+ Term_activate(t);
+}
+
+
+#ifndef FULLSCREEN_ONLY
+
+/* Open default windows (ie. as set in choices) at the appropriate sizes */
+static void show_windows(void)
+{
+ int i;
+ for (i = MAX_TERM_DATA; i-- > 0;)
+ {
+ if (!data[i].unopened)
+ {
+ if (data[i].def_open)
+ Window_Show(data[i].w, open_WHEREVER);
+ }
+ else
+ {
+ if (data[i].def_open)
+ {
+ window_openblock ob;
+ ob.window = data[i].w;
+ ob.screenrect = data[i].def_pos;
+ ob.scroll = data[i].def_scroll;
+ ob.behind = -1;
+ Wimp_OpenWindow(&ob);
+ data[i].unopened = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ | 'ref' is used to indicate whether this close is being forced by some other
+ | part of the code (eg. the Windows> submenu code). This is used to modify
+ | the 'adjust doesn't close other terms' behavior.
+ */
+
+static BOOL Hnd_MainClose(event_pollblock * pb, void *ref)
+{
+ int i;
+ window_state ws;
+ mouse_block mb;
+
+ /* New in 1.08: don't close other Terms if closed with adjust */
+ Wimp_GetPointerInfo(&mb);
+ if (ref || mb.button.data.adjust)
+ {
+ Wimp_CloseWindow(data[0].w);
+ }
+ else
+ {
+ /* Close all the terms, but mark the open ones as 'def_open' */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ Wimp_GetWindowState(data[i].w, &ws);
+ if (!ws.flags.data.open)
+ {
+ data[i].def_open = 0;
+ }
+ else
+ {
+ Wimp_CloseWindow(data[i].w);
+ data[i].def_open = 1;
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+
+static BOOL Hnd_PaletteChange(event_pollblock * pb, void *ref)
+{
+ cache_palette();
+ return TRUE;
+}
+
+
+
+static BOOL Hnd_ModeChange(event_pollblock * pb, void *ref)
+{
+ int i;
+ Screen_CacheModeInfo();
+ set_up_zrb_for_mode(); /* (re)set up the redraw block */
+ cache_palette(); /* (re)cache the palette */
+ cache_fonts(); /* (re)cache the fonts */
+ /* Enforce sizes (eg. if screen_eig.y has changed) */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ resize_term_for_font(&(data[i]));
+ return TRUE;
+}
+
+
+
+
+static BOOL Hnd_Keypress(event_pollblock * pb, void *ref)
+{
+ static const char hex[] = "0123456789ABCDEF";
+ int c = pb->data.key.code;
+ /* Check whether this key was pressed in Term 0 */
+ if (pb->data.key.caret.window == data[0].w)
+ {
+ switch (c)
+ {
+ case keycode_F12:
+ case keycode_SHIFT_F12:
+ case keycode_CTRL_F12:
+ case keycode_CTRL_SHIFT_F12:
+ /* Never intercept these */
+ break;
+
+ case 27: /* handle escape specially */
+ if (Kbd_KeyDown(inkey_CTRL))
+ {
+ ack_alarm();
+ return TRUE;
+ }
+
+ /* Send everything else onto the Term package */
+ default:
+ /* Take care of "special" keypresses */
+ switch (c)
+ {
+ case keycode_TAB:
+ {
+ c = '\t';
+ break;
+ }
+
+ case keycode_PAGEUP:
+ {
+ c = '9';
+ break;
+ }
+
+ case keycode_PAGEDOWN:
+ {
+ c = '3';
+ break;
+ }
+
+ case keycode_COPY:
+ {
+ c = '1';
+ break;
+ }
+
+ case keycode_HOME:
+ {
+ c = '7';
+ break;
+ }
+ }
+ /* Pass to the angband engine */
+ /* Allow shift & ctrl to modify the keypad keys */
+ if (c >= '0' && c <= '9')
+ {
+ kbd_modifiers m = Kbd_GetModifiers(FALSE);
+ if (m.shift)
+ {
+ c |= 0x800;
+ }
+ if (m.ctrl)
+ {
+ c |= 0x400;
+ }
+ /* Could maybe add ALT as 0x1000 ??? */
+ }
+
+ /* Keys >255 have to be send as escape sequences (31=escape) */
+ if (c > 255 || c == 31)
+ {
+ Term_keypress(31);
+ Term_keypress(hex[(c & 0xf00) >> 8]);
+ Term_keypress(hex[(c & 0x0f0) >> 4]);
+ Term_keypress(hex[(c & 0x00f)]);
+ c = 13;
+ }
+ Term_keypress(c);
+ key_pressed = 1;
+ /*if ( c==27 ) { escape_pressed = 1; } */
+ return TRUE;
+ }
+ }
+
+ Wimp_ProcessKey(c);
+ return TRUE;
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* Gamma correction window stuff */
+/*--------------------------------------------------------------------------*/
+
+static void redraw_gamma(window_redrawblock * rb, BOOL *more)
+{
+ int i, y, x, h, w;
+ int bx, by;
+ int dither;
+
+ bx = Coord_XToScreen(GC_XOFF, (convert_block *) & (rb->rect));
+ by = Coord_YToScreen(GC_YOFF, (convert_block *) & (rb->rect));
+
+ h = GC_HEIGHT / 4;
+ w = GC_WIDTH / 16;
+
+ x = bx;
+
+ while (*more)
+ {
+ for (i = 0; i < 16; i++)
+ {
+ y = by;
+ for (dither = 0; dither < 2; dither++)
+ {
+ /* Solid block: */
+ ColourTrans_SetGCOL(palette[i], dither << 8, 0);
+ GFX_RectangleFill(x, y, w, -h);
+ y -= h;
+ /* Dot on black: */
+ ColourTrans_SetGCOL(palette[0], dither << 8, 0);
+ GFX_RectangleFill(x, y, w, -h);
+ ColourTrans_SetGCOL(palette[i], dither << 8, 0);
+ GFX_RectangleFill(x + (w / 2) - 2, y - (h / 2), 2, 2);
+ y -= h;
+ }
+ x += w;
+ }
+ Wimp_GetRectangle(rb, more);
+ }
+}
+
+
+static void update_gamma(void)
+{
+ window_redrawblock rb;
+ BOOL more;
+
+ rb.window = gamma_win;
+ rb.rect.min.x = GC_XOFF;
+ rb.rect.min.y = GC_YOFF - GC_HEIGHT;
+ rb.rect.max.y = GC_XOFF + GC_WIDTH + screen_delta.x;
+ rb.rect.max.y = GC_YOFF + screen_delta.y;
+
+ Wimp_UpdateWindow(&rb, &more);
+ if (more)
+ {
+ redraw_gamma(&rb, &more);
+ }
+}
+
+
+
+static BOOL Hnd_RedrawGamma(event_pollblock * pb, void *ref)
+{
+ window_redrawblock rb;
+ BOOL more;
+
+ rb.window = pb->data.openblock.window;
+ Wimp_RedrawWindow(&rb, &more);
+ if (more)
+ {
+ redraw_gamma(&rb, &more);
+ }
+
+ return TRUE;
+}
+
+
+static BOOL Hnd_GammaClick(event_pollblock * pb, void *ref)
+{
+ int up = (ref == 0);
+
+ if (up)
+ {
+ if (gamma < 9.0)
+ {
+ gamma += 0.05;
+ Icon_SetDouble(gamma_win, 0, gamma, 2);
+ Term_xtra_acn_react();
+ update_gamma();
+ }
+ }
+ else
+ {
+ if (gamma > 0.05)
+ {
+ gamma -= 0.05;
+ Icon_SetDouble(gamma_win, GAMMA_ICN, gamma, 2);
+ Term_xtra_acn_react();
+ update_gamma();
+ }
+ }
+
+ /* Hack: if the user menu is active then force it to redraw */
+ if (user_menu_active)
+ {
+ Term_keypress(18);
+ key_pressed = 1;
+ }
+
+ return TRUE;
+}
+
+/*
+ | Reflect the current options in the gamma window
+ */
+static void set_gamma_window_state(void)
+{
+ if (minimise_memory) return;
+
+ Icon_SetDouble(gamma_win, 0, gamma, 2);
+}
+
+
+static void init_gamma_window(void)
+{
+ if (minimise_memory) return;
+
+ Template_UseSpriteArea(resource_sprites);
+ gamma_win = Window_Create("gamma", template_TITLEMIN);
+ Template_UseSpriteArea(NULL);
+ Event_Claim(event_REDRAW, gamma_win, event_ANY, Hnd_RedrawGamma, 0);
+ Event_Claim(event_CLICK, gamma_win, GAMMA_DOWN, Hnd_GammaClick, (void *)1);
+ Event_Claim(event_CLICK, gamma_win, GAMMA_UP, Hnd_GammaClick, (void *)0);
+ set_gamma_window_state();
+}
+
+
+/*--------------------------------------------------------------------------*/
+/* Sound options window stuff */
+/*--------------------------------------------------------------------------*/
+
+static slider_info volume_slider;
+
+
+/*
+ | Reflect the current sound config in the sound options window
+ */
+static void set_sound_window_state(void)
+{
+ if (minimise_memory) return;
+
+ Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
+ Slider_SetValue(&volume_slider, sound_volume, NULL, NULL);
+
+ if (sound_volume > 127)
+ volume_slider.colour.foreground = colour_RED;
+ else
+ volume_slider.colour.foreground = colour_GREEN;
+}
+
+
+
+/*
+ | The sound slider has been dragged, so update the sound volume setting
+ */
+static int update_volume_from_slider(slider_info *si, void *ref)
+{
+ sound_volume = Slider_ReadValue(si);
+
+ if (sound_volume > 127)
+ volume_slider.colour.foreground = colour_RED;
+ else
+ volume_slider.colour.foreground = colour_GREEN;
+
+ return 0;
+}
+
+
+/*
+ | Handle redraw events for the sound options window
+ */
+static BOOL Hnd_RedrawSnd(event_pollblock * pb, void *ref)
+{
+ window_redrawblock redraw;
+ BOOL more;
+
+ redraw.window = pb->data.openblock.window;
+ Wimp_RedrawWindow(&redraw, &more);
+
+ while (more)
+ {
+ Slider_Redraw(((slider_info *) ref), &redraw.cliprect);
+ Wimp_GetRectangle(&redraw, &more);
+ }
+ return (TRUE);
+}
+
+
+/*
+ | Handle clicks on the sound options window
+ */
+static BOOL Hnd_SndClick(event_pollblock * pb, void *ref)
+{
+ int icn = pb->data.mouse.icon;
+ int adj = pb->data.mouse.button.data.adjust;
+ slider_info *si = (slider_info *) ref;
+
+ switch (icn)
+ {
+ /* Bump arrows for the slider: */
+ case SND_VOL_DOWN:
+ adj = !adj;
+
+ case SND_VOL_UP:
+ adj = adj ? -1 : 1;
+ sound_volume += adj;
+ if (sound_volume < SOUND_VOL_MIN)
+ {
+ sound_volume = SOUND_VOL_MIN;
+ }
+ if (sound_volume > SOUND_VOL_MAX)
+ {
+ sound_volume = SOUND_VOL_MAX;
+ }
+ set_sound_window_state();
+ break;
+
+ /* The slider itself */
+ case SND_VOL_SLIDER:
+ Icon_ForceRedraw(sound_win, SND_VOL_SLIDER);
+ Slider_Drag(si, NULL, NULL, NULL);
+ break;
+
+ /* The enable/disable icon */
+ case SND_ENABLE:
+ enable_sound = !enable_sound;
+ Icon_SetSelect(sound_win, SND_ENABLE, enable_sound);
+ if (enable_sound)
+ {
+ initialise_sound();
+ }
+ break;
+ }
+
+ /* Hack: if the user menu is active then force it to redraw */
+ if (user_menu_active)
+ {
+ Term_keypress(18);
+ key_pressed = 1;
+ }
+
+ return TRUE;
+}
+
+
+
+/*
+ | Set the sound options window up, ready to rock :)
+ */
+static void init_sound_window(void)
+{
+ Template_UseSpriteArea(resource_sprites);
+ sound_win = Window_Create("sound", template_TITLEMIN);
+ Template_UseSpriteArea(NULL);
+
+ Event_Claim(event_REDRAW, sound_win, event_ANY, Hnd_RedrawSnd,
+ (void *)&volume_slider);
+ Event_Claim(event_CLICK, sound_win, event_ANY, Hnd_SndClick,
+ (void *)&volume_slider);
+
+ /* Set up the slider info */
+ volume_slider.window = sound_win;
+ volume_slider.icon = SND_VOL_SLIDER;
+ volume_slider.limits.min = SOUND_VOL_MIN;
+ volume_slider.limits.max = SOUND_VOL_MAX;
+ volume_slider.colour.foreground = colour_GREEN;
+ volume_slider.colour.background = colour_WHITE;
+ volume_slider.border.x = 8;
+ volume_slider.border.y = 8;
+ volume_slider.update = update_volume_from_slider;
+
+ set_sound_window_state();
+}
+
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+
+
+
+
+
+
+
+
+
+
+/*
+ | A font has been selected.
+ | At this point, menu_term is a pointer to the term for which the
+ | menu was opened.
+ */
+static void handle_font_selection(int *s)
+{
+ char name[260];
+ os_error *e;
+ char *r;
+ menu_ptr mp = font_menu;
+ int *mis;
+
+ /* Follow the >s to the entry specified */
+ for (mis = s; *mis != -1; mis++)
+ mp = ((menu_item *) (mp + 1))[*mis].submenu.menu;
+
+ /*
+ | Now, check to see if we've hit a leaf entry.
+ | NB: If the entry isn't a leaf entry then the first entry in its submenu
+ | is used instead
+ */
+ if (((int)mp) != -1)
+ {
+ mis[0] = 0;
+ mis[1] = -1;
+ mp = ((menu_item *) (mp + 1))[0].submenu.menu;
+ }
+
+ if (((int)mp) != -1)
+ return;
+
+ e = Wimp_DecodeMenu(font_menu, s, name);
+ if (e)
+ {
+ plog(e->errmess);
+ return;
+ }
+
+ /* Make sure that the string is NULL terminated */
+ for (r = name; *r >= ' '; r++)
+ ;
+ *r = 0;
+
+ attach_font_to_term(menu_term, name);
+ mark_ood(menu_term, 0, 0, 80, 24);
+ refresh_window(menu_term);
+}
+
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+
+static void load_choices(void)
+{
+ FILE *fp = NULL;
+ char *cf;
+ int i;
+ char buffer[260];
+
+ cf = find_choices(FALSE);
+ if (*cf)
+ fp = fopen(cf, "r");
+
+ /* Implement default choices */
+ data[0].def_open = 1;
+ data[0].unopened = 1; /* ie. force def_pos */
+ data[0].def_pos.min.x = (screen_size.x - 1280) / 2;
+ data[0].def_pos.max.x = (screen_size.x + 1280) / 2;
+ data[0].def_pos.min.y = (screen_size.y - 768) / 2 - 32;
+ data[0].def_pos.max.y = (screen_size.y + 768) / 2 - 32;
+ data[0].def_scroll.x = data[0].def_scroll.y = 0;
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ data[i].def_open = 0;
+ data[i].unopened = 1; /* ie. force def_pos */
+ data[i].def_pos.min.x = (screen_size.x - 1280) / 2;
+ data[i].def_pos.max.x = (screen_size.x + 1280) / 2;
+ data[i].def_pos.min.y = (screen_size.y - 768) / 2;
+ data[i].def_pos.max.y = (screen_size.y + 768) / 2;
+ data[i].def_scroll.x = data[i].def_scroll.y = 0;
+ }
+
+ if (fp)
+ {
+ const char *t_;
+ char *o_;
+
+ if (!fgets(buffer, sizeof(buffer), fp))
+ {
+ fclose(fp);
+ return;
+ }
+ if (strcmp(buffer, "[Angband config, Musus' port]\n"))
+ {
+ fclose(fp);
+ return;
+ }
+
+ /* Load choices */
+ while (fgets(buffer, sizeof(buffer), fp))
+ {
+ t_ = strtok(buffer, " "); /* Term number (or keyword, "Gamma", etc.) */
+ o_ = strtok(NULL, "\n"); /* argument string */
+ if (!o_)
+ {
+ o_ = "";
+ } /* missing (or null) argument? */
+ if (t_)
+ {
+ if (!strcmp(t_, "Gamma"))
+ gamma = atof(o_);
+ else if (!strcmp(t_, "Monochrome"))
+ force_mono = !strcmp(o_, "on");
+ else if (!strcmp(t_, "Sound"))
+ enable_sound = !strcmp(o_, "on");
+ else if (!strcmp(t_, "Volume"))
+ sound_volume = atoi(o_);
+ else if (!strcmp(t_, "FullScreen"))
+ start_fullscreen = !strcmp(o_, "on");
+ else if (!strcmp(t_, "Hourglass"))
+ use_glass = !strcmp(o_, "on");
+ else if (!strcmp(t_, "HackFlush"))
+ hack_flush = !strcmp(o_, "on");
+ else if (!strcmp(t_, "AlarmTimeH"))
+ alarm_h = atoi(o_);
+ else if (!strcmp(t_, "AlarmTimeM"))
+ alarm_m = atoi(o_);
+ else if (!strcmp(t_, "AlarmText"))
+ strcpy(alarm_message, o_);
+ else if (!strcmp(t_, "AlarmBeep"))
+ alarm_beep = !strcmp(o_, "on");
+ else if (!strcmp(t_, "AlarmType"))
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ if (!strcmp(alarm_types[i], o_))
+ alarm_type = i;
+ }
+ else if (isdigit((unsigned char)*t_))
+ {
+ int t = atoi(t_);
+ if (t >= 0 && t < MAX_TERM_DATA)
+ {
+ char *f_, *x0_, *y0_, *x1_, *y1_, *sx_, *sy_;
+ o_ = strtok(o_, " "); /* first word */
+ f_ = strtok(NULL, " "); /* font name */
+ x0_ = strtok(NULL, " "); /* x posn (min) */
+ y0_ = strtok(NULL, " "); /* y posn (min) */
+ x1_ = strtok(NULL, " "); /* x posn (max) */
+ y1_ = strtok(NULL, " "); /* y posn (max) */
+ sx_ = strtok(NULL, " "); /* x scroll offset */
+ sy_ = strtok(NULL, "\n"); /* y scroll offset */
+ data[t].def_open = (t == 0) || atoi(o_);
+ data[t].def_pos.min.x = atoi(x0_);
+ data[t].def_pos.min.y = atoi(y0_);
+ data[t].def_pos.max.x = atoi(x1_);
+ data[t].def_pos.max.y = atoi(y1_);
+ data[t].def_scroll.x = atoi(sx_);
+ data[t].def_scroll.y = atoi(sy_);
+ data[t].unopened = 1; /* ie. force def_pos */
+#ifndef FULLSCREEN_ONLY
+ attach_font_to_term(&(data[t]), f_);
+#endif /* FULLSCREEN_ONLY */
+ }
+ }
+ }
+ }
+ fclose(fp);
+ }
+
+#ifndef FULLSCREEN_ONLY
+ /*
+ | Fudge so that the main term is *always* fullsize
+ */
+ {
+ int fw, fh;
+
+ set_up_zrb(&(data[0]));
+ fw = zrb.r_charw << screen_eig.x;
+ fh = zrb.r_charh << screen_eig.y;
+ if (zrb.r_flags.bits.double_height)
+ {
+ fh *= 2;
+ }
+ fw *= 80;
+ fh *= 24;
+ data[0].def_pos.max.x = data[0].def_pos.min.x + fw;
+ data[0].def_pos.max.y = data[0].def_pos.min.y + fh;
+ data[0].def_scroll.x = 0;
+ data[0].def_scroll.y = 0;
+ }
+#endif /* FULLSCREEN_ONLY */
+
+
+}
+
+
+
+
+static void save_choices(void)
+{
+ FILE *fp = NULL;
+ FILE *fpm = NULL;
+ char *cf;
+ int i;
+
+ write_alarm_choices();
+
+ cf = find_choices(TRUE);
+ if (!*cf)
+ {
+ plog("Failed to locate writable choices file!");
+ return;
+ }
+
+ fp = fopen(cf, "w");
+ if (!fp)
+ {
+ plog("Can't write choices file");
+ return;
+ }
+
+ fpm = fopen(find_choices_mirror(), "w");
+
+ f2printf(fp, fpm, "[Angband config, Musus' port]\n");
+ f2printf(fp, fpm, "Gamma %.2lf\n", gamma);
+ f2printf(fp, fpm, "Monochrome %s\n", force_mono ? "on" : "off");
+ f2printf(fp, fpm, "Sound %s\n", enable_sound ? "on" : "off");
+ f2printf(fp, fpm, "Volume %d\n", sound_volume);
+ f2printf(fp, fpm, "FullScreen %s\n", start_fullscreen ? "on" : "off");
+ f2printf(fp, fpm, "Hourglass %s\n", use_glass ? "on" : "off");
+ f2printf(fp, fpm, "HackFlush %s\n", hack_flush ? "on" : "off");
+
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ window_state ws;
+ Wimp_GetWindowState(data[i].w, &ws);
+ f2printf(fp, fpm, "%d %d %s ", i, ws.flags.data.open,
+ data[i].font->name);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.x);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.min.y);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.x);
+ f2printf(fp, fpm, "%d ", ws.openblock.screenrect.max.y);
+ f2printf(fp, fpm, "%d %d\n", ws.openblock.scroll.x,
+ ws.openblock.scroll.y);
+ }
+
+ fclose(fp);
+
+ if (fpm)
+ {
+ fclose(fpm);
+ }
+}
+
+/*
+ | Update the Alarm choices file to reflect changed alarm settings.
+ */
+static void write_alarm_choices(void)
+{
+ FILE *fp;
+ char *cf;
+
+ /* Open the choices file for reading */
+ cf = find_alarmfile(TRUE);
+ if (!*cf)
+ {
+ plog("Can't determine Alarm file location!");
+ return;
+ }
+
+ fp = fopen(cf, "w");
+ if (!fp)
+ {
+ plog("Can't write Alarm file");
+ return;
+ }
+
+ /* Write the new alarm options */
+ fprintf(fp, "AlarmType %s\n", alarm_types[alarm_type]);
+ fprintf(fp, "AlarmTimeH %d\n", alarm_h);
+ fprintf(fp, "AlarmTimeM %d\n", alarm_m);
+ fprintf(fp, "AlarmText %s\n", alarm_message);
+ fprintf(fp, "AlarmBeep %s\n", alarm_beep ? "on" : "off");
+
+ fclose(fp);
+}
+
+/*
+ | Read the Alarm choices file.
+ */
+static void read_alarm_choices(void)
+{
+ char buffer[260];
+ FILE *fp;
+ char *cf;
+
+ cf = find_alarmfile(FALSE);
+ if (!*cf)
+ {
+ return;
+ }
+
+ fp = fopen(cf, "r");
+ if (fp)
+ {
+ const char *t_, *o_;
+ /* Load choices */
+ while (fgets(buffer, sizeof(buffer), fp))
+ {
+ t_ = strtok(buffer, " "); /* Keyword */
+ o_ = strtok(NULL, "\n"); /* argument string */
+ if (!o_)
+ {
+ o_ = "";
+ } /* missing (or null) argument? */
+ if (t_)
+ {
+ if (!strcmp(t_, "AlarmTimeH"))
+ alarm_h = atoi(o_);
+ else if (!strcmp(t_, "AlarmTimeM"))
+ alarm_m = atoi(o_);
+ else if (!strcmp(t_, "AlarmText"))
+ strcpy(alarm_message, o_);
+ else if (!strcmp(t_, "AlarmBeep"))
+ alarm_beep = !strcmp(o_, "on");
+ else if (!strcmp(t_, "AlarmType"))
+ {
+ int i;
+ for (i = 0; i < 4; i++)
+ if (!strcmp(alarm_types[i], o_))
+ alarm_type = i;
+ }
+ }
+ }
+ fclose(fp);
+ }
+}
+
+
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Handle selections from the term menu(s)
+ */
+static BOOL Hnd_TermMenu(event_pollblock * pb, void *ref)
+{
+ mouse_block mb;
+ int i;
+
+ Wimp_GetPointerInfo(&mb);
+
+ switch (pb->data.selection[0])
+ {
+ case TERM_MENU_INFO: /* Info> */
+ break;
+ case TERM_MENU_FONT: /* Font> */
+ /* Sub item selected? */
+ if (pb->data.selection[1] == -1)
+ {
+ break;
+ }
+ handle_font_selection(pb->data.selection + 1);
+ break;
+ case TERM_MENU_WINDOWS: /* Windows> */
+ if (pb->data.selection[1] == -1)
+ {
+ break;
+ }
+ i = pb->data.selection[1];
+ {
+ window_state ws;
+ Wimp_GetWindowState(data[i].w, &ws);
+ if (ws.flags.data.open)
+ {
+ if (!i)
+ Hnd_MainClose(NULL, (void *)TRUE);
+ else
+ Window_Hide(data[i].w);
+ }
+ else
+ {
+ if (!i)
+ {
+ show_windows();
+ grab_caret();
+ }
+ else
+ {
+ if (!data[i].unopened)
+ {
+ Window_Show(data[i].w, open_WHEREVER);
+ }
+ else
+ {
+ window_openblock ob;
+ ob.window = data[i].w;
+ ob.screenrect = data[i].def_pos;
+ ob.scroll = data[i].def_scroll;
+ ob.behind = -1; /* could use data[0].w; ? */
+ Wimp_OpenWindow(&ob);
+ data[i].unopened = 0;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ if (mb.button.data.adjust)
+ {
+ set_up_term_menu(menu_term);
+ Menu_ShowLast();
+ }
+
+ return TRUE;
+}
+
+
+
+
+
+
+/*
+ | Handle selections from the iconbar menu
+ */
+static BOOL Hnd_IbarMenu(event_pollblock * pb, void *ref)
+{
+ mouse_block mb;
+ Wimp_GetPointerInfo(&mb);
+
+ switch (pb->data.selection[0])
+ {
+ case IBAR_MENU_INFO: /* Info> */
+ break;
+ case IBAR_MENU_FULLSCREEN: /* Full screen */
+ /* Do Full Screen mode */
+ enter_fullscreen_mode();
+ break;
+ case IBAR_MENU_GAMMA: /* Gamma correction */
+ break;
+ case IBAR_MENU_SOUND: /* Sound */
+ /*
+ | enable_sound = !enable_sound;
+ | if ( enable_sound ) { initialise_sound(); }
+ | Menu_SetFlags( ibar_menu, IBAR_MENU_SOUND, enable_sound, 0 );
+ | set_sound_window_state();
+ */
+ break;
+ case IBAR_MENU_WINDOWS: /* Windows> */
+ /*
+ | Hack: pass it off as the equivalent selection from
+ | the term menu.
+ */
+ pb->data.selection[0] = TERM_MENU_WINDOWS;
+ return Hnd_TermMenu(pb, ref);
+ break;
+ case IBAR_MENU_SAVECHOICES: /* Save choices */
+ save_choices();
+ break;
+ case IBAR_MENU_QUIT: /* Quit */
+ if (game_in_progress && character_generated)
+ save_player();
+ quit(NULL);
+ break;
+ }
+
+ if (mb.button.data.adjust)
+ Menu_ShowLast();
+
+ return TRUE;
+}
+
+
+
+
+/*
+ * Handler for NULL events (should this check the alarm in the desktop?
+ */
+static BOOL Hnd_null(event_pollblock *event, void *ref)
+{
+ /* Really no need to check the alarm more than once per second. */
+ if (alarm_type && Time_Monotonic() > alarm_lastcheck + 100)
+ {
+ check_alarm();
+ }
+
+ return TRUE;
+}
+
+
+
+
+
+
+
+
+static BOOL Hnd_MenuSel(event_pollblock * pb, void *ref)
+{
+ if (menu_currentopen == ibar_menu)
+ return Hnd_IbarMenu(pb, ref);
+ else if (menu_currentopen == term_menu)
+ return Hnd_TermMenu(pb, ref);
+ return FALSE;
+}
+
+
+static BOOL Hnd_IbarClick(event_pollblock * pb, void *ref)
+{
+ if (pb->data.mouse.button.data.menu)
+ {
+ set_gamma_window_state();
+ set_sound_window_state();
+
+ /* Hack: shade the Save> option if appropriate */
+ if (game_in_progress && character_generated)
+ Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, PDEADCHK);
+ else
+ Menu_SetFlags(ibar_menu, IBAR_MENU_SAVE, 0, TRUE);
+
+ /*
+ | Hack: set up the Term menu as if it was opened over the main
+ | window (so that the Windows> submenu is set correctly)
+ */
+ menu_term = (term_data *)&data[0];
+ set_up_term_menu(menu_term);
+
+ Menu_Show(ibar_menu, pb->data.mouse.pos.x, -1);
+ return TRUE;
+ }
+
+ if (pb->data.mouse.button.data.select)
+ {
+ show_windows();
+ grab_caret();
+ return TRUE;
+ }
+
+ if (pb->data.mouse.button.data.adjust)
+ {
+ enter_fullscreen_mode();
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+
+
+/*
+ | Handler for PreQuit messages (eg. at shutdown).
+ */
+static BOOL Hnd_PreQuit(event_pollblock * b, void *ref)
+{
+ BOOL shutdown = (b->data.message.data.words[0] & 1) == 0;
+ task_handle originator = b->data.message.header.sender;
+ unsigned int quitref;
+ message_block mb;
+ char buffer1[64];
+ os_error e;
+ int ok;
+
+ if (!(game_in_progress && character_generated))
+ return TRUE; /* ignore, we're OK to die */
+
+ /* Stop the shutdown/quit */
+ memcpy(&mb, &(b->data.message), 24);
+ quitref = mb.header.yourref;
+ mb.header.yourref = mb.header.myref;
+ Wimp_SendMessage(event_ACK, &mb, originator, 0);
+
+ /*
+ | We handle this differently depending on the version of the Wimp;
+ | newer versions give us much more flexibility.
+ */
+ if (event_wimpversion < 350)
+ {
+ /*
+ | Older versions - use 'OK' and 'Cancel'.
+ | There is no "Save & Quit" button.
+ */
+ Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
+ sprintf(buffer1, e.errmess, VARIANT);
+ Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
+ e.errmess, 260);
+ e.errnum = 0;
+ SWI(3, 2, SWI_Wimp_ReportError, &e, 3 | 16, buffer1, NULL, &ok);
+
+ if (ok != 1)
+ return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */
+ }
+ else
+ {
+ /*
+ | Newer version: can add buttons to the dialog.
+ | we add a 'Save and Quit' button to allow the shutdown to
+ | continue /after/ saving.
+ */
+ int flags;
+ char buttons[64];
+
+ Msgs_Lookup("err.shutbuts:Save & quit,Don't quit,Quit anyway",
+ buttons, 64);
+ Msgs_Lookup("err.shuttitl:Query from %s", e.errmess, 64);
+ sprintf(buffer1, e.errmess, VARIANT);
+ Msgs_Lookup("err.shutdown:Unsaved game; are you sure you want to quit?",
+ e.errmess, 260);
+ e.errnum = 0;
+
+ flags = 0 | 16 | 256 | (4 << 9);
+
+ SWI(6, 2, SWI_Wimp_ReportError, &e, flags, buffer1, ICONNAME, 0,
+ buttons, NULL, &ok);
+
+ if (ok == 4)
+ return TRUE; /* no! Pleeeeeease don't kill leeeeddle ol' me! */
+
+ if (ok == 3)
+ save_player(); /* Save & Quit */
+ }
+
+
+ /* RO2 doesn't use the shudown flag */
+ if (shutdown && event_wimpversion >= 300 && mb.header.size >= 24)
+ {
+ key_block kb;
+ kb.code = 0x1fc; /* restart shutdown sequence */
+ Wimp_SendMessage(event_KEY, (message_block *) & kb, originator, 0);
+ }
+
+ /* "Time... to die." */
+ Event_CloseDown();
+ exit(0);
+ return TRUE; /* The one great certainty (sic) */
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+
+
+static void initialise_terms(void)
+{
+ char t[80];
+ int i;
+
+#ifndef FULLSCREEN_ONLY
+ if (!minimise_memory)
+ {
+ /* Create a window for each term. Term 0 is special (no scroll bars) */
+ data[0].w = Window_Create("angband", sizeof(angband_term_name[0]));
+ data[0].font = SYSTEM_FONT;
+ data[0].def_open = 1;
+ data[0].unopened = 1;
+ sprintf(t, "%s %s", VARIANT, VERSION);
+ Window_SetTitle(data[0].w, t);
+ strncpy(data[0].name, VARIANT, 12);
+ Event_Claim(event_KEY, data[0].w, event_ANY, Hnd_Keypress,
+ (void *)&(data[0]));
+ Event_Claim(event_REDRAW, data[0].w, event_ANY, Hnd_Redraw,
+ (void *)&(data[0]));
+ Event_Claim(event_CLICK, data[0].w, event_ANY, Hnd_TermClick,
+ (void *)&(data[0]));
+ Event_Claim(event_CLOSE, data[0].w, event_ANY, Hnd_MainClose, NULL);
+
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ data[i].w = Window_Create("term", template_TITLEMIN);
+ data[i].font = SYSTEM_FONT;
+ data[i].def_open = 0;
+ data[i].unopened = 1;
+#ifndef OLD_TERM_MENU
+ sprintf(t, "%s (%s %s)", angband_term_name[i], VARIANT, VERSION);
+#else
+ sprintf(t, "Term-%d (%s %s)", i, VARIANT, VERSION);
+#endif
+ Window_SetTitle(data[i].w, t);
+ strncpy(data[i].name, t, 12);
+ Event_Claim(event_CLICK, data[i].w, event_ANY, Hnd_TermClick,
+ (void *)&(data[i]));
+ Event_Claim(event_REDRAW, data[i].w, event_ANY, Hnd_Redraw,
+ (void *)&(data[i]));
+ }
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ term_data_link(&(data[0]), 256);
+
+ for (i = 1; i < MAX_TERM_DATA; i++)
+ {
+ term_data_link(&(data[i]), 16);
+ angband_term[i] = &(data[i].t);
+ }
+
+ angband_term[0] = &(data[0].t);
+ Term_activate(&(data[0].t));
+}
+
+
+
+
+/*
+ | Hack(ish) - determine the version of RISC OS
+ */
+static int os_version(void)
+{
+ int osv;
+ SWI(3, 2, SWI_OS_Byte, 129, 0, 255, NULL, &osv);
+ switch (osv)
+ {
+ case 0xa0: return 120;
+ case 0xa1: return 200;
+ case 0xa2: return 201;
+ case 0xa3: return 300;
+ case 0xa4: return 310;
+ case 0xa5: return 350;
+ case 0xa6: return 360;
+ case 0xa7: return 370;
+ case 0xa8: return 400;
+ default: return 370;
+ }
+ return -1; /* -sigh- */
+}
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Determine whether the current screen mode is "high-res"
+ | (ie. should we use the "Sprites22" or the "Sprites" file.
+ */
+static int sprites22(void)
+{
+ int yeig;
+
+ OS_ReadModeVariable(-1, modevar_YEIGFACTOR, &yeig);
+
+ return yeig < 2;
+}
+
+/*
+ | Determine whether we should use 2D or 3D templates.
+ | 2D templates *must* be used under Wimp <3.00, but under RO3 we
+ | use the CMOS settings to decide.
+ */
+static int templates2d(void)
+{
+ int r1, r2;
+
+ if (event_wimpversion < 300)
+ return TRUE;
+
+ /* The 3D bit is bit 0 of byte 140 */
+ OS_Byte(osbyte_READCMOSRAM, 140, 0, &r1, &r2);
+
+ return !(r2 && 1);
+}
+#endif /* FULLSCREEN_ONLY */
+
+
+
+static unsigned int htoi(char *s)
+{
+ static const char hex[] = "0123456789ABCDEF";
+ unsigned int v = 0;
+ while (*s)
+ {
+ char *m;
+ int d = toupper((unsigned char)*s++);
+ m = strchr(hex, d);
+ if (!m)
+ {
+ return v;
+ }
+ v = (v << 4) + (m - hex);
+ }
+ return v;
+}
+
+static int read_unsigned(char *t)
+{
+ int r;
+ if (SWI(2, 3, SWI_OS_ReadUnsigned, 2, t, NULL, NULL, &r))
+ r = 0;
+ return r;
+}
+
+/*
+ | Scan the string at 'n', replacing dodgy characters with underbars
+ */
+static void sanitise_name(char *n)
+{
+ for (; *n; n++)
+ {
+ if (strchr("\"$%^&*\\\'@#.,", *n))
+ *n = '_';
+ }
+}
+
+
+/*
+ | Ensure that the path to a given object exists.
+ | Ie. if |p| = "a.b.c.d" then we attempt to
+ | create directories a, a.b and a.b.c if they don't
+ | already exist.
+ | Note that 'd' may be absent.
+ */
+static int ensure_path(char *p)
+{
+ char tmp[260];
+ char *l = tmp;
+
+ while (*p)
+ {
+ if (*p == '.')
+ {
+ *l = 0;
+ if (SWI(5, 0, SWI_OS_File, 8, tmp, 0, 0, 77))
+ return 0; /* Eeek! */
+ }
+ *l++ = *p++;
+ }
+
+ return 1;
+}
+
+
+/*
+ * Set up the Scrap, Choices and Alarm paths, trying for
+ * Choices:blah...,etc. by preference, but falling back on lib/xtra
+ * if need be.
+ */
+static void init_paths(void)
+{
+ char tmp[512];
+ char subpath[128];
+ char *v;
+ char *t;
+
+ /* Form the sub-path we use for both Choices and Scrap dirs: */
+ v = subpath + sprintf(subpath, "%s", VARIANT);
+ sanitise_name(subpath);
+ sprintf(v, ".%s", VERSION);
+ sanitise_name(v + 1);
+
+ /* Do the Scrap path first: */
+ *scrap_path = 0;
+
+ /* Try for Wimp$ScrapDir... */
+ t = getenv("Wimp$ScrapDir");
+ if (t && *t)
+ {
+ sprintf(tmp, "%s.AngbandEtc.%s.", t, subpath);
+ if (ensure_path(tmp))
+ {
+ strcpy(scrap_path, tmp);
+ }
+ }
+
+ /* Couldn't use Wimp$ScrapDir, so fall back on lib.xtra.scrap */
+ if (!*scrap_path)
+ {
+ sprintf(tmp, "%sxtra.scrap.", resource_path);
+ if (ensure_path(tmp))
+ {
+ strcpy(scrap_path, tmp);
+ }
+ }
+
+ /* Now set up the Choices and Alarm files: */
+
+ /* Read only Choices file is always lib.xtra.Choices */
+ sprintf(choices_file[CHFILE_READ], "%sXtra.Choices", resource_path);
+ /* Default writable Choices file is the same */
+ strcpy(choices_file[CHFILE_WRITE], choices_file[CHFILE_READ]);
+ /* No default mirror Choices file */
+ strcpy(choices_file[CHFILE_MIRROR], "");
+
+ /* Read only Alarm file is always lib.xtra.Alarm */
+ sprintf(alarm_file[CHFILE_READ], "%sXtra.Alarm", resource_path);
+ /* Default writable Alarm file is the same */
+ strcpy(alarm_file[CHFILE_WRITE], alarm_file[CHFILE_READ]);
+
+ /* Try to use Choices$Path, etc. for the others... */
+
+ t = getenv("Choices$Write"); /* Ie. where choices should be written */
+ if (t && *t)
+ {
+ /* Choices file: */
+ sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
+ if (ensure_path(tmp))
+ {
+ /* Use for writable file: */
+ strcpy(choices_file[CHFILE_WRITE], tmp);
+ /* Form 'mirror' filename: same path but with a fixed leafname */
+ strcpy(v + 1, "Default");
+ sprintf(tmp, "%s.AngbandEtc.%s", t, subpath);
+ strcpy(choices_file[CHFILE_MIRROR], tmp);
+ }
+
+ /* Alarm file (doesn't involve subpath) */
+ sprintf(tmp, "%s.AngbandEtc.Global.Alarm", t);
+ if (ensure_path(tmp))
+ {
+ /* Use for read/writable file */
+ strcpy(alarm_file[CHFILE_WRITE], tmp);
+ }
+ }
+}
+
+
+
+
+
+
+
+/*
+ * Return the appropriate (full) pathname.
+ *
+ * For write ops, the read/write file is returned.
+ *
+ * For read ops, either the read/write file, the mirror file,
+ * or the read only file will be returned as appropriate.
+ */
+static char *find_choices(int write)
+{
+ if (write)
+ return choices_file[CHFILE_WRITE];
+
+ if (myFile_Size(choices_file[CHFILE_WRITE]) > 0)
+ return choices_file[CHFILE_WRITE];
+
+ if (myFile_Size(choices_file[CHFILE_MIRROR]) > 0)
+ return choices_file[CHFILE_MIRROR];
+
+ return choices_file[CHFILE_READ];
+}
+
+static char *find_choices_mirror(void)
+{
+ return choices_file[CHFILE_MIRROR];
+}
+
+static char *find_alarmfile(int write)
+{
+ if (write)
+ return alarm_file[CHFILE_WRITE];
+
+ if (myFile_Size(alarm_file[CHFILE_WRITE]) > 0)
+ return alarm_file[CHFILE_WRITE];
+
+ return alarm_file[CHFILE_READ];
+}
+
+
+
+
+int main(int argc, char *argv[])
+{
+ int i, j;
+ int start_full = 0;
+ char *arg_savefile = 0;
+ char *t;
+#ifdef USE_DA
+ int da_font = 1, da_game = 1;
+#endif
+
+ atexit(final_acn); /* "I never did care about the little things." */
+
+ Start_Hourglass;
+
+ /* Parse arguments */
+ for (i = 1; i < argc; i++)
+ {
+ if (argv[i][0] == '-')
+ {
+ switch (tolower((unsigned char)argv[i][1]))
+ {
+ case 'm':
+ {
+ /* Minimise Memory */
+ minimise_memory = 1;
+
+ /* Break */
+ break;
+ }
+ case 'c': /* -c[a][s][f][<n>] */
+ for (j = 2; argv[i][j]; j++)
+ {
+ int on = isupper((unsigned char)argv[i][j]);
+
+ switch (tolower((unsigned char)argv[i][j]))
+ {
+#ifdef ABBR_FILECACHE
+ case 'a': abbr_filecache =
+ on;
+ break;
+ case 'f': abbr_tmpfile =
+ on;
+ break;
+#endif
+#ifdef SMART_FILECACHE
+ case 's': smart_filecache =
+ on;
+ break;
+#endif
+
+ case 'p': flush_scrap =
+ !on;
+ break;
+
+ default:
+ if (isdigit((unsigned char)argv[i][j]))
+ {
+ max_file_cache_size =
+ atoi(argv[i] + j) << 10;
+ while (isdigit((unsigned char)argv[i][++j]))
+ ;
+ if (max_file_cache_size <= 0)
+ {
+ use_filecache = 0;
+ }
+ j--;
+ }
+ else
+ {
+ fprintf(stderr, "Unrecognised option: -c%s",
+ argv[i] + j);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+ break;
+ case 'w': /* -waitrelease */
+ hack_flush = 1;
+ break;
+ case 'e': /* -Evil */
+ allow_iclear_hack = 1;
+ break;
+ case 's': /* -s<savefile> */
+ if (argv[i][2])
+ arg_savefile = argv[i] + 2;
+ break;
+ case 'f': /* -fullscreen */
+ start_full = 1;
+ break;
+ case 'h': /* -hourglass */
+ use_glass = 1;
+ break;
+ case 't': /* -T<filetype> */
+ if (argv[i][2])
+ vfiletype = htoi(argv[i] + 2);
+ break;
+#ifdef USE_DA
+ case 'd': /* -df, -dg, -dc or -d : disable DAs */
+ switch (tolower((unsigned char)argv[i][2]))
+ {
+ case 0: /* -d => disable both */
+ da_font = da_game = 0;
+ break;
+ case 'f': /* -df => disable font only */
+ da_font = 0;
+ break;
+ case 'g': /* -dg => disable game only */
+ da_game = 0;
+ break;
+ }
+ break;
+#endif
+ case '%': /* -%<debug_opts> */
+ {
+ int v = read_unsigned(argv[i] + 2);
+ log_g_malloc = v & 1;
+ show_sound_alloc = v & 2;
+ }
+ break;
+ default:
+ fprintf(stderr, "Unrecognised option: %s", argv[i]);
+ exit(EXIT_FAILURE);
+ }
+ }
+ }
+
+ /* 1.27 - new handling of -minimise-memory: */
+#ifndef FULLSCREEN_ONLY
+ if (minimise_memory)
+#endif /* FULLSCREEN_ONLY */
+ {
+ start_full = 1;
+ fs_quit_key_text = "(fullscreen only mode)";
+ }
+#ifdef USE_DA
+ init_memory(da_font, da_game); /* Set up dynamic areas, etc. if possible */
+
+ /* Install memory allocation hooks */
+ ralloc_aux = g_malloc;
+ rnfree_aux = g_free;
+#endif
+
+ /* Install replacement error reporting routines */
+ quit_aux = quit_hook;
+ plog_aux = plog_hook;
+ core_aux = core_hook;
+
+ /* Expand the (Angband) resource path */
+ t = getenv(RISCOS_VARIANT "$Path");
+ if (!t || !*t) Msgs_ReportFatal(0, "A resources path could not be formed.");
+ strcpy(resource_path, t);
+
+ /* Decide where scrap, choices and alarm files live: */
+ init_paths();
+
+ /* Hack: if no savefile specified, use a default */
+ if (!arg_savefile)
+ {
+ arg_savefile = malloc(strlen(resource_path) + 32);
+ if (!arg_savefile)
+ {
+ Msgs_ReportFatal(0, "err.mem");
+ }
+ sprintf(arg_savefile, "%s%s", resource_path, ">Save.Savefile");
+ }
+
+ /* This crap appears here so that plog() will work properly before
+ init_acn() is called... */
+ Resource_Initialise(RISCOS_VARIANT);
+ Msgs_LoadFile("Messages");
+
+#ifndef FULLSCREEN_ONLY
+ if (!minimise_memory)
+ {
+ /* This is a hack to only call Event_Initialise3 under RO3 */
+ if (os_version() < 300)
+ Event_Initialise(RISCOS_VARIANT);
+ else
+ Event_Initialise3(RISCOS_VARIANT, 300, message_list);
+
+ EventMsg_Initialise();
+
+ /*
+ | This is a possible workaround for the FP regs getting
+ | bolloxed in the ! menu because the compiler sets them
+ | up before a call to Wimp_Poll if CSE optimisation is on.
+ | At the moment I've just turned off CSE for the function
+ | affected.
+ |
+ | event_mask.data.keepfpregisters = 1;
+ */
+
+ /* Load Templates */
+ Template_Initialise();
+ if (templates2d())
+ Template_LoadFile("Templates2");
+ else
+ Template_LoadFile("Templates");
+
+ /* Load Sprites */
+ if (sprites22())
+ resource_sprites =
+ Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites22");
+ else
+ resource_sprites = Sprite_LoadFile("<" RISCOS_VARIANT "$Dir>.Sprites");
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ Screen_CacheModeInfo();
+
+ /* Initialise some ZapRedraw stuff */
+ initialise_palette();
+ initialise_fonts(); /* Set up the fonts */
+#ifndef FULLSCREEN_ONLY
+ initialise_r_data(); /* Set up the r_data buffer */
+
+ if (!minimise_memory)
+ {
+ /* Initialise some Wimp specific stuff */
+ init_gamma_window();
+ init_sound_window();
+ init_save_window();
+ init_menus();
+ ibar_icon = Icon_BarIcon(ICONNAME, iconbar_RIGHT);
+
+ /* Global handlers */
+ Event_Claim(event_OPEN, event_ANY, event_ANY, Handler_OpenWindow, NULL);
+ Event_Claim(event_CLOSE, event_ANY, event_ANY, Handler_CloseWindow, NULL);
+ Event_Claim(event_GAINCARET, event_ANY, event_ANY, Hnd_Caret, (void *)1);
+ Event_Claim(event_LOSECARET, event_ANY, event_ANY, Hnd_Caret, (void *)0);
+ Event_Claim(event_MENU, event_ANY, event_ANY, Hnd_MenuSel, NULL);
+ Event_Claim(event_CLICK, window_ICONBAR, ibar_icon, Hnd_IbarClick, NULL);
+ Event_Claim(event_CLICK, event_ANY, event_ANY, Hnd_Click, NULL);
+ EventMsg_Claim(message_PALETTECHANGE, event_ANY, Hnd_PaletteChange, NULL);
+ EventMsg_Claim(message_MODECHANGE, event_ANY, Hnd_ModeChange, NULL);
+ EventMsg_Claim(message_PREQUIT, event_ANY, Hnd_PreQuit, NULL);
+
+ /* Initialise the sound stuff */
+ initialise_sound();
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ /* Initialise some Angband stuff */
+ initialise_terms();
+ load_choices();
+ read_alarm_choices();
+ init_file_paths(unixify_name(resource_path));
+
+ Start_Hourglass; /* Paranoia */
+
+ /* Hack - override the saved options if -F was on the command line */
+ start_fullscreen |= start_full;
+
+ /* hack so that the cursor is yellow if undefined */
+ if (palette[CURSOR_COLOUR] == palette[0])
+ {
+ angband_color_table[CURSOR_COLOUR][1] = (CURSOR_RGB & 0xff00) >> 8;
+ angband_color_table[CURSOR_COLOUR][2] = (CURSOR_RGB & 0xff0000) >> 16;
+ angband_color_table[CURSOR_COLOUR][3] = (CURSOR_RGB & 0xff000000) >> 24;
+ }
+
+ /* Catch nasty signals */
+ signals_init();
+ /* use pref-acn.prf */
+ ANGBAND_SYS = "acn";
+
+#ifndef FULLSCREEN_ONLY
+ if (start_fullscreen)
+ {
+#endif /* FULLSCREEN_ONLY */
+ enter_fullscreen_mode();
+#ifndef FULLSCREEN_ONLY
+ }
+ else
+ {
+ Start_Hourglass; /* Paranoia */
+ Hnd_ModeChange(NULL, NULL); /* Caches the various fonts/palettes */
+ show_windows();
+ grab_caret();
+
+ Event_Claim(event_NULL, event_ANY, event_ANY, Hnd_null, NULL);
+
+ /* Wait for a null poll so that the windows can appear */
+ do
+ {
+ Event_Poll();
+ }
+ while (event_lastevent.type != 0);
+ }
+#endif /* FULLSCREEN_ONLY */
+
+ /* Initialise Angband */
+ Start_Hourglass; /* Paranoia */
+
+ strncpy(savefile, unixify_name(arg_savefile), sizeof(savefile));
+ savefile[sizeof(savefile) - 1] = '\0';
+
+ use_sound = 1;
+ init_angband();
+ initialised = 1;
+ game_in_progress = 1;
+/* pause_line(23);*/
+ flush();
+ /* Stop_Hourglass; */
+ play_game(FALSE);
+
+ if (fullscreen_mode) leave_fullscreen_mode();
+
+ Stop_Hourglass;
+
+ quit(NULL);
+
+ return 0;
+
+ debug("to stop the 'unused' warning :)");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Stuff below here is for the full screen display */
+/*--------------------------------------------------------------------------*/
+
+static errr Term_xtra_acn_checkFS(void);
+static errr Term_xtra_acn_eventFS(void);
+static errr Term_xtra_acn_reactFS(int force);
+static errr Term_curs_acnFS(int x, int y);
+static errr Term_xtra_acn_clearFS(void);
+static errr Term_xtra_acnFS(int n, int v);
+static errr Term_wipe_acnFS(int x, int y, int n);
+static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s);
+static void bored(void);
+static void redraw_areaFS(int x, int y, int w, int h);
+static void draw_cursor(int x, int y);
+
+/*
+ | We use this to keep the mouse in the same place on return from full-screen
+ | mode.
+ */
+static wimp_point old_mouse_posn;
+
+/*
+ | Take a copy of the current mode descriptor/number and return either
+ | a pointer to it (as an int) if it's a new mode, or the mode number.
+ | NB: A static pointer is used and the descriptor returned is only
+ | valid until the next call to this function.
+ |
+ | Basically, a replacement for OS_Byte 135 / OS_ScreenMode1, IYSWIM.
+ */
+static int current_mode(void)
+{
+ static void *descriptor = NULL;
+ int mode;
+ int size;
+ int i;
+ int *vals;
+
+ if (descriptor)
+ {
+ free(descriptor);
+ descriptor = NULL;
+ }
+
+ SWI(1, 3, SWI_OS_Byte, 135, NULL, NULL, &mode);
+ if (mode < 256)
+ {
+ return mode;
+ }
+
+ vals = (int *)(mode + 20);
+ for (i = 0; vals[i] != -1; i += 2)
+ ;
+
+ size = 24 + 8 * i; /* Size of data */
+ descriptor = malloc(size);
+ if (!descriptor)
+ {
+ core("Out of memory!");
+ }
+ memcpy(descriptor, (void *)mode, size);
+
+ return (int)descriptor;
+}
+
+
+
+/*
+ | Select the best mode we can for full screen.
+ | Returns 12 for (low-res, ie. mode 12) or 27 for high-res,
+ | or a pointer to a mode descriptor (as an int).
+ */
+static int select_fullscreen_mode(void)
+{
+ static struct
+ {
+ int flags, x, y, l2bpp, hz, term;
+ }
+ desc;
+ int mode = 0;
+
+ desc.flags = 1; /* format 0 */
+ desc.x = 640;
+ desc.y = 480; /* 640x480 */
+ desc.l2bpp = 2; /* 16 colours */
+ desc.hz = -1; /* best we can get */
+ desc.term = -1; /* don't fuss about modevars */
+
+ SWI(1, 1, SWI_OS_CheckModeValid, &desc, &mode);
+ if (mode != (int)&desc)
+ {
+ SWI(1, 1, SWI_OS_CheckModeValid, 27, /**/ &mode);
+ if (mode != 27)
+ {
+ SWI(1, 1, SWI_OS_CheckModeValid, 12, /**/ &mode);
+ if (mode != 12)
+ {
+ mode = 0;
+ }
+ }
+ }
+
+ return mode;
+}
+
+
+/*
+ | Change screen mode
+ */
+static void change_screenmode(int to)
+{
+ if (SWI(2, 0, SWI_OS_ScreenMode, 0, to))
+ {
+ if (to < 256)
+ {
+ GFX_VDU(22);
+ GFX_VDU(to);
+ }
+ else
+ {
+ /* Finished with my woman / cos she couldn't help me with ... */
+ core("Eeek! mode isn't valid, but it /should/ be...");
+ }
+ }
+}
+
+
+/*
+ | Constrain the mouse pointer to a point - this means that the damn
+ | hourglass won't move around with the mouse :)
+ */
+static void constrain_pointer(void)
+{
+ mouse_block ptr;
+ wimp_rect r;
+ int ys = screen_eig.y == 1 ? 32 : 64; /* Cope with dbl height glass */
+
+ Screen_CacheModeInfo(); /* Make sure we know the screen size */
+ r.min.x = r.max.x = screen_size.x - 32;
+ r.min.y = r.max.y = screen_size.y - ys;
+
+ /* Retrieve and store old (wimp) pointer position */
+ Wimp_GetPointerInfo(&ptr);
+ old_mouse_posn = ptr.pos;
+
+ Pointer_RestrictToRect(r);
+
+ /* Turn the pointer off also */
+ SWI(2, 0, SWI_OS_Byte, 106, 0);
+}
+
+static void release_pointer()
+{
+ wimp_rect r;
+
+ r.min.x = r.max.x = old_mouse_posn.x;
+ r.min.y = r.max.y = old_mouse_posn.y;
+
+ Pointer_RestrictToRect(r);
+
+ Pointer_Unrestrict();
+
+ /* Turn the pointer back on also */
+ SWI(2, 0, SWI_OS_Byte, 106, 1);
+}
+
+
+
+
+
+/*
+ | Convert a 1bpp bitmap into a 4bpp bitmap (bit flipped)
+ */
+static int byte_to_word_flipped(int b)
+{
+ int w;
+ if (b & 128)
+ {
+ w = 0xf0000000;
+ }
+ else
+ {
+ w = 0;
+ }
+ if (b & 64)
+ {
+ w |= 0x0f000000;
+ }
+ if (b & 32)
+ {
+ w |= 0x00f00000;
+ }
+ if (b & 16)
+ {
+ w |= 0x000f0000;
+ }
+ if (b & 8)
+ {
+ w |= 0x0000f000;
+ }
+ if (b & 4)
+ {
+ w |= 0x00000f00;
+ }
+ if (b & 2)
+ {
+ w |= 0x000000f0;
+ }
+ if (b & 1)
+ {
+ w |= 0x0000000f;
+ }
+ return w;
+}
+
+
+
+
+/*
+ | try to load the fallback fullscreen font and convert it to 4bpp
+ */
+static int cache_zapfontHR(void)
+{
+ int handle;
+ unsigned int extent;
+ char buffer[260];
+ struct
+ {
+ char id[8];
+ int w, h, f, l, r1, r2;
+ }
+ zfh;
+ int *op;
+ char *ip;
+ int l, i;
+
+ /* Try to open the file */
+ sprintf(buffer, "%s%s", resource_path, "xtra.FullScreen");
+ handle = myFile_Open(buffer, 0x4f);
+ if (!handle)
+ {
+ return 0;
+ }
+
+ /* Check file's extent */
+ extent = myFile_Extent(handle);
+ if (extent > sizeof(zfh) + 256 * 16)
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ /* Load the header */
+ if (myFile_ReadBytes(handle, &zfh, sizeof(zfh)))
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ /* Check font size */
+ if ((zfh.w != 8) || (zfh.h > 16))
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ /* Load the 1bpp data */
+ if (myFile_ReadBytes(handle, fullscreen_font, extent - sizeof(zfh)))
+ {
+ myFile_Close(handle);
+ return 0;
+ }
+
+ myFile_Close(handle);
+
+ l = zfh.l > 255 ? 255 : zfh.l;
+
+ if (zfh.h > 8)
+ {
+ op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 4);
+ ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
+ (zfh.f * zfh.h));
+ while (l-- >= zfh.f)
+ {
+ for (i = 0; i < zfh.h; i++)
+ {
+ *--op = byte_to_word_flipped(*--ip);
+ }
+ }
+ fullscreen_height = zfh.h;
+ }
+ else
+ {
+ op = (int *)(((int)fullscreen_font) + (l + 1) * zfh.h * 8);
+ ip = (char *)(((int)fullscreen_font) + (l + 1) * zfh.h -
+ (zfh.f * zfh.h));
+ while (l-- >= zfh.f)
+ {
+ for (i = -zfh.h; i < zfh.h; i++)
+ {
+ int t = byte_to_word_flipped(*--ip);
+ *--op = t;
+ *--op = t;
+ }
+ }
+ fullscreen_height = zfh.h * 2;
+ }
+
+ fullscreen_topline = TERM_TOPLINE_HR;
+ fullscreen_topline += ((16 - fullscreen_height) * 13);
+
+ return 1;
+}
+
+
+
+
+
+static int cache_fs_fontHR(void)
+{
+ ZapFont *src = SYSTEM_FONT;
+ int c;
+ int *op;
+ char *ip;
+
+ /* Allocate the storage for the font */
+ fullscreen_font = f_malloc(256 * 4 * 16);
+ if (!fullscreen_font)
+ {
+ return 0;
+ }
+ op = (int *)fullscreen_font;
+
+ /* Check to see if the main term's font is suitable (ie. 8x16 or 8x8) */
+ if ((data[0].font->w == 8) && (data[0].font->h <= 16))
+ src = data[0].font;
+
+ /*
+ | Hack: if we're forced to use the system font, try to load the
+ | 'fullscreen' font from lib.xtra. If that fails, then I guess we're
+ | stuck with the system font.
+ */
+
+ if (src == SYSTEM_FONT)
+ if (cache_zapfontHR())
+ {
+ return 1;
+ }
+
+ ip = (char *)(src->bpp_1);
+
+ /* Now, create the font */
+ if (src->h > 8)
+ {
+ int e = src->h * (src->l > 256 ? 256 : src->l);
+ op += (src->f * src->h);
+ for (c = src->f * src->h; c < e; c++)
+ *op++ = byte_to_word_flipped(*ip++);
+ fullscreen_height = src->h;
+ }
+ else
+ {
+ int e = src->h * (src->l > 256 ? 256 : src->l);
+ op += (src->f * src->h) * 2;
+ for (c = src->f * src->h; c < e; c++)
+ {
+ int t = byte_to_word_flipped(*ip++);
+ *op++ = t;
+ *op++ = t;
+ }
+ fullscreen_height = src->h * 2;
+ }
+
+ fullscreen_topline = TERM_TOPLINE_HR;
+ fullscreen_topline += ((16 - fullscreen_height) * 13);
+
+ return 1;
+}
+
+
+
+
+
+static int cache_fs_fontLR(void)
+{
+ ZapFont *src = SYSTEM_FONT;
+ int c, e;
+ int *op;
+ char *ip;
+
+ /* Allocate the storage for the font */
+ fullscreen_font = f_malloc(256 * 4 * 8);
+ if (!fullscreen_font)
+ {
+ return 0;
+ }
+ op = (int *)fullscreen_font;
+
+ /* Check to see if the main term's font is suitable (ie. 8x8) */
+ if ((data[0].font->w == 8) && (data[0].font->h <= 8))
+ src = data[0].font;
+
+ ip = (char *)(src->bpp_1);
+
+ /* Now, create the font */
+ e = src->h * (src->l > 256 ? 256 : src->l);
+ op += (src->f * src->h);
+ for (c = src->f * src->h; c < e; c++)
+ *op++ = byte_to_word_flipped(*ip++);
+
+ fullscreen_height = src->h;
+ fullscreen_topline = TERM_TOPLINE_LR;
+ fullscreen_topline += ((8 - fullscreen_height) * 13);
+
+ return 1;
+}
+
+
+
+static void set_keys(int claim)
+{
+ static int old_c_state;
+ static int old_f_state[8];
+ int i;
+
+ if (claim)
+ {
+ /* Cursors/copy act as function keys */
+ /* f0-f9, cursors, generate 0x80-0x8f */
+ /* sh-f0-f9,cursors, generate 0x90-0x9f */
+ /* ctrl f0-f9,cursors, generate 0xa0-0xaf */
+ /* sh-c-f0-f9,cursors, generate 0xb0-0xbf */
+ /* f10-f12 generate 0xca-0xcc */
+ /* shift f10-f12 generate 0xda-0xdc */
+ /* ctrl f10-f12 generate 0xea-0xec */
+ /* ctrlshift f10-f12 generate 0xfa-0xfc */
+
+ SWI(3, 2, SWI_OS_Byte, 4, 2, 0, /**/ NULL, &old_c_state);
+
+ for (i = 0; i < 4; i++)
+ {
+ SWI(3, 2, SWI_OS_Byte, 225 + i, 0x80 + (i * 0x10), 0, NULL,
+ old_f_state + i);
+ SWI(3, 2, SWI_OS_Byte, 221 + i, 0xc0 + (i * 0x10), 0, NULL,
+ old_f_state + i + 4);
+ }
+ }
+ else
+ {
+ SWI(3, 0, SWI_OS_Byte, 4, old_c_state, 0);
+ for (i = 0; i < 4; i++)
+ {
+ SWI(3, 0, SWI_OS_Byte, 225 + i, old_f_state[i], 0);
+ SWI(3, 0, SWI_OS_Byte, 221 + i, old_f_state[i + 4], 0);
+ }
+ }
+}
+
+
+
+
+
+
+/*
+ | Enter the full screen mode.
+ |
+ | Full screen display uses either mode 27 (if supported) and 8x16 fonts
+ | (or system font 'twiddled' to double height), or mode 12 (if mode 27
+ | is unavailable) and the system font (or an 8x8 font).
+ |
+ */
+
+static void enter_fullscreen_mode(void)
+{
+ int vduvars[2] =
+ { 149, -1 };
+ int i;
+
+ /* New in 1.18 - protect against 're-entracy' */
+ if (fullscreen_font)
+ return;
+
+ /* New in 1.20 - hack IClear out of the way */
+ if (allow_iclear_hack)
+ iclear_hack();
+
+ /* Choose the mode we want */
+ fullscreen_mode = select_fullscreen_mode();
+
+ if (!fullscreen_mode)
+ {
+ plog("Unable to select a suitable screen mode (27 or 12)");
+ return;
+ }
+
+ if (!((fullscreen_mode == 12) ? cache_fs_fontLR() : cache_fs_fontHR()))
+ {
+ plog("Unable to cache a font for full screen mode");
+ return;
+ }
+
+ /* Read the current screen mode */
+ /* SWI( 1,3, SWI_OS_Byte, 135, NULL, NULL, &old_screenmode ); */
+ old_screenmode = current_mode();
+
+ Stop_Hourglass;
+
+ /* Change to the chosen screen mode */
+ change_screenmode(fullscreen_mode);
+
+ /* Restrict the pointer */
+ constrain_pointer();
+
+ /* Remove the cursors */
+ SWI(0, 0, SWI_OS_RemoveCursors);
+
+ Start_Hourglass;
+
+ /* Get the base address of screen memory */
+ SWI(2, 0, SWI_OS_ReadVduVariables, vduvars, vduvars);
+ fullscreen_base = (int *)(vduvars[0]);
+
+ /* Fudge the Term interface */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+ term *t = &(data[i].t);
+ t->xtra_hook = Term_xtra_acnFS;
+ t->wipe_hook = Term_wipe_acnFS;
+ t->curs_hook = Term_curs_acnFS;
+ t->text_hook = Term_text_acnFS;
+ }
+
+ /* Grab the palette */
+ Term_xtra_acn_reactFS(TRUE);
+
+ /* Make sure that the keys work properly */
+ set_keys(TRUE);
+
+ /* refresh the term */
+ /*Term_activate( &(data[0].t) ); */
+ redraw_areaFS(0, 0, 80, 24);
+ if (data[0].cursor.visible)
+ draw_cursor(data[0].cursor.pos.x, data[0].cursor.pos.y);
+
+ /* Display a reminder of how to get back... */
+ /* Hack: disable force_mono */
+ i = force_mono;
+ force_mono = 0;
+ Term_text_acnFS(0, TIME_LINE, strlen(fs_quit_key_text), 8,
+ fs_quit_key_text);
+ force_mono = i;
+}
+
+
+
+
+static void leave_fullscreen_mode(void)
+{
+ int i;
+
+ /* New in 1.18 - protect against 're-entracy' */
+ if (!fullscreen_font)
+ return;
+
+ /* Restore the Term interface */
+ for (i = 0; i < MAX_TERM_DATA; i++)
+ {
+#ifndef FULLSCREEN_ONLY
+ term *t = &(data[i].t);
+ t->xtra_hook = Term_xtra_acn;
+ t->wipe_hook = Term_wipe_acn;
+ t->curs_hook = Term_curs_acn;
+ t->text_hook = Term_text_acn;
+#endif
+ mark_ood(&(data[i]), 0, 0, 80, 24);
+ }
+
+ /* Deallocate the font */
+ f_free(fullscreen_font);
+ fullscreen_font = 0;
+ fullscreen_mode = 0;
+
+ Stop_Hourglass;
+
+ /* Restore the screen mode */
+ Wimp_SetMode(old_screenmode);
+
+ /* Restore the pointer */
+ release_pointer();
+
+ Start_Hourglass;
+
+ /* Restore the various soft keys */
+ set_keys(FALSE);
+
+ /* New in 1.20 Remove the IClear hack */
+ if (allow_iclear_hack)
+ remove_iclear_hack();
+
+#ifndef FULLSCREEN_ONLY
+ /* Refresh the windows - this probably isn't necessary anyway */
+ if (!minimise_memory)
+ refresh_windows();
+#endif /* FULLSCREEN_ONLY */
+}
+
+
+
+
+
+static void fs_writechars(int x, int y, int n, const char *chars, char attr)
+{
+ int *scr, *scrb;
+ int *cdat;
+ int j;
+ unsigned int fgm;
+
+ if (force_mono)
+ {
+ if (attr != TERM_DARK)
+ {
+ attr = TERM_WHITE;
+ }
+ }
+ fgm = (unsigned int)zpalette[(unsigned int) attr];
+
+ scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
+ + x * 4 + 320 * fullscreen_topline);
+
+ while (n--)
+ {
+ scr = scrb++;
+ cdat = (int *)(((int)fullscreen_font)
+ + (*chars++) * (fullscreen_height << 2));
+ for (j = 0; j < fullscreen_height; j++)
+ {
+ *scr = *cdat++ & fgm;
+ scr += 80;
+ }
+ }
+}
+
+
+static void fs_writechar(int x, int y, char c, char attr)
+{
+ int *scrb;
+ int *cdat;
+ int j;
+ unsigned int fgm;
+
+ if (force_mono)
+ {
+ if (attr != TERM_DARK)
+ {
+ attr = TERM_WHITE;
+ }
+ }
+ fgm = (unsigned int)zpalette[(unsigned int) attr];
+
+ scrb = (int *)(((int)fullscreen_base) + y * fullscreen_height * 320
+ + x * 4 + 320 * fullscreen_topline);
+ cdat = (int *)(((int)fullscreen_font) + (c * (fullscreen_height << 2)));
+ for (j = 0; j < fullscreen_height; j++)
+ {
+ *scrb = *cdat++ & fgm;
+ scrb += 80;
+ }
+}
+
+
+
+static void draw_cursorHR(int x, int y)
+{
+ ColourTrans_SetGCOL(cursor_rgb, 0, 0);
+ GFX_Move(x * 16,
+ 959 - y * (fullscreen_height * 2) - fullscreen_topline * 2);
+ GFX_DrawBy(14, 0);
+ GFX_DrawBy(0, -(fullscreen_height * 2 - 2));
+ GFX_DrawBy(-14, 0);
+ GFX_DrawBy(0, fullscreen_height * 2 - 2);
+}
+
+static void draw_cursorLR(int x, int y)
+{
+ ColourTrans_SetGCOL(cursor_rgb, 0, 0);
+ GFX_Move(x * 16,
+ 1023 - y * (fullscreen_height * 4) - fullscreen_topline * 4);
+ GFX_DrawBy(14, 0);
+ GFX_DrawBy(0, -(fullscreen_height * 4 - 4));
+ GFX_DrawBy(-14, 0);
+ GFX_DrawBy(0, fullscreen_height * 4 - 4);
+}
+
+
+
+
+
+static void draw_cursor(int x, int y)
+{
+ if (fullscreen_mode == 12)
+ draw_cursorLR(x, y);
+ else
+ draw_cursorHR(x, y);
+}
+
+
+
+static void redraw_areaFS(int x, int y, int w, int h)
+{
+ int i, j;
+ for (j = y; j < y + h; j++)
+ for (i = x; i < x + w; i++)
+ fs_writechar(i, j, data[0].t.old->c[j][i], data[0].t.old->a[j][i]);
+}
+
+
+
+static int wimp_code(int c)
+{
+ /* shift/ctrl keypad? */
+ if (c >= '0' && c <= '9')
+ {
+ kbd_modifiers m = Kbd_GetModifiers(FALSE);
+ if (m.shift)
+ {
+ c |= 0x800;
+ }
+ if (m.ctrl)
+ {
+ c |= 0x400;
+ }
+ return c;
+ }
+ if (c == 9)
+ {
+ return 0x18a;
+ } /* Tab */
+ if (c <= 127)
+ {
+ return c;
+ } /* normal ASCII/ctrl */
+ if (c >= 0x80 && c <= 0xff)
+ {
+ return c + 0x100;
+ } /* f0-f9, etc. */
+
+ return -1; /* unknown */
+}
+
+
+
+
+static void do_keypress(int code)
+{
+ static const char hex[] = "0123456789ABCDEF";
+
+ if (code == KEYPRESS_QUIT && !minimise_memory)
+ {
+#ifdef FULLSCREEN_ONLY
+ Sound_SysBeep();
+#else
+ leave_fullscreen_mode();
+#endif
+ return;
+ }
+
+ if (code == 27)
+ {
+ if (Kbd_KeyDown(inkey_CTRL))
+ {
+ ack_alarm();
+ return;
+ }
+ }
+
+ if (code <= 255)
+ {
+ Term_keypress(code);
+ }
+ else
+ {
+ Term_keypress(31);
+ Term_keypress(hex[(code & 0xf00) >> 8]);
+ Term_keypress(hex[(code & 0x0f0) >> 4]);
+ Term_keypress(hex[(code & 0x00f)]);
+ Term_keypress(13);
+ }
+}
+
+
+
+
+static errr Term_xtra_acn_checkFS(void)
+{
+ int bh, bl;
+ int c;
+
+ Stop_Hourglass;
+
+ bored();
+
+ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
+ bl = (bl & 0xff) + (bh << 8);
+
+ if (bl > 0)
+ {
+ SWI(0, 1, SWI_OS_ReadC, &c);
+ bl = wimp_code(c);
+ if (bl >= 0)
+ {
+ do_keypress(bl);
+ }
+ }
+
+ Start_Hourglass;
+
+ return 0;
+}
+
+
+
+
+static errr Term_xtra_acn_eventFS(void)
+{
+ int c;
+ int w = -1;
+
+ Stop_Hourglass;
+
+ for (w = -1; w == -1;)
+ {
+ int bh, bl;
+ do
+ {
+ bored();
+ SWI(3, 3, SWI_OS_Byte, 128, 255, 0, /**/ NULL, &bl, &bh);
+ bl = (bl & 0xff) + (bh << 8);
+ }
+ while (!bl);
+
+ SWI(0, 1, SWI_OS_ReadC, &c);
+ w = wimp_code(c);
+ if (w >= 0)
+ {
+ do_keypress(w);
+ }
+ }
+
+ Start_Hourglass;
+
+ return 0;
+}
+
+
+
+/*
+ * React to changes
+ */
+static errr Term_xtra_acn_reactFS(int force)
+{
+ unsigned int i;
+ int p, r, g, b;
+
+ static double old_gamma = -1.0;
+
+ if (gamma != old_gamma)
+ {
+ force = 1;
+ old_gamma = gamma;
+ }
+
+ /* Set the screen colours */
+ for (i = 0; i < 16; i++)
+ {
+ if (COLOUR_CHANGED(i) || force)
+ {
+ r = (int)(255.0 *
+ pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
+ g = (int)(255.0 *
+ pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
+ b = (int)(255.0 *
+ pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
+ GFX_VDU(19);
+ GFX_VDU(i);
+ GFX_VDU(16);
+ GFX_VDU(r);
+ GFX_VDU(g);
+ GFX_VDU(b);
+
+ palette[i] = (b << 24) | (g << 16) | (r << 8);
+ p = i;
+ p |= (p << 4);
+ p |= (p << 8);
+ p |= (p << 16);
+ zpalette[i] = p;
+
+ a_palette[i][1] = angband_color_table[i][1];
+ a_palette[i][2] = angband_color_table[i][2];
+ a_palette[i][3] = angband_color_table[i][3];
+
+ /* Find any higher colour numbers and make them "wrong" */
+ for (p = 16; p < 256; p++)
+ if ((zpalette[p] & 0xf) == i)
+ a_palette[p][1] = angband_color_table[p][1] + 2;
+ }
+ }
+
+
+ /* Go through the palette updating any changed values */
+ for (i = 16; i < 256; i++)
+ {
+ if (COLOUR_CHANGED(i) || force)
+ {
+ r = (int)(255.0 *
+ pow(angband_color_table[i][1] / 255.0, 1.0 / gamma));
+ g = (int)(255.0 *
+ pow(angband_color_table[i][2] / 255.0, 1.0 / gamma));
+ b = (int)(255.0 *
+ pow(angband_color_table[i][3] / 255.0, 1.0 / gamma));
+ p = (b << 24) | (g << 16) | (r << 8);
+ palette[i] = p;
+ SWI(1, 1, SWI_ColourTrans_ReturnColourNumber, palette[i], &p);
+ p |= (p << 4);
+ p |= (p << 8);
+ p |= (p << 16);
+ zpalette[i] = p;
+ a_palette[i][1] = angband_color_table[i][1];
+ a_palette[i][2] = angband_color_table[i][2];
+ a_palette[i][3] = angband_color_table[i][3];
+ }
+ }
+
+ cursor_rgb = palette[CURSOR_COLOUR];
+
+ return 0;
+}
+
+
+static errr Term_curs_acnFS(int x, int y)
+{
+ if (Term == &(data[0].t))
+ {
+ if (data[0].cursor.visible)
+ redraw_areaFS(data[0].cursor.pos.x, data[0].cursor.pos.y, 1, 1);
+ data[0].cursor.pos.x = x;
+ data[0].cursor.pos.y = y;
+ if (data[0].cursor.visible)
+ draw_cursor(x, y);
+ }
+ return 0;
+}
+
+static errr Term_xtra_acn_clearFS(void)
+{
+ char e[80];
+ int j;
+
+ if (Term == &(data[0].t))
+ {
+ for (j = 0; j < 80; j++)
+ e[j] = ' ';
+
+ GFX_Wait();
+
+ for (j = 0; j < 24; j++)
+ fs_writechars(0, j, 80, e, 0);
+ }
+
+ return 0;
+}
+
+
+
+
+
+
+
+static errr Term_xtra_acnFS(int n, int v)
+{
+ term_data *t = (term_data *)Term;
+
+ switch (n)
+ {
+ case TERM_XTRA_CLEAR:
+ if (t == (&data[0]))
+ Term_xtra_acn_clearFS();
+ return 0;
+
+ case TERM_XTRA_EVENT:
+ if (v)
+ return Term_xtra_acn_eventFS();
+ else
+ return Term_xtra_acn_checkFS();
+
+ case TERM_XTRA_BORED:
+ bored();
+ return Term_xtra_acn_checkFS();
+
+ case TERM_XTRA_FLUSH:
+ /* 1.21 - Hack: wait until no keys are pressed */
+ if (hack_flush)
+ for (v = 0; v != 0xff;)
+ SWI(1, 2, SWI_OS_Byte, 122, 0, &v);
+ SWI(3, 0, SWI_OS_Byte, 21, 0, 0); /* Flush Kbd buffer */
+ return 0;
+
+ case TERM_XTRA_FRESH:
+ return 0;
+
+ case TERM_XTRA_FROSH:
+ return 0;
+
+ case TERM_XTRA_SHAPE:
+ if (t == (&data[0]))
+ {
+ t->cursor.visible = v;
+ if (v)
+ draw_cursor(t->cursor.pos.x, t->cursor.pos.y);
+ else
+ redraw_areaFS(t->cursor.pos.x, t->cursor.pos.y, 1, 1);
+ }
+ return 0;
+
+ case TERM_XTRA_NOISE:
+ Sound_SysBeep();
+ return 0;
+
+ case TERM_XTRA_REACT:
+ return Term_xtra_acn_reactFS(FALSE);
+
+ case TERM_XTRA_DELAY:
+ if (v > 0)
+ {
+ int start = Time_Monotonic();
+ v = (v + 5) / 10; /* Round to nearest cs */
+ GFX_Wait();
+ while ((Time_Monotonic() - start) < v)
+ ;
+ }
+ return (0);
+
+ case TERM_XTRA_SOUND: /* Play a sound :) */
+ if (enable_sound)
+ {
+ play_sound(v);
+ }
+ return 0;
+
+ /* Subdirectory scan */
+ case TERM_XTRA_SCANSUBDIR:
+ {
+ filing_dirdata directory;
+ filing_direntry *entry;
+
+ scansubdir_max = 0;
+
+ if (Filing_OpenDir(riscosify_name(scansubdir_dir), &directory, sizeof(filing_direntry), readdirtype_DIRENTRY) != NULL)
+ {
+ Error_Report(0, "Couldn't open directory \"%s\"", riscosify_name(scansubdir_dir));
+ return 0;
+ }
+
+ while ((entry = Filing_ReadDir(&directory)) != NULL)
+ {
+ if (entry->objtype == filing_DIRECTORY)
+ {
+ string_free(scansubdir_result[scansubdir_max]);
+ scansubdir_result[scansubdir_max] = string_make(entry->name);
+ ++scansubdir_max;
+ }
+ }
+
+ Filing_CloseDir(&directory);
+
+ return 0;
+ }
+
+ /* Return current "time" in milliseconds */
+ case TERM_XTRA_GET_DELAY:
+ {
+ Term_xtra_long = Time_Monotonic() * 100;
+
+ return 0;
+ }
+
+ /* Rename main window */
+ case TERM_XTRA_RENAME_MAIN_WIN:
+ {
+ Window_SetTitle(data[0].w, angband_term_name[0]);
+ return 0;
+ }
+
+ default:
+ return 1;
+ }
+}
+
+static errr Term_wipe_acnFS(int x, int y, int n)
+{
+ if (Term == &(data[0].t))
+ while (n--)
+ fs_writechar(x++, y, ' ', 0);
+ return 0;
+}
+
+static errr Term_text_acnFS(int x, int y, int n, byte a, cptr s)
+{
+ if (Term == &(data[0].t))
+ fs_writechars(x, y, n, s, (char)a);
+ return 0;
+}
+
+
+
+static void bored()
+{
+ static int last = -1;
+ char ts[80];
+ time_t ct;
+ struct tm *lt;
+ unsigned int l;
+ int ofm;
+ static int alarm_flash = 1;
+
+ /* Really no need to check the alarm more than once per second. */
+ if (alarm_type && Time_Monotonic() > alarm_lastcheck + 100)
+ {
+ check_alarm();
+ }
+
+ l = Time_Monotonic();
+ if ((l - last) < (alarm_flash ? 25 : 50))
+ {
+ return;
+ }
+ last = l;
+
+ time(&ct);
+ lt = localtime(&ct);
+ l = strftime(ts, 80, "%c %Z", lt);
+
+ /* Hack: disable force_mono around printing the time */
+ ofm = force_mono;
+ force_mono = 0;
+
+ /* Hack: Is the alarm supposed to be going off? */
+ if (alarm_disp || alarm_flash)
+ {
+ char blk[60];
+ int c = 8;
+ if (!alarm_disp)
+ {
+ alarm_flash = 11;
+ }
+ switch (alarm_flash / 2)
+ {
+ case 4: sprintf(blk, "%-57s", alarm_cancel_text);
+ break;
+ case 5: sprintf(blk, "%-57s", fs_quit_key_text);
+ break;
+ default:
+ c = alarm_flash & 1 ? TERM_RED : TERM_WHITE;
+ sprintf(blk, "%02d:%02d %-51s", alarm_h, alarm_m,
+ alarm_message);
+ }
+ fs_writechars(0, TIME_LINE, 57, blk, c);
+ if (++alarm_flash > 11)
+ {
+ alarm_flash = 0;
+ }
+ }
+
+ /* Display time */
+ fs_writechar(79 - l, TIME_LINE, ' ', 0);
+ fs_writechars(80 - l, TIME_LINE, l, ts, 8);
+
+ force_mono = ofm;
+}
+
+
+
+
+
+
+#ifdef USE_DA
+/*--------------------------------------------------------------------------*/
+/* (Simple) Heap management (using OS_Heap) */
+/*--------------------------------------------------------------------------*/
+
+typedef void *heap;
+
+static os_error *Heap_Initialise(heap h, size_t size)
+{
+ return SWI(4, 0, SWI_OS_Heap, 0, h, 0, size);
+}
+
+static void *Heap_Claim(heap h, size_t size)
+{
+ void *fred;
+ os_error *e;
+ e = SWI(4, 3, SWI_OS_Heap, 2, h, 0, size, NULL, NULL, &fred);
+ return e ? NULL : fred;
+}
+
+static os_error *Heap_Release(heap h, void *block)
+{
+ return SWI(3, 0, SWI_OS_Heap, 3, h, block);
+}
+
+static int Heap_ChangeHeapSize(heap h, int resize_by)
+{
+ int by;
+ SWI(4, 4, SWI_OS_Heap, 5, h, 0, resize_by, 0, 0, 0, &by);
+ return by;
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Stuff below here is for using Dynamic areas (under RO3.5+) */
+/*--------------------------------------------------------------------------*/
+
+static int game_area = -1; /* The DA the game is using */
+static int font_area = -1; /* The DA the fonts are using */
+
+static void *game_area_base; /* base address of game area */
+static void *font_area_base; /* base address of font area */
+
+static int font_area_size; /* size of the fonts' DA */
+static int font_heap_size; /* size of the fonts' heap */
+static int game_area_size; /* size of the game's DA */
+static int game_heap_size; /* size of the game's heap */
+
+#define MAX_F_DA_SIZE (2<<20) /* Max size of font area (2Mb) */
+#define MAX_G_DA_SIZE (4<<20) /* Max size of game area (4Mb) */
+#define SHRINK_GRAN (4<<10) /* Try to recalaim wastage > this (4Kb) */
+
+
+/*
+ | Free dynamic areas when we exit
+ */
+static void cleanup_memory(void)
+{
+ if (game_area != -1)
+ {
+ SWI(2, 0, SWI_OS_DynamicArea, 1, game_area);
+ game_area = -1;
+ }
+
+ if (font_area != -1)
+ {
+ SWI(2, 0, SWI_OS_DynamicArea, 1, font_area);
+ font_area = -1;
+ }
+
+}
+
+
+
+/*
+ | Set up the memory allocation stuff.
+ | We check to see if DAs are possible and if so initialise two:
+ | one for the game's use (via the rnalloc() hooks) and one for
+ | our own use (for fonts, etc).
+ |
+ | Each area is created 16Kb in size, with a max size of 2/4Mb.
+ |
+ | If 'daf' is TRUE, an area is created for the fonts.
+ | If 'dag' is TRUE, an area is created for the game.
+ */
+static void init_memory(int daf, int dag)
+{
+ os_error *e = NULL;
+
+ if (!daf)
+ {
+ /* Paranoia */
+ font_area = -1;
+ font_area_base = 0;
+ }
+ else
+ {
+ e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */
+ -1, /* Let OS allocate no. */
+ 16 << 10, /* Initial size */
+ -1, /* Let OS allocate address */
+ 1 << 7, /* Cacheable, bufferable, RW */
+ MAX_F_DA_SIZE, /* Max size */
+ 0, /* handler */
+ 0, /* handler workspace */
+ VARIANT " font data", /* Name */
+ /* */
+ NULL, /* r0 */
+ &font_area, /* area number allocated */
+ NULL, /* r2 */
+ &font_area_base /* base address of area */
+ );
+
+ if (e)
+ {
+ game_area = font_area = -1;
+ game_area_base = font_area_base = 0; /* paranoia */
+ return;
+ }
+ else
+ {
+ e = SWI(2, 3, SWI_OS_DynamicArea, 2, font_area,
+ NULL, NULL, &font_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+
+ e = Heap_Initialise((heap) font_area_base, font_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+ font_heap_size = font_area_size;
+ }
+ }
+
+ /* Make sure DA(s) are removed when we quit */
+ atexit(cleanup_memory);
+
+ if (!dag)
+ {
+ /* Paranoia */
+ game_area = -1;
+ game_area_base = 0;
+ }
+ else
+ {
+ e = SWI(9, 4, SWI_OS_DynamicArea, 0, /* Create */
+ -1, /* Let OS allocate no. */
+ 16 << 10, /* Initial size */
+ -1, /* Let OS allocate address */
+ 1 << 7, /* Cacheable, bufferable, RW */
+ MAX_G_DA_SIZE, /* Max size */
+ 0, /* handler */
+ 0, /* handler workspace */
+ VARIANT " game data", /* Name */
+ /* */
+ NULL, /* r0 */
+ &game_area, /* area number allocated */
+ NULL, /* r2 */
+ &game_area_base /* base address of area */
+ );
+
+ if (e)
+ {
+ game_area = -1;
+ game_area_base = 0; /* paranoia */
+ }
+ else
+ {
+ e = SWI(2, 3, SWI_OS_DynamicArea, 2, game_area,
+ NULL, NULL, &game_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+
+ e = Heap_Initialise((heap) game_area_base, game_area_size);
+ if (e)
+ {
+ Error_ReportFatal(e->errnum, "%d:%s", e->errmess);
+ }
+ game_heap_size = game_area_size;
+ }
+ }
+}
+
+static int grow_dynamicarea(int area, int by)
+{
+ os_error *e;
+ e = SWI(2, 2, SWI_OS_ChangeDynamicArea, area, by, /**/ NULL, &by);
+ /* Can't check errors since a 'failed' shrink returns one... */
+ return by;
+}
+
+
+/*
+ | Try to shrink the font-cache heap and area as much as possible.
+ */
+static void f_shrink_heap(void)
+{
+ int s;
+ /* Shrink the heap as far as possible */
+ font_heap_size -=
+ Heap_ChangeHeapSize((heap) font_area_base, -MAX_F_DA_SIZE);
+ /* Shrink the dynamic area if necessary */
+ s = font_area_size - font_heap_size;
+ if (s >= SHRINK_GRAN)
+ font_area_size -= grow_dynamicarea(font_area, -s);
+}
+
+/*
+ | Allocate a block of memory in the font heap
+ */
+static void *f_malloc(size_t size)
+{
+ void *c;
+ int s;
+ if (font_area == -1)
+ {
+ return malloc(size);
+ }
+ c = Heap_Claim((heap) font_area_base, size);
+
+ if (!c)
+ {
+ /* The Claim failed. Try to grow the area by the size of the block */
+ s = grow_dynamicarea(font_area, size + 64); /* 64 is overkill */
+ if (!s)
+ {
+ return NULL;
+ }
+ font_area_size += s;
+ s = font_area_size - font_heap_size;
+ font_heap_size += Heap_ChangeHeapSize((heap) font_area_base, s);
+ c = Heap_Claim((heap) font_area_base, size);
+ if (c)
+ {
+ f_shrink_heap();
+ }
+ }
+ return c;
+}
+
+
+/*
+ | Free a block of memory in the font heap
+ */
+static void f_free(void *blk)
+{
+ os_error *e;
+ if (font_area == -1)
+ {
+ free(blk);
+ return;
+ }
+ e = Heap_Release((heap) font_area_base, blk);
+ if (e)
+ Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
+ f_shrink_heap();
+}
+
+
+
+
+
+/*
+ | Allocate a block of memory in the game heap
+ */
+static vptr g_malloc(huge size)
+{
+ void *c;
+ int s;
+
+ if (game_area == -1)
+ {
+ return malloc((size_t) size);
+ }
+ c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
+ if (!c)
+ {
+ /* The Claim failed. Try to grow the area by the size of the block */
+ s = grow_dynamicarea(game_area, (size_t) size + 64); /* 64 is overkill */
+ if (!s)
+ {
+ return NULL;
+ }
+ game_area_size += s;
+ s = game_area_size - game_heap_size;
+ game_heap_size += Heap_ChangeHeapSize((heap) game_area_base, s);
+ c = Heap_Claim((heap) game_area_base, (size_t) size + 4);
+ }
+
+ if (c)
+ {
+ strcpy((char *)c, "MUSH");
+ c = (void *)(((int)c) + 4);
+ }
+
+ if (log_g_malloc)
+ fprintf(stderr, "ralloc(%ld) == %p\n", (long)size, c);
+
+ return c;
+}
+
+
+/*
+ | Free a block of memory in the game heap
+ |
+ | The 'len' is to be compatible with z-virt.c (we don't need/use it)
+ | Returns NULL.
+ */
+static vptr g_free(vptr blk, huge size)
+{
+ os_error *e;
+ int s;
+
+ if (game_area == -1)
+ {
+ free(blk);
+ return NULL;
+ }
+
+ if (log_g_malloc)
+ fprintf(stderr, "rnfree(%p)\n", blk);
+
+ if (strncmp(((char *)blk) - 4, "MUSH", 4))
+ core("game heap corrupt / bad attempt to free memory");
+
+ blk = (void *)(((int)blk) - 4);
+
+ e = Heap_Release((heap) game_area_base, blk);
+ if (e)
+ Msgs_ReportFatal(e->errnum, "err.swi", __LINE__, e->errmess);
+
+ /* Shrink the heap as far as possible */
+ game_heap_size -=
+ Heap_ChangeHeapSize((heap) game_area_base, -MAX_G_DA_SIZE);
+
+ /* Shrink the dynamic area if necessary */
+ s = game_area_size - game_heap_size;
+ if (s >= SHRINK_GRAN)
+ game_area_size -= grow_dynamicarea(game_area, -s);
+
+ return NULL;
+}
+
+
+#endif /* USE_DA */
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | New to 1.04: Sound support :)
+ |
+ | We use the PlayIt module (for convenience).
+ |
+ | The Lib/xtra/sound/sound.cfg file is used to map sample names onto
+ | event names.
+ |
+ | Since textual names are used in the .cfg file, we need to have a lookup
+ | table to translate them into numbers. At present we use the
+ | angband_sound_name array defined in variable.c
+ |
+ | Since there can be multiple sounds for each event we need to use a
+ | list to store them.
+ */
+
+/* NB: This will be clipped to 10 under RISC OS 2 */
+#define MAX_SAMPNAME_LEN 64
+
+
+
+
+/*
+ | The list format:
+ */
+typedef struct samp_node
+{
+ char sample_name[MAX_SAMPNAME_LEN + 1]; /* Sample name */
+ struct samp_node *next; /* -> next node */
+}
+SampNode;
+
+typedef struct samp_info
+{
+ int samples; /* # samples for this event */
+ SampNode *samplist; /* list of sample names */
+}
+SampInfo;
+
+
+/*
+ | Just need an array of SampInfos
+ */
+static SampInfo sample[SOUND_MAX];
+
+/*
+ | This flag will only be set non-zero if the SampInfo array is
+ | valid.
+ */
+static int sound_initd = 0;
+
+
+static void read_sound_config(void)
+{
+ int i;
+ char buffer[2048];
+ FILE *f;
+ int max_sampname_len = truncate_names()? 10 : MAX_SAMPNAME_LEN;
+ FILE *dbo = NULL;
+
+ if (show_sound_alloc)
+ {
+ sprintf(buffer, "%s%s", resource_path, "sndmap/out");
+ dbo = fopen(buffer, "w");
+ if (!dbo)
+ {
+ core("can't create sndmap/out debugging file");
+ }
+ }
+
+ if (!sound_initd)
+ {
+ /* Initialise the sample array */
+ for (i = 0; i < SOUND_MAX; i++)
+ {
+ sample[i].samples = 0;
+ sample[i].samplist = NULL;
+ }
+ sound_initd = 1;
+ }
+ else
+ {
+ /* Deallocate the sample lists */
+ for (i = 0; i < SOUND_MAX; i++)
+ {
+ SampNode *si = sample[i].samplist;
+ sample[i].samples = 0;
+ sample[i].samplist = NULL;
+ while (si)
+ {
+ SampNode *ns = si->next;
+ free(si);
+ si = ns;
+ }
+ }
+ }
+
+
+ /* Open the config file */
+ sprintf(buffer, "%sSound:%s", RISCOS_VARIANT, "sound/cfg");
+ f = fopen(buffer, "r");
+
+ /* No cfg file => no sounds */
+ if (!f)
+ {
+ if (show_sound_alloc)
+ {
+ fprintf(dbo, "** Can't open cfg file '%s'\n", buffer);
+ fclose(dbo);
+ }
+ return;
+ }
+
+ /* Parse the file */
+ while (fgets(buffer, sizeof(buffer), f))
+ {
+ char *sample_name;
+ int event_number;
+
+ /* Skip comments and lines that begin with whitespace */
+ if (*buffer == '#' || isspace((unsigned char)*buffer))
+ {
+ continue;
+ }
+
+ /* Hack: ignore any line beginning '[' (section marker) */
+ if (*buffer == '[')
+ {
+ continue;
+ }
+
+ /* Place a NULL after the event name and find the first sample name */
+ sample_name = buffer;
+ while (*sample_name && !isspace((unsigned char)*sample_name))
+ sample_name++;
+
+ /* Bad line? */
+ if (*sample_name == 0)
+ {
+ continue;
+ } /* just ignore it */
+
+ /* Terminate the sample name */
+ *sample_name++ = 0;
+
+ /* Look up the event name to get the event number */
+ for (event_number = SOUND_MAX - 1; event_number >= 0; event_number--)
+ if (!strcmp(buffer, angband_sound_name[event_number]))
+ break;
+
+ /* No match -> just ignore the line */
+ if (event_number < 0)
+ {
+ if (show_sound_alloc)
+ fprintf(dbo, "* Ignoring unknown event '%s'\n", buffer);
+ continue;
+ }
+
+ /* Find the = */
+ while (*sample_name && *sample_name != '=')
+ sample_name++;
+
+ /* Bad line? */
+ if (*sample_name == 0)
+ {
+ continue;
+ } /* just ignore it */
+
+ /* Skip the '=' */
+ sample_name++;
+
+
+ /*
+ | Now we find all the sample names and add them to the
+ | appropriate list in the sample mapping array
+ */
+
+ while (*sample_name)
+ {
+ char *s;
+ SampNode *sn;
+
+ /* Find the start of the next word */
+ while (isspace((unsigned char)*sample_name) && *sample_name)
+ sample_name++;
+
+ /* End of line? */
+ if (!*sample_name)
+ {
+ break;
+ }
+
+ /* Find the end of the sample name */
+ s = sample_name; /* start of the name */
+ while (!isspace((unsigned char)*sample_name) && *sample_name)
+ sample_name++;
+
+ /* Hack: shorten sample names that are too long */
+ if ((sample_name - s) > max_sampname_len)
+ s[max_sampname_len] = ' ';
+
+ /* Allocate a node in the sample list for the event */
+ if ((sn = malloc(sizeof(SampNode))) == NULL)
+ core("Out of memory (scanning sound.cfg)");
+
+ /* Link the node to the list */
+ sn->next = sample[event_number].samplist;
+ sample[event_number].samplist = sn;
+
+ /* Imcrement the sample count for that event */
+ sample[event_number].samples++;
+
+ /*
+ | Copy the sample name into the node, converting it into
+ | RISC OS style as we go.
+ */
+ for (i = 0; !isspace((unsigned char)s[i]) && s[i]; i++)
+ {
+ if (s[i] == '.')
+ sn->sample_name[i] = '/';
+ else if (s[i] == '/')
+ sn->sample_name[i] = '.';
+ else
+ sn->sample_name[i] = s[i];
+ }
+ /*
+ | The sample name '*' is special and means "no new sound"
+ | so don't store a filename for these mappings.
+ */
+ if (i == 1 && sn->sample_name[0] == '*')
+ {
+ i = 0;
+ }
+ sn->sample_name[i] = 0;
+ }
+ }
+
+ /* Close the file */
+ fclose(f);
+
+ if (show_sound_alloc)
+ {
+ int i;
+ SampNode *l;
+
+ for (i = 0; i < SOUND_MAX; i++)
+ {
+ fprintf(dbo, "\n\nEvent '%s'", angband_sound_name[i]);
+ fprintf(dbo, " (%d sounds)\n", sample[i].samples);
+ for (l = sample[i].samplist; l; l = l->next)
+ fprintf(dbo, "\t%s\n", l->sample_name);
+ }
+ fclose(dbo);
+ }
+
+}
+
+
+
+/*
+ | Try to make sure that PlayIt is loaded.
+ | This requires AngSound rel. 4
+ */
+static void check_playit(void)
+{
+ if (SWI(2, 0, SWI_OS_Module, 18, "PlayIt"))
+ {
+ int t;
+ SWI(2, 1, SWI_OS_File, 17, "Angsound:LoadPlayIt", &t);
+ if (t == 1)
+ SWI(1, 0, SWI_OS_CLI,
+ "RMEnsure PlayIt 0.00 Run AngSound:LoadPlayIt");
+ }
+}
+
+
+
+
+static void initialise_sound(void)
+{
+ /* Load the configuration file */
+ Hourglass_On();
+ read_sound_config();
+ check_playit();
+ Hourglass_Off();
+}
+
+
+
+static void play_sample(char *leafname)
+{
+ char buffer[260];
+
+ strcpy(buffer, "%playit_stop");
+
+ if (!SWI(1, 0, SWI_OS_CLI, buffer))
+ {
+ SWI(1, 0, SWI_PlayIt_Volume, sound_volume);
+ sprintf(buffer, "%%playit_play %sSound:%s", RISCOS_VARIANT, leafname);
+ SWI(1, 0, SWI_OS_CLI, buffer);
+ }
+
+ return;
+}
+
+
+
+static void play_sound(int event)
+{
+ /* Paranoia */
+ if (!sound_initd)
+ {
+ return;
+ }
+
+ /* Paranoia */
+ if (event < 0 || event >= SOUND_MAX)
+ return;
+
+ /* msg_format("Sound '%s'",angband_sound_name[event]); */
+
+ /* Choose a sample */
+ if (sample[event].samples)
+ {
+ int s = rand() % sample[event].samples;
+ SampNode *sn = sample[event].samplist;
+ while (s--)
+ {
+ sn = sn->next;
+ if (!sn)
+ {
+ plog("Adny botched the sound config - please send him a copy of your sound/cfg file.");
+ }
+ }
+ if (*(sn->sample_name))
+ play_sample(sn->sample_name);
+ }
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | This stuff is for the Term_user hook
+ */
+
+
+
+static void display_line(int x, int y, int c, const char *fmt, ...)
+{
+ va_list ap;
+ char buffer[260];
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ Term_putstr(x, y, -1, c, buffer);
+ va_end(ap);
+}
+
+
+
+/*
+ | Let the user change the alarm message
+ */
+static void do_alarm_message_input(int y)
+{
+ int k;
+ int inspos = strlen(alarm_message);
+ char old_message[52];
+
+ strcpy(old_message, alarm_message);
+
+ do
+ {
+ display_line(26, y, TERM_YELLOW, "%-51s", alarm_message);
+ Term_gotoxy(26 + inspos, y);
+ k = inkey();
+ switch (k)
+ {
+ case 21: /* ^U */
+ *alarm_message = 0;
+ inspos = 0;
+ break;
+ case 128: case 8: /* delete */
+ if (inspos > 0)
+ {
+ alarm_message[--inspos] = 0;
+ }
+ break;
+ case 27: /* escape */
+ strcpy(alarm_message, old_message);
+ k = 13;
+ break;
+ default:
+ if (k > 31 && k < 127 && inspos < 50)
+ {
+ alarm_message[inspos++] = k;
+ alarm_message[inspos] = 0;
+ }
+ }
+ }
+ while (k != 13);
+
+ display_line(26, y, TERM_WHITE, "%-51s", alarm_message);
+}
+
+
+#define tum_col(X) ((X) ? TERM_L_BLUE : TERM_WHITE )
+#define tum_onoff(X) ((X) ? "On " : "Off")
+
+static errr Term_user_acn(int n)
+{
+ int cursor_state;
+ int optn = 0;
+ int k, adj;
+ int redraw_mung = 0;
+ int max_opt = 11;
+ int alarm_modified = 0; /* Will be true if the alarm choices need to be (re)saved */
+
+ /*
+ | Hack: let the desktop front end know that
+ | the user menu is active...
+ */
+ user_menu_active = TRUE;
+
+
+ /*
+ | This is thanks to Norcroft CC... it seems to want
+ | to set up FP regs nice and early and then relies
+ | on them remaining constant over the function call.
+ | The trouble is that we implicitly call Wimp_Poll
+ | whilst waiting for a key press...
+ */
+ event_mask.data.keepfpregisters = 1;
+
+ /*
+ | Hack: alarm type 1 /looks/ the same as type 3 but doesn't get
+ | cancelled as a type 3 would. This allows alarms to go off and
+ | be cancelled without affecting the alarm type whilst it's being
+ | set up here.
+ */
+ if (alarm_type == 3)
+ {
+ alarm_type = 1;
+ }
+
+ /*
+ | Store the screen
+ */
+ Term_activate(&(data[0].t));
+ Term_save();
+ Term_get_cursor(&cursor_state);
+ Term_set_cursor(TRUE);
+
+ do
+ {
+ redraw_mung = 0;
+ Term_clear();
+ display_line(2, 1, TERM_YELLOW, "%s %s", VARIANT, VERSION);
+ display_line(2, 2, TERM_SLATE, "Front-end %s", PORTVERSION);
+ display_line(2, 4, TERM_WHITE,
+ "Use cursor up/down to select an option then cursor left/right to alter it.");
+ display_line(2, 5, TERM_WHITE,
+ "Hit 'S' to save these settings (alarm settings are saved automatically).");
+ display_line(2, 6, TERM_WHITE, "Hit ESC to return to the game.");
+
+ for (k = 0; k < 16; k++)
+ display_line(31 + k * 3, 8, k, "##", k);
+
+ do
+ {
+ display_line(2, 8, tum_col(optn == 0),
+ " Gamma correction : %.2lf", gamma);
+ display_line(2, 9, tum_col(optn == 1), " Force monochrome : %s",
+ tum_onoff(force_mono));
+ display_line(2, 10, tum_col(optn == 2), " Sound effects : %s",
+ tum_onoff(enable_sound));
+ display_line(2, 11, tum_col(optn == 3), " Sound effect volume : ");
+ display_line(26, 11,
+ sound_volume > 127 ? TERM_RED : tum_col(optn == 3),
+ "%-3d", sound_volume);
+ display_line(30, 11, tum_col(optn == 3), "(127 = full volume)");
+ display_line(2, 12, tum_col(optn == 4), " Start fullscreen : %s",
+ tum_onoff(start_fullscreen));
+ display_line(30, 12, tum_col(optn == 4),
+ "(also selects fullscreen/desktop now)");
+ display_line(2, 13, tum_col(optn == 5), " Use hourglass : %s",
+ tum_onoff(use_glass));
+ display_line(2, 14, tum_col(optn == 6),
+ "'Hard' input flushing : %s", tum_onoff(hack_flush));
+
+ display_line(7, 16, tum_col(optn == 7), " Alarm type : %-20s",
+ alarm_types[alarm_type]);
+ display_line(7, 17, TERM_WHITE, " Time : ");
+ display_line(26, 17, tum_col(optn == 8), "%02d", alarm_h);
+ display_line(28, 17, TERM_WHITE, ":");
+ display_line(29, 17, tum_col(optn == 9), "%02d", alarm_m);
+ display_line(7, 18, tum_col(optn == 10), " Message : %-51s",
+ alarm_message);
+ display_line(7, 19, tum_col(optn == 11), " Beep : %s",
+ tum_onoff(alarm_beep));
+
+#ifdef FE_DEBUG_INFO
+ display_line(2, 23, tum_col(optn == 23), "Show debug info");
+ max_opt = 12;
+#endif
+
+ switch (optn)
+ {
+ case 12: Term_gotoxy(2, 23);
+ break;
+ case 11: Term_gotoxy(26, 19);
+ break;
+ case 10: Term_gotoxy(26, 18);
+ break;
+ case 9: Term_gotoxy(29, 17);
+ break;
+ case 8: Term_gotoxy(26, 17);
+ break;
+ case 7: Term_gotoxy(26, 16);
+ break;
+ default: Term_gotoxy(26, optn + 8);
+ }
+
+ k = inkey();
+ adj = (k == '4' || k == 'h') ? -1 : (k == '6' || k == 'l') ? 1 : 0;
+
+ switch (k)
+ {
+ case 18: /* Hack: force the screen to update */
+ redraw_mung = 1;
+ k = 27;
+ break;
+ case 's': case 'S':
+ save_choices();
+ display_line(2, 23, TERM_YELLOW, "Options saved. ");
+ Term_fresh();
+ Term_xtra(TERM_XTRA_DELAY, 750);
+ Term_erase(2, 23, 60);
+ break;
+ case '8': case 'k':
+ if (--optn < 0)
+ {
+ optn = max_opt;
+ }
+ break;
+ case '2': case 'j':
+ if (++optn > max_opt)
+ {
+ optn = 0;
+ }
+ break;
+ case 13: case 32: case 't': /* Allow return, space and t to toggle some options */
+ case '4': case 'h':
+ case '6': case 'l':
+ {
+ switch (optn)
+ {
+ case 0: /* Gamma correction */
+ gamma += adj * 0.05;
+ if (gamma > 9.00)
+ {
+ gamma = 9.00;
+ }
+ if (gamma < 0.05)
+ {
+ gamma = 0.05;
+ }
+ Term_xtra(TERM_XTRA_REACT, 0);
+#ifndef FULLSCREEN_ONLY
+ set_gamma_window_state();
+#endif /* FULLSCREEN_ONLY */
+ /* flush(); */
+ Term_fresh();
+ break;
+ case 1: /* Force monochrome */
+ force_mono = !force_mono;
+ if (fullscreen_font)
+ redraw_areaFS(0, 0, 80, 24);
+ else
+ Term_xtra(TERM_XTRA_REACT, 0);
+ /* flush(); */
+ Term_fresh();
+ break;
+ case 2: /* Sound enable / disable */
+ enable_sound = !enable_sound;
+#ifndef FULLSCREEN_ONLY
+ set_sound_window_state();
+#endif /* FULLSCREEN_ONLY */
+ if (enable_sound)
+ {
+ initialise_sound();
+ }
+ break;
+ case 3: /* Sound volume */
+ sound_volume += adj;
+ if (sound_volume < SOUND_VOL_MIN)
+ sound_volume = SOUND_VOL_MIN;
+ if (sound_volume > SOUND_VOL_MAX)
+ sound_volume = SOUND_VOL_MAX;
+#ifndef FULLSCREEN_ONLY
+ set_sound_window_state();
+#endif /* FULLSCREEN_ONLY */
+ break;
+ case 4: /* Start fullscreen */
+ start_fullscreen = !start_fullscreen;
+ if (start_fullscreen)
+ enter_fullscreen_mode();
+ else if (!minimise_memory)
+ leave_fullscreen_mode();
+ break;
+ case 5: /* Start fullscreen */
+ use_glass = !use_glass;
+ if (!use_glass)
+ {
+ if (glass_on)
+ {
+ Hourglass_Off();
+ }
+ glass_on = 0;
+ }
+ break;
+ case 6: /* hack flush */
+ hack_flush = !hack_flush;
+ break;
+ case 7: /* Alarm on/off */
+ alarm_type += adj;
+ if (adj)
+ {
+ alarm_modified = 1;
+ }
+ if (alarm_type > 2)
+ {
+ alarm_type = 0;
+ }
+ if (alarm_type < 0)
+ {
+ alarm_type = 2;
+ }
+ if (!alarm_type && alarm_disp)
+ {
+ ack_alarm();
+ } /* XXXXX Cancel an already active alarm? */
+ break;
+ case 8: /* Alarm hours */
+ alarm_h += adj;
+ if (adj)
+ {
+ alarm_modified = 1;
+ }
+ if (alarm_h < 0)
+ {
+ alarm_h += 24;
+ }
+ if (alarm_h > 23)
+ {
+ alarm_h -= 24;
+ }
+ if (alarm_disp)
+ {
+ ack_alarm();
+ }
+ break;
+ case 9: /* Alarm minutes */
+ alarm_m += adj;
+ if (adj)
+ {
+ alarm_modified = 1;
+ }
+ if (alarm_m < 0)
+ {
+ alarm_m += 60;
+ }
+ if (alarm_m > 59)
+ {
+ alarm_m -= 60;
+ }
+ if (alarm_disp)
+ {
+ ack_alarm();
+ }
+ break;
+ case 10:
+ alarm_modified = 1;
+ do_alarm_message_input(18);
+ break;
+ case 11:
+ alarm_modified = 1;
+ alarm_beep = !alarm_beep;
+ break;
+ case 12:
+ show_debug_info();
+ redraw_mung = 1;
+ k = 27;
+ break;
+ }
+ }
+ }
+ }
+ while (k != 27);
+ }
+ while (redraw_mung);
+
+ /* Rehack the alarm type: */
+ if (alarm_type == 1)
+ {
+ alarm_type = 3;
+ }
+
+ if (alarm_modified)
+ {
+ write_alarm_choices();
+ }
+
+ Term_set_cursor(cursor_state);
+
+ /* Restore the screen */
+ Term_load();
+
+ /* Don't need to preserve FP regs any more */
+ event_mask.data.keepfpregisters = 0;
+
+ /*
+ | Hack: tell the desktop front end that we're done.
+ */
+ user_menu_active = FALSE;
+
+ return 0;
+}
+
+
+
+
+/*--------------------------------------------------------------------------*/
+
+#ifdef USE_FILECACHE
+
+/*
+ | 'Random' File-cacheing for *band.
+ |
+ | Rewritten since as of Zang 225 the mechanism for handling
+ | these files has changed dramatically and the old system
+ | is no longer viable.
+ |
+ | These new functions basically provide an alternative to the
+ | normal my_fopen() (or fopen()) and my_fgets() functions.
+ |
+ | To use the file caching it is therefore necessary to alter
+ | files.c to call cached_fopen(), cached_fclose() and cached_fgets()
+ | rather than the normal functions.
+ |
+ | Note that these funtions will only work for files that are intended
+ | to be read as a series of \n terminated lines of ASCII text using my_fgets().
+ |
+ */
+
+/*
+ | Hack: use the game's dynamic area if possible:
+ */
+#define fc_malloc(X) (g_malloc(X))
+#define fc_free(X) (g_free(X,0))
+
+#ifndef ABBR_FILECACHE
+/*
+ | Make these to do nothing. They'll never
+ | be called anyway. Having them present makes
+ | for neater code later on (ie. we use a variable
+ | rather than the pre-processor to decide whether
+ | to do compression).
+ */
+static int compress_string(char *os, char *s)
+{
+ core("main-acn internal logic error 001");
+ return 0;
+}
+static int decompress_string(char *d, char *s, int max_len)
+{
+ core("main-acn internal logic error 002");
+ return 0;
+}
+static int compressed_length(char *s)
+{
+ core("main-acn internal logic error 003");
+ return 0;
+}
+
+#else
+
+/*
+ | When caching files we try to use some abbreviations.
+ | We use both whole words and pairs of letters.
+ | NB: For this to work, the file must contain only
+ | 7 bit characters.
+ */
+static char *abbrv_w[] =
+{
+ /* These words all begin with a space */
+ " of ", " the ", " you ", " to ", " a ", " says", " is ", " that ", " and ",
+ " your ", " are ", " it ", " be ", " for ", " me", " will ", " in ",
+ " not ", " this ", " have ", " can ", " on ", " my ", " with ", " say ",
+ " all", " by ", " get ", " but ", " just ", " die", " as ", " time ",
+ " if ",
+ " like ",
+ /* These words do not */
+ "I ", "The ", "You ", "They ", "It ", "don", 0
+};
+
+/* Number of words */
+#define FC_ABBRV_NUMWORDS 41
+
+/* Number of them that don't start with a space */
+#define FC_ABBRV_NONSPC 6
+
+/*
+ | NB: No letter pair may start with \0.
+ */
+static char abbrv_lp[] =
+ "e ttht s heiner aoure'\0, anonf sd y r ongofator.\0"
+ "n arllstha wes m ieaisen bl yndtoo yometele d f hve"
+ "ayuralitneelN: chig ilroassaseliti lraa otedbede 'ri" "..u nntno!'ee\0\0";
+
+
+/*
+ | Compress the given string using the abbreviation tables above.
+ | Returns compressed length *including* terminator (it may
+ | be part of an abbreviation, you see...)
+ | Note that we can compress the string in-place, ie. 'os' may be
+ | the same as 's'.
+ */
+static int compress_string(char *os, char *s)
+{
+ char *o, *f, *d;
+ int i;
+
+ o = os;
+
+ while (*s)
+ {
+ int fw, lw;
+ if (*s == ' ')
+ {
+ fw = 0;
+ lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ }
+ else
+ {
+ fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ lw = FC_ABBRV_NUMWORDS;
+ }
+ for (i = fw; i < lw; i++)
+ {
+ d = abbrv_w[i]; /* Word to check against */
+ for (f = s; *f && *f == *d; f++, d++)
+ ;
+ if (*d == 0) /* Match? */
+ {
+ s = *f ? f : f - 1; /* Update string pointer */
+ *o++ = 128 + i; /* store code */
+ break; /* Quit looking for words */
+ }
+ }
+
+ /* Do we need to check the letter pairs? */
+ if (i == lw)
+ {
+ for (i = 0; abbrv_lp[i]; i += 2)
+ {
+ if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
+ {
+ *o++ = 128 + FC_ABBRV_NUMWORDS + i / 2;
+ /* NB: If the next character is the terminator then we're done. */
+ if (!s[1])
+ {
+ return (o - os);
+ }
+ s += 2; /* Quit looking for letters */
+ break;
+ }
+ }
+ /* NB: This next check is only safe because no letter pair starts with a NULL */
+ if (!abbrv_lp[i])
+ *o++ = *s++;
+ }
+ }
+
+ /* Don't forget that terminator! */
+ *o++ = 0;
+
+ return o - os;
+}
+
+/*
+ | As compress_string (above), but stores nothing and
+ | only returns the length of the compressed string.
+ */
+static int compressed_length(char *s)
+{
+ char *f, *d;
+ int i, l;
+
+ l = 0;
+ while (*s)
+ {
+ int fw, lw;
+ if (*s == ' ')
+ {
+ fw = 0;
+ lw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ }
+ else
+ {
+ fw = FC_ABBRV_NUMWORDS - FC_ABBRV_NONSPC;
+ lw = FC_ABBRV_NUMWORDS;
+ }
+ for (i = fw; i < lw; i++)
+ {
+ d = abbrv_w[i];
+ for (f = s; *f && *f == *d; f++, d++)
+ ;
+ if (*d == 0) /* Match? */
+ {
+ s = *f ? f : f - 1; /* Update string pointer */
+ l++; /* increment output length */
+ break; /* Quit looking for words */
+ }
+ }
+
+ /* Do we need to check the letter pairs? */
+ if (i == lw)
+ {
+ for (i = 0; abbrv_lp[i]; i += 2)
+ {
+ if (s[0] == abbrv_lp[i] && s[1] == abbrv_lp[i + 1])
+ {
+ l++; /* increment output length */
+ /* NB: If the next character is the terminator then we're done. */
+ if (!s[1])
+ {
+ return l;
+ }
+ s += 2; /* Quit looking for letters */
+ break;
+ }
+ }
+ /* NB: This next check is only safe because no letter pair starts with a NULL */
+ if (!abbrv_lp[i])
+ {
+ l++;
+ s++;
+ }
+ }
+ }
+ /* Don't forget that terminator! */
+ return l + 1;
+}
+
+
+
+/*
+ | Decompress the given string 's' into the buffer at 'd'.
+ | At most, max_len characters (incl. \0 terminator) will be
+ | written into d.
+ | Returns the length of 's'.
+ */
+static int decompress_string(char *d, char *s, int max_len)
+{
+ char *os = s;
+
+ while (max_len > 1)
+ {
+ int nc = *s++; /* Get next character */
+
+ if (nc < 128) /* Is it a plain character? */
+ {
+ if (0 == (*d++ = nc))
+ {
+ break;
+ }
+ max_len--;
+ }
+ else /* Abbreviation to expand. */
+ {
+ if (nc >= FC_ABBRV_NUMWORDS + 128) /* Letter pair? */
+ {
+ *d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2];
+ if (0 ==
+ (*d++ = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128)) * 2 + 1]))
+ break;
+ max_len -= 2;
+ }
+ else /* It's a word */
+ {
+ char *ws = abbrv_w[nc - 128];
+ while (*ws && max_len > 1)
+ {
+ *d++ = *ws++;
+ max_len--;
+ }
+ }
+ }
+ }
+
+ /* Skip over the rest of the abbreviated string if we ran out of space */
+ if (max_len <= 1) /* Out of space? */
+ {
+ int nc;
+ *d = 0; /* Terminate */
+ do
+ {
+ nc = *s++; /* Next char */
+ if (nc >= 128 + FC_ABBRV_NUMWORDS) /* Ignore words */
+ nc = abbrv_lp[(nc - (FC_ABBRV_NUMWORDS + 128) * 2) + 1]; /* Only check 2nd letter of pair */
+ }
+ while (nc);
+ }
+ return s - os; /* Length of abbreviated string */
+}
+
+#endif /* ABBR_FILECACHE */
+
+
+/* Each entry in the cache looks like this: */
+typedef struct fce_
+{
+ char *name; /* canonical pathname of file */
+ char *text; /* text (be it compressed or not) */
+ char *eof; /* byte beyond the last byte of text */
+ int used; /* access counter when the file was last used */
+ int compressed; /* compression method, (ie. 0 for none or 1 for abbreviations) */
+}
+FileCacheEntry;
+
+/*
+ | The handles we chuck around are pointers to one of these structs.
+ | Note that since we actually return (and take) |FILE*|s we just
+ | compare the value of a |FILE*| with the limits of the array of
+ | |CachedFileHandle|s to decide whether its 'ours' or a genuine
+ | (ie. stdio) file handle. This /is/ pretty lame, I know...
+ */
+typedef struct cfh_
+{
+ char *ptr; /* sequential file pointer, as it were */
+ FileCacheEntry *fce; /* ->the file-cache entry data */
+}
+CachedFileHandle;
+
+#define MAX_OPEN_CACHED_FILES 16 /* We allow up to 16 of these files open at once */
+#define MAX_CACHE_ENTRIES 64 /* We allow up to 64 cache entries */
+
+static FileCacheEntry *file_cache; /* to be used as file_cache[MAX_CACHE_ENTRIES] */
+static CachedFileHandle *cached_file_handle; /* to be used as cached_file_handle[MAX_OPEN_CACHED_FILES] */
+static int file_cache_initd = 0; /* Is the cache initialised? */
+static int file_cache_size = 0; /* Total size of the cached files */
+static int fc_access_counter = 1; /* incremented on each cache access */
+/*
+ | Pre-calculate max. possible value of a FILE* (ie. address in memory)
+ | that could be a valid |CachedFileHandle*|.
+ */
+static FILE *max_cfh_addr; /* == (FILE*) (&(cached_file_handle[MAX_OPEN_CACHED_FILES-1])) */
+
+/*
+ | Initialise the file cache
+ */
+static void init_file_cache(void)
+{
+ int i;
+
+ /* Allocate storage */
+ file_cache = fc_malloc(MAX_CACHE_ENTRIES * sizeof(FileCacheEntry));
+ cached_file_handle =
+ fc_malloc(MAX_OPEN_CACHED_FILES * sizeof(CachedFileHandle));
+
+ if (!file_cache || !cached_file_handle)
+ {
+ /* Disable file-caching */
+ if (file_cache)
+ {
+ fc_free(file_cache);
+ }
+ if (cached_file_handle)
+ {
+ fc_free(cached_file_handle);
+ }
+ use_filecache = 0;
+ }
+ else
+ {
+ /* Initialise the cache */
+ for (i = 0; i < MAX_CACHE_ENTRIES; i++)
+ file_cache[i].name = NULL;
+ for (i = 0; i < MAX_OPEN_CACHED_FILES; i++)
+ cached_file_handle[i].fce = NULL;
+ fc_access_counter = 1;
+ file_cache_size = 0;
+ max_cfh_addr =
+ (FILE *)(&(cached_file_handle[MAX_OPEN_CACHED_FILES - 1]));
+ }
+ file_cache_initd = 1;
+}
+
+
+/*
+ | Helper: take a copy of the string and return a pointer to it
+ */
+static char *string_cpy(char *s)
+{
+ char *d = fc_malloc(strlen(s) + 1L);
+ if (d)
+ {
+ strcpy(d, s);
+ }
+ return d;
+}
+
+
+/*
+ | Cache the specified file, returning either the cache entry
+ | that it has been cached at, or NULL for failure.
+ |
+ | Note that for the abbreviated file cache a temporary file
+ | is used to allow the compression to be applied just once.
+ | (otherwise it has to be done twice - once to determine the
+ | eventual compressed size and once to actually store and compress
+ | it).
+ */
+static FileCacheEntry *cache_file(char *name)
+{
+ int i, size = 0;
+ FILE *fp;
+ char buffer[1024];
+ char *d;
+
+ FILE *tf = NULL; /* Used if abbr_filecache and abbr_tmpfile are set */
+ char cfn[1024]; /* Used if abbr_filecache and abbr_tmpfile are set */
+
+ /* Find the first free slot in the cache */
+ for (i = 0; i < MAX_CACHE_ENTRIES; i++)
+ if (!file_cache[i].name)
+ break;
+
+ /* No more entries? */
+ if (i >= MAX_CACHE_ENTRIES)
+ {
+ return NULL;
+ }
+
+ /* Set up the info on the file */
+ if ((file_cache[i].name = string_cpy(name)) == NULL)
+ {
+ return NULL;
+ }
+
+ /* Open the file */
+ fp = my_fopen(name, "r");
+ if (!fp)
+ {
+ fc_free(file_cache[i].name);
+ file_cache[i].name = 0;
+ return NULL;
+ }
+
+ /* Open/create tempfile if need be: */
+ if (abbr_filecache && abbr_tmpfile)
+ {
+ /* Hack: Form the pathname of the cached compressed file (in canonical form) */
+ sprintf(cfn, "%s%s", scrap_path,
+ riscosify_name(name + strlen(resource_path)));
+ /* Ensure that that particular directory exists... */
+ ensure_path(cfn);
+ /* Check whether cache file is out of date */
+ if (file_is_newer(riscosify_name(name), cfn))
+ {
+ tf = fopen(cfn, "wb");
+ size = 0;
+ }
+ else
+ {
+ tf = fopen(cfn, "rb");
+ if (tf)
+ {
+ size = myFile_Size(cfn);
+ }
+ }
+ }
+
+ /* If we don't have the cached file (but want it), compress the source text to it */
+ if (tf)
+ {
+ if (!size)
+ {
+ int k;
+ while (!my_fgets(fp, buffer, sizeof(buffer)))
+ {
+ if (smart_filecache && (!*buffer || *buffer == '#'))
+ continue;
+ k = compress_string(buffer, buffer);
+ if (fwrite(buffer, 1, k, tf) != k)
+ {
+ fclose(tf);
+ remove(cfn);
+ core("error writing tempfile");
+ }
+ size += k;
+ }
+ fclose(tf);
+ tf = fopen(cfn, "rb");
+ }
+ }
+ else
+ {
+ /* Count the number of bytes */
+ while (!my_fgets(fp, buffer, sizeof(buffer)))
+ {
+ if (smart_filecache && (!*buffer || *buffer == '#'))
+ continue;
+ if (abbr_filecache)
+ size += compressed_length(buffer);
+ else
+ size += strlen(buffer) + 1;
+ }
+ }
+
+ /* Close the (source) file */
+ my_fclose(fp);
+
+ /* Allocate enough storage for the text */
+ file_cache[i].text = fc_malloc(size + 1L);
+ if (!file_cache[i].text)
+ {
+ fc_free(file_cache[i].name);
+ file_cache[i].name = 0;
+ if (tf)
+ {
+ fclose(tf);
+ }
+ return NULL;
+ }
+
+ /* Do we have a tempfile to load? */
+ if (tf)
+ {
+ if (fread(file_cache[i].text, 1, size, tf) != size)
+ core("error reading tempfile");
+ fclose(tf);
+ }
+ else
+ {
+ /* Re-open the file... */
+ fp = my_fopen(name, "r");
+ if (!fp)
+ {
+ fc_free(file_cache[i].name);
+ fc_free(file_cache[i].text);
+ file_cache[i].name = 0;
+ return NULL;
+ }
+
+ /* And read it into the buffer... */
+ d = file_cache[i].text;
+ while (!my_fgets(fp, buffer, sizeof(buffer)))
+ {
+ if (smart_filecache && (!*buffer || *buffer == '#'))
+ continue;
+ if (abbr_filecache)
+ d += compress_string(d, buffer);
+ else
+ {
+ strcpy(d, buffer);
+ d += strlen(buffer) + 1;
+ }
+ }
+
+ if ((d - file_cache[i].text) != size)
+ {
+ debug("Calculated size is %d, pointer offset is %d", size,
+ (d - file_cache[i].text));
+ core("Cached file is larger than calculated!");
+ }
+
+ /* Close the file */
+ my_fclose(fp);
+ }
+
+ /* Set up the 'last accessed' value, etc. */
+ file_cache[i].used = fc_access_counter++;
+ file_cache[i].eof = file_cache[i].text + size;
+ file_cache[i].compressed = abbr_filecache;
+ file_cache_size += size;
+
+ /* Return success */
+ return &(file_cache[i]);
+}
+
+
+/*
+ | Discard a file from the cache
+ */
+static void discard_cached_file(int i)
+{
+ if (!file_cache[i].name)
+ {
+ return;
+ } /* invalid request */
+ fc_free(file_cache[i].text);
+ fc_free(file_cache[i].name);
+ file_cache_size -= (file_cache[i].eof) - (file_cache[i].text);
+ file_cache[i].name = 0;
+}
+
+
+/*
+ | Attempt to flush as much of the cache as required
+ | to bring it within the size limit.
+ | If protect != 0 then that entry in the cache won't be flushed.
+ */
+static void flush_file_cache(FileCacheEntry * protect)
+{
+ int i, j, done;
+ int oldest_u, oldest_e;
+ FileCacheEntry *fce;
+ int needed = (4 << 10); /* Hack: try to free at least 4K */
+
+ done = (file_cache_size + needed) <= max_file_cache_size;
+
+ while (!done)
+ {
+ oldest_u = fc_access_counter;
+ oldest_e = -1;
+
+ fce = file_cache;
+ /* Find oldest entry that isn't in use */
+ for (i = 0; i < MAX_CACHE_ENTRIES; fce++, i++)
+ {
+ if (fce == protect)
+ {
+ continue;
+ } /* Hack ;) */
+ if (fce->name) /* Is this cache slot full? */
+ {
+ for (j = 0; j < MAX_OPEN_CACHED_FILES; j++)
+ if (cached_file_handle[j].fce == fce)
+ break;
+ if (j < MAX_OPEN_CACHED_FILES)
+ continue; /* Cached file is still open */
+ if (fce->used < oldest_u)
+ {
+ oldest_e = i;
+ oldest_u = file_cache[i].used;
+ }
+ }
+ }
+
+ if (oldest_e < 0)
+ done = 1; /* We can flush nothing more */
+ else
+ {
+ discard_cached_file(oldest_e);
+ done = (file_cache_size + needed) <= max_file_cache_size;
+ }
+ }
+}
+
+
+/*
+ | Locate the specified file within the cache.
+ | Returns NULL if the file is not cached
+ */
+static FileCacheEntry *find_cached_file(char *name)
+{
+ int i;
+ FileCacheEntry *fce = file_cache;
+
+ for (i = 0; i < MAX_CACHE_ENTRIES; i++, fce++)
+ {
+ if (fce->name)
+ if (streq(fce->name, name))
+ return fce;
+ }
+ return NULL;
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+/* Externally visible file cache stuff */
+/*--------------------------------------------------------------------------*/
+
+/*
+ | Open a file...
+ | Returns the file cache handle of the file, or NULL for failure.
+ | Note that if mode is anything other than "r" the call defers to
+ | my_fopen().
+ |
+ | NB: The returned handle is almost certainly *NOT* a |FILE*|
+ | (although it may be if the cache cannot accomodate the file).
+ |
+ | Therefore, you *MUST* ensure that any file opened with cached_fopen()
+ | is only ever accessed via cached_fgets() and cached_fclose().
+ |
+ | Failure to do so will result in, ahem, unpleasantness. Extreme
+ | unpleasantness.
+ */
+FILE *cached_fopen(char *name, char *mode)
+{
+ FileCacheEntry *fcs = NULL;
+ int fch;
+
+ if (strcmp(mode, "r") || !use_filecache)
+ return my_fopen(name, mode);
+
+ if (!file_cache_initd)
+ {
+ init_file_cache();
+ }
+
+ if (max_file_cache_size >= 0)
+ {
+ /* Find a free cache entry */
+ for (fch = 0; fch < MAX_OPEN_CACHED_FILES; fch++)
+ if (!cached_file_handle[fch].fce)
+ break;
+
+ /* Out of handles? */
+ if (fch >= MAX_OPEN_CACHED_FILES)
+ return my_fopen(name, mode);
+
+ /* Is the file already cached? */
+ fcs = find_cached_file(name);
+ if (!fcs)
+ {
+ /* File wasn't cached, so cache it */
+ flush_file_cache(NULL); /* Clean stuff out of the cache if need be */
+ fcs = cache_file(name); /* Cache the new file */
+ flush_file_cache(fcs); /* Flush, but keep the latest file */
+ }
+ }
+
+ /* Did we fail to cache the file? */
+ if (!fcs)
+ {
+ return my_fopen(name, mode);
+ }
+
+ /* File was cached OK */
+ cached_file_handle[fch].ptr = fcs->text; /* Init sequential pointer */
+ cached_file_handle[fch].fce = fcs; /* Cache block pointer */
+ fcs->used = fc_access_counter++; /* Opening the file counts as an access */
+
+ return (FILE *)(&cached_file_handle[fch]);
+}
+
+
+/*
+ | Close a file
+ */
+errr cached_fclose(FILE *fch_)
+{
+ CachedFileHandle *fch;
+
+ /* Is the FILE* genuine? */
+ if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
+ return my_fclose(fch_);
+
+ fch = (CachedFileHandle *) fch_;
+
+ /* Check for "Ooopses": */
+ if (fch->fce == NULL)
+ core("cached_fclose called on a non-open file handle");
+
+ flush_file_cache(NULL); /* Clean out the cache if need be */
+ fch->fce = NULL; /* Mark file handle as inactive */
+
+ return 0;
+}
+
+
+/*
+ | Do the my_fgets thing on a file
+ */
+errr cached_fgets(FILE *fch_, char *buffer, int max_len)
+{
+ CachedFileHandle *fch;
+ char *eof;
+ char *ptr;
+
+ /* Is the FILE* genuine? */
+ if ((fch_ < (FILE *)cached_file_handle) || (fch_ > max_cfh_addr))
+ return my_fgets(fch_, buffer, max_len);
+
+ fch = (CachedFileHandle *) fch_;
+
+ /* Check for "Oopses": */
+ if (!file_cache_initd)
+ core("cached_fgets() on uninitialised file-cache");
+ if (!fch->fce)
+ core("cached_fgets called for a un-open file");
+
+ eof = fch->fce->eof;
+ ptr = fch->ptr;
+
+ /* Out of bounds? */
+ if (ptr >= eof)
+ {
+ return 1;
+ } /* Read failed */
+
+ /*
+ | Read the next line, up to \0 (which would have v=been \n in the original file),
+ | or max_len-1 characters
+ */
+ if (fch->fce->compressed)
+ ptr += decompress_string(buffer, ptr, max_len);
+ else
+ {
+ if (eof - ptr < max_len)
+ {
+ max_len = eof - ptr;
+ }
+ for (; max_len >= 1; max_len--)
+ if ((*buffer++ = *ptr++) == 0)
+ break;
+ *buffer = 0; /* terminate (paranoia) */
+ }
+
+ /* Update sequential pointer */
+ fch->ptr = ptr;
+
+ return 0;
+}
+
+#endif /* USE_FILECACHE */
+
+
+/*
+ | This section deals with checking that the .raw files are up to date
+ | wrt to the .txt files. Note that this function won't work for RISC OS 2
+ | (due to the lack of OS_Args 7) so it simply returns 0 to indicate that
+ | the file isn't OOD.
+ |
+ | For this to work, the equivalent function (in init2.c) needs to be
+ | #if-d out (and this function should be declared). You'll probably
+ | also need to zap the UNIX #includes at the top of the file
+ */
+
+extern errr check_modification_date(int fd, cptr template_file)
+{
+ char raw_buf[1024];
+ char txt_buf[1024];
+ int i;
+ os_error *e;
+
+ if (os_version() < 300)
+ {
+ return 0;
+ }
+
+ /* Use OS_Args 7 to find out the pathname 'fd' refers to */
+ e = SWI(6, 0, SWI_OS_Args,
+ /* In: */
+ 7, /* Get path from filehandle */
+ fd, /* file handle */
+ raw_buf, /* buffer */
+ 0, 0, /* unused */
+ 1024 /* size of buffer */
+ /* No output regs used */
+ );
+ if (e)
+ {
+ core(e->errmess);
+ }
+
+ /* Build the path to the template_file */
+ path_build(txt_buf, sizeof(txt_buf), ANGBAND_DIR_EDIT, template_file);
+
+ i = file_is_newer(riscosify_name(txt_buf), raw_buf);
+
+ return i;
+}
+
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ | This is the hideous IClear hack. Basically what we do is to take a copy
+ | of the module and kill it in the RMA. Then, on return to the desktop
+ | we reinstate the module.
+ |
+ | NB: This is truly, truly evil.
+ */
+
+static int *iclear_module = NULL;
+
+/*
+ | Start the IClear hack
+ */
+static void iclear_hack(void)
+{
+ os_error *e;
+ int code = 0;
+ int *i;
+
+ /* Get base address of module */
+ e = SWI(2, 4, SWI_OS_Module, 18, "IClear", 0, 0, 0, &code);
+ if (e || !code)
+ return;
+
+ /* Module size is at code!-4 */
+ i = (int *)code;
+ iclear_module = malloc(i[-1] + 4);
+ if (!iclear_module)
+ return;
+
+ /* Copy the module */
+ *iclear_module = i[-1];
+ memcpy(iclear_module + 1, (void *)code, i[-1]);
+
+ /* Kill the current version */
+ e = SWI(2, 0, SWI_OS_Module, 4, "IClear");
+ if (e)
+ {
+ free(iclear_module);
+ iclear_module = NULL;
+ }
+}
+
+
+/*
+ | Remove the IClear hack
+ */
+static void remove_iclear_hack(void)
+{
+ os_error *e;
+
+ if (!iclear_module)
+ return;
+
+ e = SWI(3, 0, SWI_OS_Module, 11, iclear_module + 1, *iclear_module);
+ if (e)
+ debug("Failed to reinstall IClear: %s", e->errmess);
+
+ free(iclear_module);
+ iclear_module = NULL;
+}
+
+
+
+/*--------------------------------------------------------------------------*/
+
+/* Alarm functions */
+static int alarm_ackd = 0; /* has the alarm been acknowledged? */
+static window_handle aw = 0; /* alarm window */
+
+/*
+ | Is the alarm due to go off, ie. is it enabled, and if so
+ | does the current time match the alarm time?
+ */
+static void check_alarm()
+{
+ time_t t;
+ struct tm *lt;
+
+ alarm_lastcheck = Time_Monotonic();
+
+ time(&t);
+ lt = localtime(&t);
+ if (lt->tm_hour == alarm_h && lt->tm_min == alarm_m)
+ {
+ if (!alarm_ackd) alarm_disp = 1;
+ }
+ else
+ {
+ alarm_ackd = 0;
+ }
+
+ /* Hack: if the alarm has already been acknowledged then don't re-trigger it */
+ if (alarm_ackd)
+ {
+ alarm_disp = 0;
+ }
+
+ /* Hack: if the alarm should make a noise, then make one: */
+ if (alarm_disp && alarm_beep == 1)
+ {
+ static unsigned int last_beep = 0;
+ unsigned int t = Time_Monotonic();
+ if (t >= last_beep + 100)
+ {
+ Sound_SysBeep();
+ last_beep = t;
+ }
+ }
+
+ /*
+ | If we're in the desktop then fire the alarm off if need be.
+ | If we aren't then do nothing - the fullscreen bored() function
+ | will take care of the alarm.
+ */
+#ifndef FULLSCREEN_ONLY
+ if (!fullscreen_font && alarm_disp)
+ trigger_alarm_desktop();
+#endif /* FULLSCREEN_ONLY */
+}
+
+
+static void ack_alarm(void)
+{
+ if (aw)
+ {
+ Window_Delete(aw);
+ aw = 0;
+ }
+ alarm_ackd = 1;
+ alarm_disp = 0;
+
+ if (alarm_type == 3)
+ {
+ /* One shot alarm */
+ alarm_type = 0;
+ write_alarm_choices();
+ }
+
+}
+
+#ifndef FULLSCREEN_ONLY
+/*
+ | Click in the (desktop) alarm window
+ */
+static BOOL Hnd_AlarmClick(event_pollblock * pb, void *ref)
+{
+ ack_alarm();
+ return TRUE;
+}
+
+
+/*
+ | The alarm has gone off in the desktop
+ */
+static void trigger_alarm_desktop(void)
+{
+ char buffer[120];
+ if (aw)
+ return;
+
+ aw = Window_Create("alarm", template_TITLEMIN);
+ if (!aw)
+ {
+ core("failed to create Alarm window!");
+ }
+ sprintf(buffer, "Alarm from %s", VARIANT);
+ Window_SetTitle(aw, buffer);
+ Event_Claim(event_CLICK, aw, 0, Hnd_AlarmClick, NULL);
+ Event_Claim(event_CLOSE, aw, event_ANY, Hnd_AlarmClick, NULL);
+
+ Icon_printf(aw, 1, "An alarm was set for %02d:%02d", alarm_h, alarm_m);
+ Icon_SetText(aw, 2, alarm_message);
+ Window_Show(aw, open_CENTERED);
+}
+
+#endif /* FULLSCREEN_ONLY */
+
+
+/*--------------------------------------------------------------------------*/
+
+#ifndef FE_DEBUG_INFO
+static void show_debug_info(void)
+{
+ core("main-acn internal logic error 004");
+}
+#else
+
+static int debug_cx = 0;
+static int debug_cy = 0;
+static int debug_cl = TERM_WHITE;
+static int debug_sl = 0;
+
+
+static void debug_cls(void)
+{
+ Term_clear();
+ debug_cx = debug_cy = debug_sl = 0;
+}
+
+static void debug_tcol(int c)
+{
+ debug_cl = c;
+}
+
+
+static void debug_scroll(void)
+{
+ char **c = ((term_data *)Term)->t.scr->c; /* char array [24][80] */
+ byte **a = ((term_data *)Term)->t.scr->a; /* attr array [24][80] */
+ int cc;
+ char tmp[82];
+ int y, x, p;
+
+ cc = a[1][0];
+
+ for (y = 1; y < 23; y++)
+ {
+ for (x = p = 0; x < 80; x++)
+ {
+ if (a[y][x] != cc)
+ {
+ tmp[p] = 0;
+ Term_putstr(x - p, y - 1, p, cc, tmp);
+ p = 0;
+ cc = a[y][x];
+ }
+ tmp[p++] = c[y][x];
+ }
+ Term_putstr(x - p, y - 1, p, cc, tmp);
+ }
+
+ Term_erase(0, 22, 80);
+}
+
+
+static void debug_print_line(char *l)
+{
+ char *le;
+ int cr = 0;
+
+ /* Handle scrolling */
+ if (debug_cy > 22)
+ {
+ debug_cy = 22;
+ if (--debug_sl < 0)
+ {
+ int k;
+ display_line(0, 23, TERM_YELLOW, "[RET one line, SPC one page]");
+ do
+ {
+ k = inkey();
+ }
+ while (k != 32 && k != 13);
+ Term_erase(0, 23, 79);
+ debug_sl = k == 32 ? 21 : 0;
+ }
+ debug_scroll();
+ }
+
+ /* Hack: check for NL */
+ for (le = l; *le; le++)
+ if (*le == '\n')
+ {
+ cr = 1;
+ break;
+ }
+
+ /* display text */
+ Term_putstr(debug_cx, debug_cy, le - l, debug_cl, l);
+
+ /* move cursor */
+ if (!cr)
+ {
+ debug_cx += (le - l);
+ if (debug_cx >= 80)
+ {
+ cr = 1;
+ }
+ }
+ if (cr)
+ {
+ debug_cx = 0;
+ debug_cy += 1;
+ }
+
+ Term_gotoxy(debug_cx, debug_cy);
+
+}
+
+
+static int debug_next_line(char *lb, char **t, int cx)
+{
+ int i = 0;
+ char *lt = *t;
+
+ if (!*lt)
+ {
+ return -1;
+ } /* Out of text */
+
+ while (*lt && cx < 80)
+ {
+ lb[i] = *lt++;
+
+ if (lb[i] == '\n') /* New line */
+ {
+ cx = 0; /* Cursor x will be 0 after displaying */
+ i++; /* Keep the \n in the output */
+ break; /* All done */
+ }
+ else if (lb[i] == '\t') /* Tab */
+ {
+ while (cx < 80)
+ {
+ lb[i++] = ' ';
+ cx++;
+ if ((cx & 7) == 0)
+ {
+ break;
+ }
+ }
+ }
+ else /* Anything else */
+ {
+ cx++;
+ i++;
+ }
+ }
+
+ lb[i] = 0; /* terminate line buffer */
+ *t = lt; /* update text pointer */
+ return cx; /* return cursor x after printing */
+}
+
+static void debug_printf(char *fmt, ...)
+{
+ char buffer[1024];
+ char line[82];
+ va_list ap;
+ char *p = buffer;
+
+ va_start(ap, fmt);
+ vsprintf(buffer, fmt, ap);
+ va_end(ap);
+
+ /* Now split the string into display lines */
+ while (debug_next_line(line, &p, debug_cx) >= 0)
+ debug_print_line(line);
+}
+
+
+static void debug_version_info(void)
+{
+ debug_tcol(TERM_YELLOW);
+
+ debug_printf("\n\nMisc. Info:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tVariant name = \"%s\"\n", VARIANT);
+ debug_printf("\tFront-end version: %s\n", PORTVERSION);
+ debug_printf("\tFront-end compiled: %s %s\n", __TIME__, __DATE__);
+ debug_printf("\tCompile time flags:\n");
+
+#ifdef USE_FILECACHE
+ debug_printf("\t\tUSE_FILECACHE\n");
+#endif
+
+#ifdef ABBR_FILECACHE
+ debug_printf("\t\tABBR_FILECACHE\n");
+#endif
+
+#ifdef SMART_FILECACHE
+ debug_printf("\t\tSMART_FILECACHE\n");
+#endif
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nResource path:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t\"%s\"\n", resource_path);
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nTempfile path:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t\"%s\"\n", scrap_path);
+ debug_printf("\tScrapfiles are %s deleted at exit.\n",
+ (flush_scrap ? "" : "NOT"));
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nChoices files:\n");
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tDesired files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tPrimary (r/w): \"%s\"\n", choices_file[CHFILE_WRITE]);
+ debug_printf("\t Fallback (r): \"%s\"\n", choices_file[CHFILE_READ]);
+ debug_printf("\t Mirror (r/w): \"%s\"\n", choices_file[CHFILE_MIRROR]);
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tActual files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t Write: \"%s\"\n", find_choices(TRUE));
+ debug_printf("\t Read: \"%s\"\n", find_choices(FALSE));
+
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nAlarm files:\n");
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tDesired files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tPrimary (r/w): \"%s\"\n", alarm_file[CHFILE_WRITE]);
+ debug_printf("\t Fallback (r): \"%s\"\n", alarm_file[CHFILE_READ]);
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tActual files:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\t Write: \"%s\"\n", find_alarmfile(TRUE));
+ debug_printf("\t Read: \"%s\"\n", find_alarmfile(FALSE));
+#ifdef USE_DA
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nDynamic areas:\n");
+ debug_tcol(TERM_WHITE);
+ debug_printf("\tFontcache DA = %d\t", font_area);
+ debug_printf("size = %d\theap size = %d\n", font_area_size, font_heap_size);
+ debug_printf("\t ralloc DA = %d\t", game_area);
+ debug_printf("size = %d\theap size = %d\n", game_area_size, game_heap_size);
+#endif
+}
+
+
+static void debug_filecache_info(void)
+{
+#ifndef USE_FILECACHE
+ debug_tcol(TERM_L_DARK);
+ debug_printf("File cache disabled at compile time.\n");
+#else
+ int j, k;
+ int t, cs, ucs, cf;
+
+ cf = cs = ucs = j = k = 0; /* To stop an usused warning if USE_FILECACHE is undefined */
+ t = strlen(resource_path);
+
+ if (!file_cache_initd)
+ {
+ init_file_cache();
+ } /* Paranoia */
+ debug_tcol(TERM_YELLOW);
+ debug_printf("\nFilecache contents:\n");
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("Flags: Smart=%d; Abbrv=%d; Slave=%d; Enable=%d\n",
+ smart_filecache, abbr_filecache, abbr_tmpfile, use_filecache);
+ debug_tcol(TERM_SLATE);
+ if (smart_filecache || abbr_filecache)
+ debug_printf("\t\t%3s %6s/%-6s %6s %6s Path (relative to lib/)\n",
+ "Hnd", "Cache", "Disc", "Time", "Status");
+ else
+ debug_printf("\t\t%3s %6s %6s %6s Path (relative to lib/)\n", "Hnd",
+ "Size", "Time", "Status");
+
+ for (j = 0; j < MAX_CACHE_ENTRIES; j++)
+ {
+ FileCacheEntry *fce = &(file_cache[j]);
+ if (fce->name)
+ {
+ cf++;
+ debug_tcol(TERM_L_GREEN);
+ debug_printf("\t\t%3d ", j);
+ debug_tcol(TERM_L_UMBER);
+ if (!smart_filecache && !abbr_filecache)
+ debug_printf("%6d ", fce->eof - fce->text);
+ else
+ {
+ debug_printf("%6d/", fce->eof - fce->text);
+ k = myFile_Size(riscosify_name(fce->name));
+ debug_printf("%-6d ", k);
+ if (k > 0)
+ {
+ ucs += k;
+ }
+ }
+ cs += fce->eof - fce->text;
+ debug_printf("%6d ", fce->used);
+ for (k = 0; k < MAX_OPEN_CACHED_FILES; k++)
+ if (cached_file_handle[k].fce == fce)
+ break;
+ debug_tcol(TERM_RED);
+ debug_printf("%-6s ", k < MAX_OPEN_CACHED_FILES ? "Open" : "");
+ debug_tcol(TERM_L_UMBER);
+ debug_printf("%s\n", fce->name + t);
+ }
+ }
+
+ debug_tcol(TERM_L_BLUE);
+ debug_printf("\tTotal:\t%3d ", cf);
+ if (ucs)
+ debug_printf("%6d/%-6d\n", cs, ucs);
+ else
+ debug_printf("%6d\n", cs);
+ debug_tcol(TERM_BLUE);
+#endif /* USE_FILECACHE */
+}
+
+
+static void show_debug_info(void)
+{
+ int k;
+ /* blank the term */
+ debug_cls();
+
+ /* Repeatedly prompt for a command */
+ do
+ {
+ debug_tcol(TERM_VIOLET);
+ debug_printf("\nInfo: (V)ersion, (F)ilecache, ESC=exit ");
+ do
+ {
+ k = inkey();
+ switch (k)
+ {
+ case 'v': case 'V': debug_version_info();
+ break;
+ case 'f': case 'F': debug_filecache_info
+ ();
+ break;
+ case 27: break;
+ default: k = 0;
+ }
+ }
+ while (!k);
+ }
+ while (k != 27);
+
+ Term_clear();
+}
+
+#endif /* FE_DEBUG_INFO */
+
+#endif /* __riscos */
+
+