diff options
Diffstat (limited to 'src/main-ros.c')
-rw-r--r-- | src/main-ros.c | 8670 |
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 */ + + |