frotz-2.44/0000755000175000017500000000000012527052365007617 500000000000000frotz-2.44/TODO0000644000175000017500000000252512527045477010241 00000000000000Rework os_process_arguments() in src/curses/ux_init.c to make Frotz more convenient to use as a BBS door or MUD module. Add mouse support through. Not sure how useful this would be, but someone did ask. Remove debug options from command line. They should be set through the hotkey menu instead Hotkey menu needs work. Blink the screen instead of ringing the bell when in quiet mode. Add zlib support to allow Frotz to read gzipped story files. Add support for debug verbs (teleport, pilfer, bamf, (frotz|futz|lumen), tail, travis, lummox, systolic, lingo, spiel, rooms, items, omap, embezzle). I really like the Rezrov's cheat functions. Give the hotmenu a full ncurses-based interface. This would allow MUCH more flexibility in configuring Frotz. More sensible handling of saves, transcripts, and game files. Curses menu to select desired game. Current working code heavily borrowed from Alan Shutko's "int-fiction" frontend program. Blorb gamefile support. Done for curses. Still working on DOS. Blorb sound support on all platforms. Full support will probably require a sound driver which can accept multiple inputs simultanously (such as esound or ALSA). Some sort of automatic endian-detection for the blorb code. An X11 graphical interface using GTK widgets (thus allowing V6 full support). Cocoa support for OSX. frotz-2.44/INSTALL0000644000175000017500000002036212527045477010601 00000000000000=========================================================================== --------------------------------------------------------------------------- | READ THIS FILE ALL THE WAY THROUGH BEFORE ATTEMPTING TO INSTALL FROTZ | --------------------------------------------------------------------------- =========================================================================== Frotz was originally written specifically for MS/PC DOS. When it was ported to Unix, it was done mainly with Linux in mind. Since then, Frotz has been written with an aim to compile and run smoothly on as many platforms as possible. The source is rather generic C code and runs well on pretty much all current Unixen. These are the only three things needed to compile and run Frotz: * Some variant of Unix with an ANSI C compiler (gcc works fine) * A POSIX-compliant version of make (GNU make will do) * A reasonably good SYSV derived curses library (ncurses is best) Frotz can be compiled without curses, leaving you with a dumb-mode interface. This option is provided for hysterical raisins and for those who find it useful for web-based front-ends. Read the DUMB file for more information. Linux uses ncurses, so you're safe there. The version of curses that comes with NetBSD 1.6.x and later is good too. Earlier versions lacked certain features needed by Frotz. I don't know about the other *BSD curses. You can download ncurses from ftp://ftp.gnu/org/gnu/ncurses/. If you insist on using the vendor-supplied curses library, see the platform-specific info below. It's always a good idea to have the GNU version of make(1) around. If you want sound support, you'll need the OSS drivers (found on Linux machines and some *BSD machines). Support for other drivers is in the works. ======================= Editing the Makefile || ======================= You should take a look at the Makefile, which is found in the src/ subdirectory. Read the comments. It's pretty self-explanatory. This is where you define your compiler, where Frotz will be installed, where your curses (or ncurses) library is, if you want sound support, and so on. If this is too much for you, try installing by NetBSD's pkgsrc, FreeBSD's ports, RPM, DEB, or whatever packaging scheme suits you. ================================= Compiling and installing Frotz || ================================= Make sure you're in the directory that was created when you untarred the Frotz tarball. Type "make" to compile Frotz. If you get any showstopping complaints, it's most likely because you didn't tell make where your curses library is, where the curses header is, if you're using plain old curses or ncurses, or you're trying to use sound on an unsupported platform. If you get any showstopping complaints, it's most likely because the curses library on your computer doesn't supply some critical functionality and you don't have ncurses to fall back on. Once the compile is complete, make sure you have the correct permissions to write where you want Frotz installed, then type "make install". To uninstall Frotz, type "make uninstall". If you don't have permission to install Frotz, you can just put the resulting executable somewhere like $HOME/bin and add that directory to your $PATH. For compiling, installing, and uninstalling dumb frotz, use "make dumb", "make install_dumb", and "make uninstall_dumb". ======================================== Installing and playing games on Frotz || ======================================== If you've unfamiliar with Infocom-style text adventures, you should probably stop here and read the file HOW_TO_PLAY. Then come back and continue. Now that you have Frotz installed, you'll probably want to play some of those ultra-nifty text adventures on it. These games come in files which are compiled programs that run on the Z-machine, which interpreters like Frotz emulate. The best-stocked archive of freeware games for use on Z-machine interpreters is the Interactive Fiction Archive at http://www.ifarchive.org. There are several mirrors of the archive all over the world listed there. Here are direct URLs to the zcode directories: http://www.ifarchive.org/indexes/if-archiveXgamesXzcode.html ftp://ftp.ifarchive.org/if-archive/games/zcode Here is the scheme I use for organizing my Zcode games: /usr/local/share/zcode This contains games written after the demise of Infocom. Most are freeware. /home/dave/.zcode I sometimes put games here too. /usr/local/share/zcode/infocom This is where I keep my collection of genuine Infocom games. /usr/local/share/zcode/infocom/sound Soundfiles from "Lurking Horror" and "Sherlock" go here. /usr/local/share/zcode/infocom/graphics Graphics files from Zork 0, Arthur, Shogun, and Journey go here. I add this command to my .profile file: export ZCODE_PATH="/usr/local/share/zcode: \ /usr/local/share/zcode/infocom:$HOME/.zcode Now, when I want to play Zork I, I will type "frotz zork1.dat" at the command prompt. Then I will then be told I am standing in an open field west of a white house which has a boarded front door. You can also just give a path to the game file. When you save your game, all save files are put in the current directory unless you specify a full path. Please name your saves intelligently. You MUST put sound and graphics in directories named "sound" and "graphics" in the same directory as the gamefile. Yes, this is a bit confusing. That's why Blorb will be so wonderful when Unix Frotz supports it. You'll probably want to make use of Frotz's new config file functionality. The options in the config file mirror the command line options and free you from having to remember to add something like "-Z0" to get rid of complaints about buggy zcode or if you want to always play with white text on black at a Linux console (instead of white on blue). Sample config files are included here as "frotz.conf-big" (which lists all possible options) and "frotz.conf-small" (a shortened one listing the more commonly-used options). The Makefile defines where Frotz will look for the frotz.conf file. By default, this is /usr/local/etc and can be changed at compile time if you like. This file will be read if Frotz notices you don't have a config file of your own in "$HOME/.frotzrc". =========================== Platform-specific issues || =========================== Linux: No apparent problems. This is the only platform on which sound is known to work properly. [Net|Open|Free]BSD: If you have NetBSD 1.6.x or later, you can use the supplied BSD curses. Otherwise you must have ncurses. Digital UNIX: No apparent problems. Tru64 Unix: Rebadged Digital Unix. Irix: The vendor-supplied curses library is broken as well as all versions of ncurses supplied on SGI's freeware CDs and in SGI's freeware archive. You MUST compile and install at least ncurses 5.0 from source. Versions of ncurses older than 5.0 are also broken on Irix. MacOS X: You must use "cc" instead of "gcc" even though it's really GCC. This is a quirk of the Mac OS X Developer Tools. MAN_PREFIX in the Makefile should be set to "/usr/local/share". Installing ncurses on MacOS X used to be troublesome because of some nonstandard places for things, but it seems that thse problems have been solved with ncurses 5.2. A precompiled version of ncurses for MacOS X is available at http://gnu-darwin.sourceforge.net/. Solaris: Some versions of curses on Solaris have trouble with color support. At least the one in Solaris 2.6 works okay. If compiled with the -02 option on an UltraSPARC using gcc 2.8.1, you may get lots of weird segfaults. The problem seems UltraSPARC related and it's not clear if this problem crosses flavor boundaries (ie, if UltraLinux or NetBSD on UltraSparc have this problem too). Because version 2.8.x of gcc had lots of strange problems, gcc might be to blame. I don't have sufficient access to test this theory, so if you're able to enlighten me on this, please do so. SunOS: Uncomment the "MEMMOVE_DEF..." line in the Makefile before compiling. Since I don't have access to a SunOS machine or an install CD (hint hint), Frotz on SunOS is untested. Other flavors of Unix: Getting Unix Frotz to compile and run seems to focus mostly on making sure make(1) can find the proper curses library. It's probably a good idea to install ncurses anyway. Now go on and have fun! frotz-2.44/BUGS0000644000175000017500000000673712527042452010233 00000000000000============= Color mode || ============= It seems that it's up to the curses library to decide if a program may or may not use color. For example, ncurses on NetBSD refuses to allow Frotz to run in color mode, even when color mode is forced. Frotz and the program it's running think color is being emitted, but ncurses won't allow it. On the other hand, NetBSD's standard curses library (1.6 and later) will let colors be seen. ======================================= Ctrl-Space causing Bogus ZC_TIME_OUT || ======================================= Under ncurses, getch() will return OK (defined to 0 ) when Ctrl-@ or Ctrl-Space is pressed. 0 is also the ZSCII character code for ZC_TIME_OUT. This causes a fatal error "Call to non-routine", after which Frotz aborts. This doesn't happen with all games nor is the crashing consistent. Sometimes repeated tests on a single game will yield some crashes and some non-crashes. When linked with ncurses, we must make sure that unix_read_char() does not return a bogus ZC_TIME_OUT. This returning of 0 appears to be abnormal behavior peculiar to ncurses, so an #ifdef is used to enable the fix only if ncurses is being used. Alembic's fix didn't have this #ifdef and so it broke NetBSD's newly-updated curses library. ======================= Bug-testing Programs || ======================= Unix Frotz now comes with crashme, TerpEtude, gntests, strictz, and Unicode Test. These are intended to excercise the interpreter and verify that it is functioning according to spec. You will find these programs in the src/test/ directory. ================== Screen Resizing || ================== There are some significant problems involved in getting screen resizes (as in resizing an xterm) to work correctly at least with Frotz's implementation of the Z-Machine and probably the Z-Machine standard itself. For this reason, I have not implemented screen resizing for Frotz. I know that some zcode interpreters are able to deal with resizes somewhat gracefully, but I haven't seen one yet that will handle some weirder situations. Infocom's "Border Zone" and "Beyond Zork are especially troublesome for doing resizes. Nitfol seems to have the best handling of screen resizes. Since it uses the Glk library, I'm inclined to think the Glk library is at least partially responsible. If you have any other ideas on how to cleanly resize screens, I'd like to hear them. The Z-Machine seems to assume that when the screen dimensions are set, they will never change over the course of the game, even across saves and restores. For some reason, saves include screen dimensions and I've yet to discover how to override the Z-Machine from setting its dimensions to the saved ones instead of how the current screen really is. When the Z-Machine was first defined by Infocom, this made sense since terminal screens rarely, if ever, changed their dimensions and few people were assumed to trade saves across platforms. ===================== Arrow Key Handling || ===================== Not really a bug, but an idiosyncracy. In "Journey", sometimes you can't use the right-arrow key to move from the "party commands" menu to the "individual commands" menu. IMHO, this is a bit sloppy, but it isn't Frotz's fault. Instead, use the spacebar to jump from menu to menu. I mistakenly attributed this to an unknown bug. That'll teach me to do bugtesting when I'm about to nod off to sleep. =================== Other Known Bugs || =================== Sound flag is not being set properly. frotz-2.44/DOSBUILD.txt0000644000175000017500000000455312527045477011522 00000000000000=========================================================================== --------------------------------------------------------------------------- | READ THIS FILE ALL THE WAY THROUGH BEFORE ATTEMPTING TO INSTALL FROTZ | --------------------------------------------------------------------------- =========================================================================== Frotz was originally written specifically for MS/PC DOS. When it began to be ported to other machines and operating systems, the DOS port lagged behind. This has been fixed to the extent that DOS is capable of the new enhancements. For instance, DOS Frotz is now capable of using Blorb files to get audio and graphical data. The old Infocom-style mechanism of doing this has been removed. Sound is supported through Soundblaster compatible sound cards. Graphic levels from Hercules to MCGA are supported. To compile DOS Frotz, you need the following: * Borland Turbo C++ 3.00. This is the latest version I'm aware of capable of building 16-bit DOS applications. * PC-DOS, MS-DOS, or DR-DOS version 5.0 or higher. FreeDOS works well too. ============== Compilation || ============== Turbo C++ is typically installed to C:\tc. Make sure your path contains C:\tc\bin. When this is done, type "make". Did Borland's make program run? Now type "tcc". If those two commands work, then you should be ready to compile. Somehow get the source tree onto your DOS machine or into a DOS emulation environment. If you're using emulation, this should be as simple as copying the tree you're in now into the proper directory. For real hardware, a floppy disk will do. To compile, go into the Frotz directory and type "build". That will invoke "build.bat", a batch file that simply executes "make -f makefile.tc". The reason for this is a bit of syntactic sugar to deal with the fact that it is impractical to have one Makefile build both Unix and DOS versions. After about a minute, you should have FROTZ.EXE. This file is all you need to run Frotz. Put it somewhere in your path and go to wherever you keep your Infocom games. Type "frotz zork1.dat" or whatever and there you go. ======================== Hardware Requirements || ======================== The exact hardware requirements of DOS Frotz are yet to be determined. Please send me reports of what Zcode works and what doesn't. frotz-2.44/README0000644000175000017500000000514712527051671010425 00000000000000FROTZ V2.44 - An interpreter for all Infocom and other Z-machine games. Complies with standard 1.0 of Graham Nelson's specification. Originally written by Stefan Jokisch in 1995-1997. Ported to Unix by Galen Hazelwood. Reference code and Unix port currently maintained by David Griffith. - Compiles and runs on most common flavors of Unix, both open source and not. - Plays all Z-code games including V6. - Old-style sound support through OSS driver. - Config files. - Configurable error checking. - Quetzal save file format. - Distributed under the GNU Public License. For information on what Interactive Fiction is and how to play it, see the file "HOW_TO_PLAY". For installation information, see the file "INSTALL". For update history, see the file "Changelog". For information on known bugs in Frotz, see the file "BUGS". For bug reports, check the Unix Frotz website to see if there's a new release. If not, send your bug report to me at dave@661.org. For information on porting Frotz to new platforms, see the file "PORTING". For those who are involved in creating and distributing binary packages containing Frotz and including Frotz in BSD-style ports/pkgsrc trees, see the file "PACKAGING". The latest information on and source code of Unix Frotz is available at https://github.com/DavidGriffith/frotz The latest release of Unix Frotz is available from the Interactive Fiction Archive at: http://www.ifarchive.org/if-archive/infocom/interpreters/frotz/ frotz-.tar.gz and ftp://ftp.ifarchive.org/if-archive/infocom/interpreters/frotz/ frotz-.tar.gz The Interactive Fiction Archive has several mirrors which may be better choices depending on where you live. Here is a partial list. in the USA: http://mirror.ifarchive.org/ http://ifarchive.jmac.org/ http://ifmirror.russotto.net/ http://ifarchive.flavorplex.com/ http://ifarchive.smallwhitehouse.org/ http://ifarchive.wurb.com/ http://ifarchive.plover.net/ http://www.ifarchive.info/ http://ifarchive.ifreviews.org/ http://mirrors.ibiblio.org/interactive-fiction/ in Ireland: http://ifarchive.heanet.ie/ in Austria: http://ifarchive.giga.or.at/ FTP based mirrors are also available: in the USA: ftp://ifarchive.ifreviews.org/if-archive/ in Austria: ftp://ftp.giga.or.at/pub/ifarchive/ in Finland: ftp://ftp.funet.fi/pub/mirrors/ftp.ifarchive.org/if-archive/ And via Gopher: in the USA: gopher://gopher.feedle.net/1/if-archive/ frotz-2.44/doc/0000755000175000017500000000000012527051523010357 500000000000000frotz-2.44/doc/frotz.conf-big0000644000175000017500000000440112527045477013063 00000000000000# This is a sample Unix Frotz configuration file which contains examples # of all supported options. # If this is actually $HOME/.frotzrc instead of the systemwide # configuration file, $HOME/.frotzrc will override. These options are # explained in more detail in the Unix Frotz README file and the manpage. ############################################################ # These options control some basic characteristics of Frotz. # Use color text (default "yes" if supported) color yes # Set foreground color (default "white" if in color mode) foreground white # Set background color (default "blue" if in color mode) background blue # Set error-reporting mode (default "once") errormode once # Set path to search for game files (no default) zcode_path /usr/local/games/zcode # Use plain ASCII only (default "no") ascii no # Turn sound on or off (default "on") sound on ############################################ # These are some less-commonly used options. # Set random number seed (default comes from the Unix epoch) randseed 1234 # Set number of undo slots (default 500) undo_slots 500 # Set Tandy bit (default "no") tandy no # Alter piracy opcode (default "no") piracy no # Expand abbreviations (default "no") expand_abb no ######################################################################### # These options are useful for weird terminals or if you want to force a # certain mode. # Force color mode (default "no") # Useful for use with supposedly current flavors of Unix that still don't # understand what the "xterm-color" terminal type is. force_color no # Set screen height (default is detected height) screen_height 24 # Set screen width (default is detected screen width) screen_width 80 # Set script width (default is screen width) script_width 80 # Set context lines (default "0") context_lines 0 # Set left margin (default "0") left_margin 0 # Set right margin (default "0") right_margin 0 ############################################################# # These options are mainly useful for debugging and cheating. # Watch attribute setting (default "no") attrib_set no # Watch attribute testing (default "no") attrib_test no # Watch object movement (default "no") obj_move no # Watch object locating (default "no") obj_loc no frotz-2.44/doc/frotz.conf-small0000644000175000017500000000071712527042452013426 00000000000000# This is a basic sample Unix Frotz configuration file. It contains some # a few options the average user is likely to want to change. # If this is actually $HOME/.frotzrc instead of the systemwide # configuration file, $HOME/.frotzrc will override. These options are # explained in more detail in the Unix Frotz README file and the manpage. color yes foreground white backgrounnd black zcode_path /usr/local/games/zcode errormode once ascii no sound on frotz-2.44/doc/dfrotz.60000644000175000017500000002122312527051702011675 00000000000000.\" -*- nroff -*- .TH FROTZ 6 2.44 .SH NAME dfrotz \- interpreter for Infocom and other Z-Machine games (dumb interface) .SH SYNOPSIS .B dfrotz .RI [ options "] " file .SH DESCRIPTION .B Frotz is a Z-Machine interpreter. The Z-machine is a virtual machine designed by Infocom to run all of their text adventures. It went through multiple revisions during the lifetime of the company, and two further revisions (V7 and V8) were created by Graham Nelson after the company's demise. The specification is now quite well documented; this version of Frotz supports version 1.0. .P This version of Frotz fully supports all these versions of the Z-Machine except for version 6. Version 6 is semi-supported by displaying the outlines of V6 graphics with the picture number in the bottom-right corner. .P This manpage is for Frotz with the dumb interface. Neither sound-effects nor colors are supported. .SH OPTIONS .TP .B \-a Watch attribute setting. Setting and clearing of attributes on objects will be noted in debugging messages. .TP .B \-A Watch attribute testing. Every time the z-machine tests an attribute value, the test and the result will be reported. .TP .B \-h N Screen height. Every N lines, a MORE prompt will be printed. Use of the .B \-m option renders this option moot. .TP .B \-i Ignore fatal errors. If a Z-Machine interpreter encounters a zcode error such as division-by-zero or addressing an illegal object, the proper response is to abort execution. This is done because the zcode program doesn't have a clear idea of what is going on. There are some games out there that cause fatal errors because the authors were careless and used an interpreter that didn't properly check for errors. This option is intended to get around such bugs, but be warned that Strange Things may happen if fatal errors are not caught. .TP .B \-m Turn off MORE prompts. This can be desirable when using a printing terminal. .TP .B \-o Watch object movement. This option enables debugging messages from the interpreter which describe the moving of objects in the object tree. .TP .B \-O Watch object location. These debugging messages detail the locations of objects in the object tree. .TP .B \-p Plain ASCII output only. This inhibits the output of accented letters and other characters from the Latin-1 character set, replacing them with reasonable alternatives. This may be necessary on devices lacking these characters. .TP .B \-P Alter the piracy opcode. The piracy opcode was never used by Infocom. This switch is really only useful for those who like to toy around with Z-code. .TP .B \-R \exxx Set runtime options. This option may be used repeatedly. .TP .B \-s N Set the random number seed value. The given seed value is used as the initial seed value on every restart. This is helpful for testing games like .B Curses which make random decisions before the first input (such that the hot key Alt\-S does not really help). .TP .B \-S N Set the transcript width. By default your transscript files are formatted to a width of 80 columns per line, regardless of the current screen width. This switch allows you to change this setting. In particular, use \-S 0 to deactivate automatic line splitting in transscript files. .TP .B \-t Sets the z-machine's .I Tandy bit, which may affect the behavior of certain Infocom games. For example, Zork I pretends not to have sequels, and Witness has its language toned down. .TP .B \-u N Sets the number of slots available for Frotz's multiple undo hotkey (see below). This defaults to twenty, which should be sufficient for most purposes. Setting too high a number here may be dangerous on machines with limited memory. .TP .B \-w N Manually sets the screen width. This should not be necessary except in special circumstances. .TP .B \-x Expand the abbreviations "g", "x", and "z" to "again", "examine", and "wait". This switch is for use iwth old Infocom games that lack these common abbreviations which were introduced in later games. Use it with caution: A few games might use "g", "x" or "z" for different purposes. .TP .B \-Z N Error checking mode. .br 0 = don't report errors. .br 1 = report first instance of an error. .br 2 = report all errors. .br 3 = exit after any error. .br Default is 1 (report first instance of an error). .SH CONFIGURATION Unline it's curses-using sibling, .B dfrotz does not use configuration files. All configuration is done on the command line or while .B dfrotz is running. .P .SS General Commands .TP .B \ehelp Show help message. .TP .B \eset Show the current values of runtime settings. .TP .B \es Show the current contents of the whole screen. .TP .B \ed Discard the part of the input before the cursor. .TP .B \ewN Advance clock N/10 seconds, possibly causing the current and subsequent inputs to timeout. .TP .B \ew Advance clock by the amount of real time since this input started (times the current speed factor). .TP .B \et Advance clock just enough to timeout the current input .SS Reverse-Video Display Method Settings .TP .B \ern none .TP .B \erc CAPS .TP .B \erd doublestrike .TP .B \eru underline .SS Output Compression Settings .TP .B \ecn none: show whole screen before every input. .TP .B \ecm max: show only lines that have new nonblank characters. .TP .B \ecs spans: like max, but emit a blank line between each span of screen lines shown. .TP .B \echN Hide top N lines (orthogonal to above modes). .SS Misc Settings .TP .B \esfX Set speed factor to X. (0 = never timeout automatically). .TP .B \emp Toggle use of MORE prompts .TP .B \eln Toggle display of line numbers. .TP .B \elt Toggle display of the line type identification chars. .TP .B \evb Toggle visual bell. .TP .B \epb Toggle display of picture outline boxes. .TP (Toggle commands can be followed by a 1 or 0 to set value ON or OFF.) .SS Character Escapes .TP .B \e\e backslash .TP .B \e# backspace .TP .B \e[ escape .TP .B \e_ return .TP .B \e< cursor-left .TP .B \e> cursor-right .TP .B \e^ cursor-up .TP .B \e. cursor-down .TP .B \e1..\e0 f1..f10 .TP .B \eD..\eX Standard Frotz hotkeys. .TP use \eH (help) to see the list of hotkeys. .SS Line Type Identification Characters .SS Input lines (untimed) .TP .B > A regular line-oriented input .TP .B ) A single-character input .TP .B } A line input with some input before the cursor. Use \ed to discard it. .SS Input lines (timed) .TP .B T A regular line-oriented input .TP .B t A single-character input .TP .B D A line input with some input before the cursor. Use \ed to discard it. .SS Output lines .TP .B ] Output line that contains the cursor. .TP .B . A blank line emitted as part of span compression. .TP .B \~ (blank) Any other output line. .SH ENVIRONMENT Unline it's curses-using sibling, .B dfrotz does not search any path for game files. .SH FURTHER INFORMATION .PP A .BR git(1) repository of all versions of Unix Frotz back to 2.32 is available for public perusal here: .br https://github.com/DavidGriffith/frotz/. .PP The bleeding edge of Frotz development may be followed there. A wiki summarising Frotz is also there at this URL: .br https://github.com/DavidGriffith/frotz/wiki/ .PP Source tarballs are available at the IF Archive or any of its many mirrors: .br http://www.ifarchive.org/ .PP Most distributions of Linux and BSD include Frotz in their package repositories. .SH CAVEATS .PP The Z Machine itself has trouble with the concept of resizing a terminal. It assumes that once the screen height and width are set, they will never change; even across saves. This made sense when 24x80 terminals were the norm and graphical user interfaces were mostly unknown. I'm fairly sure there's a way around this problem, but for now, don't resize an xterm in which frotz is running. Also, you should try to make sure the terminal on which you restore a saved game has the same dimensions as the one on which you saved the game. .PP This manpage is not intended to tell users HOW to play interactive fiction. Refer to the file HOW_TO_PLAY included in the Unix Frotz documentation or visit one of the following sites: .br http://www.microheaven.com/ifguide/ .br http://www.brasslantern.org/beginners/ .br http://www.musicwords.net/if/how_to_play.htm .PP If you prefer a PDF file of how to play, here is one: .br http://inform-fiction.org/I7Downloads/Examples/dm/IntroductionToIF.pdf .SH BUGS This program has no bugs. no bugs. no bugs. no *WHAP* thank you. If you find one, please report it to the Github site referenced above in .B FURTHER INFORMATION. .SH AUTHORS .B Frotz was written by Stefan Jokisch in 1995-7. .br The Unix port was done by Galen Hazelwood. .br The Unix port is currently maintained by David Griffith . .SH "SEE ALSO" .BR frotz (6) .BR nitfol (6) .BR rezrov (6) .BR jzip (6) .BR xzip (6) .BR inform (1) frotz-2.44/doc/frotz.60000644000175000017500000003214512527051714011541 00000000000000.\" -*- nroff -*- .TH FROTZ 6 2.44 .SH NAME frotz \- interpreter for Infocom and other Z-Machine games .SH SYNOPSIS .B frotz .RI [ options "] " file .SH DESCRIPTION .B Frotz is a Z-Machine interpreter. The Z-machine is a virtual machine designed by Infocom to run all of their text adventures. It went through multiple revisions during the lifetime of the company, and two further revisions (V7 and V8) were created by Graham Nelson after the company's demise. The specification is now quite well documented; this version of Frotz supports version 1.0. .P This version of Frotz fully supports all these versions of the Z-Machine except for version 6. Version 6 is semi-supported by displaying the outlines of V6 graphics with the picture number in the bottom-right corner. .SH OPTIONS .TP .B \-a Watch attribute setting. Setting and clearing of attributes on objects will be noted in debugging messages. .TP .B \-A Watch attribute testing. Every time the z-machine tests an attribute value, the test and the result will be reported. .TP .B \-b Sets the default background color. corresponds to one of the Z-machine colors, which are as follows: .br .I black red green yellow blue magenta cyan white .br If color support is disabled or not available on your terminal, this option does nothing. .TP .B \-c N Sets the number of context lines used. By default, after a ``[MORE]'' prompt, and assuming there is enough output pending, Frotz will allow all the currently visible lines to scroll off the screen before prompting again. This switch specifies how many lines of text Frotz will hold over and display at the top of the next screen. .TP .B \-d Disable color. .TP .B \-e Enable sound. If you've disabled sound in a config file and want to hear sound effects, use this. .TP .B \-f Sets the default foreground color. corresponds to one of the Z-machine colors, which are as follows .br .I black red green yellow blue magenta cyan white .br If color support is disabled or is not available on your terminal, this option does nothing. .TP .B \-F Force color mode. If you've disabled color in a config file and want to Frotz to display colors, use this. .TP .B \-h N Manually sets the screen height. Though most curses libraries are intelligent enough to determine the current width from the terminal, it may sometimes be necessary to use this option to override the default. .TP .B \-i Ignore fatal errors. If a Z-Machine interpreter encounters a zcode error such as division-by-zero or addressing an illegal object, the proper response is to abort execution. This is done because the zcode program doesn't have a clear idea of what is going on. There are some games out there that cause fatal errors because the authors were careless and used an interpreter that didn't properly check for errors. This option is intended to get around such bugs, but be warned that Strange Things may happen if fatal errors are not caught. .TP .B \-l N Sets the left margin, for those who might have specific formatting needs. .TP .B \-o Watch object movement. This option enables debugging messages from the interpreter which describe the moving of objects in the object tree. .TP .B \-O Watch object location. These debugging messages detail the locations of objects in the object tree. .TP .B \-p Plain ASCII output only. This inhibits the output of accented letters and other characters from the Latin-1 character set, replacing them with reasonable alternatives. This may be necessary on devices lacking these characters. .TP .B \-P Alter the piracy opcode. The piracy opcode was never used by Infocom. This switch is really only useful for those who like to toy around with Z-code. .TP .B \-q Quiet. Turns off sound effects. Useful when running Frotz on a remote machine and you don't want to bother whoever's near the console with weird noises. .TP .B \-r N Sets the right margin. .TP .B \-s N Set the random number seed value. The given seed value is used as the initial seed value on every restart. This is helpful for testing games like .B Curses which make random decisions before the first input (such that the hot key Alt\-S does not really help). .TP .B \-S N Set the transcript width. By default your transscript files are formatted to a width of 80 columns per line, regardless of the current screen width. This switch allows you to change this setting. In particular, use \-S 0 to deactivate automatic line splitting in transscript files. .TP .B \-t Sets the z-machine's .I Tandy bit, which may affect the behavior of certain Infocom games. For example, Zork I pretends not to have sequels, and Witness has its language toned down. .TP .B \-u N Sets the number of slots available for Frotz's multiple undo hotkey (see below). This defaults to twenty, which should be sufficient for most purposes. Setting too high a number here may be dangerous on machines with limited memory. .TP .B \-w N Manually sets the screen width. Again, this should not be necessary except in special circumstances. .TP .B \-x Expand the abbreviations "g", "x", and "z" to "again", "examine", and "wait". This switch is for use with old Infocom games that lack these common abbreviations which were introduced in later games. Use it with caution: A few games might use "g", "x" or "z" for different purposes. .TP .B \-Z N Error checking mode. .br 0 = don't report errors. .br 1 = report first instance of an error. .br 2 = report all errors. .br 3 = exit after any error. .br Default is 1 (report first instance of an error). .SH CONFIGURATION FILES On startup, .B frotz will first check the system's frotz.conf then $HOME/.frotzrc for configuration information. The configuration file uses a simple syntax of .br .PP Color names may be any of the following: .br black\ |\ red\ |\ green\ |\ blue\ |\ magenta\ |\ cyan\ |\ white .PP .BR ascii \ \ on\ |\ off .br Use plain ASCII only. Default is "off". .PP .BR background \ \ .br Set background color. Default is terminal's default background color. .PP .BR color \ \ yes\ |\ no .br Use color text. Default is "yes" if supported. .PP .BR errormode \ \ never\ |\ once\ |\ always\ |\ fatal .br Set error reporting mode. .br .I never Don't report any errors except for fatal ones. .br .I once Report only the first instance of an error. .br .I always Report every instance of an error. .br .I fatal Abort on any error, even non-fatal ones. .br Default is "once". .PP .BR expand_abb \ \ on\ |\ off .br Expand abbreviations. Default is off. Expand the abbreviations "g", "x", and "z" to "again", "examine", and "wait". This switch is for use with old Infocom games that lack these common abbreviations which were introduced in later games. Use it with caution. A few games might use the "g", "x", or "z" for different purposes. .PP .BR foreground \ \ .br Set foreground color. Default is terminal's default forground color. .PP .BR ignore_fatal \ \ on\ |\ off .br Ignore fatal errors. If a Z-Machine interpreter encounters a zcode error such as division-by-zero or addressing an illegal object, the proper response is to abort execution. This is done because the zcode program doesn't have a clear idea of what is going on. There are some games out there that cause fatal errors because the authors were careless and used an interpreter that didn't properly check for errors. This option is intended to get around such bugs, but be warned that Strange Things may happen if fatal errors are not caught. .br Default is "off" .PP .BR piracy \ \ on\ |\ off .br Alter the piracy opcode. Default is off. The piracy opcode was never used by Infocom. This option is only useful for those who like to toy around with Z-code. .PP .BR randseed \ \ .br Set random number seed. Default comes from the Unix epoch. .PP .BR sound \ \ on\ |\ off .br Turn sound effects on or off. Default is "on". .PP .BR tandy \ \ on\ |\ off .br Set the machine's .I Tandy bit. This may affect the behavior of certain Infocom games. For example, Zork I pretends not to have sequels, and Witness has its language toned down. Default is "off". .PP .BR undo_slots \ \ .br Set number of undo slots. Default is 500. .PP .BR zcode_path \ \ /path/to/zcode/files:/another/path .br Set path to search for zcode game files. This is just like the $PATH environmental variable except that you can't put environmental variables in the path or use other shortcuts. For example, "$HOME/games/zcode" is illegal because the shell can't interpret that $HOME variable. .P The following options are really only useful for weird terminals, weird curses libraries or if you want to force a certain look (like play in 40-column mode). .PP .BR context_lines \ \ .br Set the number of context lines used. By default, after a ``[MORE]'' prompt, and assuming there is enough output pending, frotz will allow all the currently visible lines to scroll off the screen before prompting again. This switch specifies how many lines of text frotz will hold over and display at the top of the next screen. Default is "0". .PP .BR left_margin \ \ .br Set the left margin. This is for those who might have special formatting needs. .PP .BR right_margin \ \ .br Set the right margin. This is for those who might have special formatting needs. .PP .BR screen_height \ \ .br Manually set screen height. Most curses libraries are intelligent enough to determine the current width of the terminal. You may need to use this option to override the default. .PP .BR screen_width \ \ .br Manually set screen width. Again, this should not be necessary except in special circumstances. .PP .BR script_width \ \ .br Set the transcript width. Default is 80 columns per line, regardless of the current screen width. This switch allows you to change this setting. You may set this to "0" to deactivate automatic line-splitting in transcript files. .P The following options are mainly useful for debugging or cheating. .PP .BR attrib_set \ \ on\ |\ off .br Watch attribute setting. Setting and clearing of attributes on objects will be noted in debugging messages. Default is "off" .PP .BR attrib_test \ \ on\ |\ off .br Watch attribute testing. Every time the z-machine tests an attribute value, the test and the result will be reported. Default is "off". .PP .BR obj_loc \ \ on\ |\ off .br Watch object location. These debugging messages detail the locations of objects in the object tree. Default is "off". .PP .BR obj_move \ \ on\ |\ off .br Watch object movement. This option enables debugging messages from the interpreter which describe the movement of objects in the object tree. Default is "off". .SH ENVIRONMENT If the ZCODE_PATH environmental variable is defined, frotz will search that path for game files. If that doesn't exist, INFOCOM_PATH will be searched. .SH FURTHER INFORMATION .PP A .BR git(1) repository of all versions of Unix Frotz back to 2.32 is available for public perusal here: .br https://github.com/DavidGriffith/frotz/. .PP The bleeding edge of Frotz development may be followed there. A wiki summarising Frotz is also there at this URL: .br https://github.com/DavidGriffith/frotz/wiki/ .PP Source tarballs are available at the IF Archive or any of its many mirrors: .br http://www.ifarchive.org/ .PP Most distributions of Linux and BSD include Frotz in their package repositories. .SH CAVEATS .PP The Z Machine itself has trouble with the concept of resizing a terminal. It assumes that once the screen height and width are set, they will never change; even across saves. This made sense when 24x80 terminals were the norm and graphical user interfaces were mostly unknown. I'm fairly sure there's a way around this problem, but for now, don't resize an xterm in which frotz is running. Also, you should try to make sure the terminal on which you restore a saved game has the same dimensions as the one on which you saved the game. .PP You can use a path like "/usr/local/games/zcode:$HOME/zcode" with $ZCODE_PATH or $INFOCOM_PATH because the shell will digest that $HOME variable for you before setting $ZCODE_PATH. While processing frotz.conf and $HOME/.frotzrc, a shell is not used. Therefore you cannot use environmental variables in the "zcodepath" option within the config files. .PP This manpage is not intended to tell users HOW to play interactive fiction. Refer to the file HOW_TO_PLAY included in the Unix Frotz documentation or visit one of the following sites: .br http://www.microheaven.com/ifguide/ .br http://www.brasslantern.org/beginners/ .br http://www.musicwords.net/if/how_to_play.htm .PP If you prefer a PDF file of how to play, here is one: .br http://inform-fiction.org/I7Downloads/Examples/dm/IntroductionToIF.pdf .SH BUGS This program has no bugs. no bugs. no bugs. no *WHAP* thank you. If you find one, please report it to the Github site referenced above in .B FURTHER INFORMATION. .SH AUTHORS .B Frotz was written by Stefan Jokisch for MSDOS in 1995-7. .br The Unix port was done by Galen Hazelwood. .br The Unix port is currently maintained by David Griffith . .SH "SEE ALSO" .BR dfrotz (6) .BR nitfol (6) .BR rezrov (6) .BR jzip (6) .BR xzip (6) .BR inform (1) frotz-2.44/PORTING0000644000175000017500000001133412527045477010614 00000000000000Frotz is a very portable program and as such has been ported to quite a lot of different platforms. These are actively-maintained ports and their webpages: ------------------------------------------------------= Windows Frotz Frotz for machines running Microsoft Windows. http://www.davidkinder.co.uk/frotz.html DOS Good ol' DOS https://github.com/DavidGriffith/frotz iOS Frotz for the Apple iPhone, iPad, and iPod Touch. https://itunes.apple.com/us/app/frotz/id287653015 (app store) https://code.google.com/p/iphonefrotz/ (source code) Gameboy Advance Frotz for the Nintendo Gameboy Advance http://gbaemu.dcemu.co.uk/gbafrotz.shtml Grue An Infocom playing bot for irc http://grue.sourceforge.net/ CliFrotz Frotz for PalmOS machines http://zodiacstuff.sourceforge.net/clifrotz.html To my knowledge, nobody maintains these ports anymore and none have pages: -------------------------------------------------------------------------- AmigaFrotz: For the Commodore Amiga EbmFrotz: For the Franklin's eBookman FrotzCE: For Windows CE Kwest: KDE graphical interface Pilot Frotz: For Palm Pilot machines Frotz might work well with these platforms: ------------------------------------------- Apple IIgs. I'm fairly sure that with its greater memory capacity than the ealier members of the Apple II family, at least text-mode Frotz should be doable. Perhaps even graphics and sound could be done. RISC/OS I don't see why not. Texas Instruments TI-92 and TI-92+ graphing calculators: With a Motorola 680000 of some sort and loads of nifty development software, it should be a worthwhile effort. Look at http://www.ticalc.org or all sorts of interesting things that have been done with this calculator. CP/M: Usually we'll have a 64 kilobyte limit to memory. Fortunately there is a solution in the form of an interpreter written in Z80 assembly called ZXZVM available at the IF Archive. It seems specific to the Spectrum +3, PCW16, and PCW10 CP/M machines. I'd be most pleased if someone with one of those new IMSAI Series 2 machines is able to get ZXZVM working on that machine. However, at http://www.imsai.net, the new machine is described as having 1 meg of static system memory. Given all this, Frotz might be doable on some more beefy CP/M machines. Frotz probably won't work on these platforms: --------------------------------------------- Apple II: The IIe and IIc with expanded memory (at least 143k) might be enough for running up to V5, but I'm not sure if Frotz will appreciate working in such a small space. Yes, I know that several Solid Gold editions were released for Apple II. Commodore 64: Memory is limited to a bit less than 64 kilobytes. In the heyday of Infocom, the C64 could barely support V4 games. Commodore 128: Probably same troubles as with Apple II. Atari 8-bit: Probably troubles as with the Commodore 64. Digital cameras: Someone ported the venerable arcade machine emulator MAME to a Kodak digital camera some time ago. There should be enough space and processing power to run Frotz, but as with wireless phones, IF is impractical on such a platform. I asked Brian Moriarty, author of several works of Interactive Fiction for Infocom about the feasability of porting Frotz to machines using the MOS 6502 (Apple II, Atari 8-bit, Commodore 64 and 128). Here is his response to my emailed question: Date: Thu, 8 Aug 2002 18:54:51 -0400 From: Brian Moriarty To: Dave Subject: RE: porting Frotz to 6502 machines The original 6502 ZIPs were hand-written in assembler, and the resulting binaries were usually quite small (around 8k). Of course, a C compiler wouldn't be quite as efficient. The larger problem is that many of the later Z features (beyond version 2) simply won't work on an Atari, C64 or Apple II, either due to RAM, display or disk drive restrictions. When moving into versions 3 and beyond, we decided to leave that class of machine behind. -----Original Message----- From: Dave Sent: Thursday, August 08, 2002 6:43 PM To: Brian Moriarty Subject: porting Frotz to 6502 machines Brian Moriarty, I was wondering if I could pick your brain a bit on Z-machine portability. One of the ongoing goals for the Frotz project is extreme portability, including to all sorts of old architectures. I've been pondering porting Frotz to 6502 machines such as the C64, Apple II, and Atari 8-bit computers. Doing some investigation with the CC65 6502 C compiler brought me to the conclusion that Frotz would produce a binary too large to fit in the memory of these machines. Apple IIgs might work. Given your work on making Z-machine interpreters for these machines (albeit in assembly, I assume) would this be an accurate assumption? Also, given what I've found out about the new upcoming IMSAI Series 2 machine, this might be capable of handling Frotz. frotz-2.44/ChangeLog0000644000175000017500000001722012527045477011321 00000000000000Summary of changes between Frotz 2.43 and Frotz 2.44: ===================================================== Frotz 2.44 was released on Sunday May 17, 2015. NEW FEATURES - Blorb support implemented. No support for audio in Blorb files yet. - Added an -m option for Dumb Frotz to suppress the [MORE] prompt. - Makefile and code for compiling for 16-bit DOS. BUG FIXES - Fixed a problem with saving and restoring in certain games. - Added some fflush() calls to make Dumb Frotz more suitable for bot use. - Removed old-style save support. It was getting hard to manage. Only the Quetzal save file format is supported now. - Fixed assorted minor 64-bitness problems. Summary of changes between Unix Frotz 2.42 and Unix Frotz 2.43: =============================================================== Unix Frotz 2.43 was released on Monday October 28, 2002. NEW FEATURES - Dumb interface has been added as a compile target. See the DUMB file for commentary. - Experimental voice input and output. This code is not yet available to the public. See the SPEECH file for more info. - Added David Kinder's Unicode Test 1.0 to the Z-machine Test Suite - Added Inform Randomization Test 1.0 to the Z-machine Test Suite. ENHANCEMENTS - Confirmed to work with NetBSD 1.6.x curses library. Unlike with ncurses, color mode will work when $TERM is "xterm". See the BUGS file for commentary. - Consolidated and organized most global variables into a few global structs. - Added patches from David Kinder to facilitate loading a new game without exiting Frotz entirely. Files affected: buffer.c, process.c, and sound.c. - Rewrote most documentation. Fixed typos in the rest. - Reorganized source tree to make portability to different platforms easier. The bugtest/ directory is now src/test/ and is called the "Z-machine Test Suite". BUG FIXES - Finally changed all references in documentation and source of ftp.gmd.de to ftp.ifarchive.org. - Fixed a problem with command-recording which caused "[999]" to be appended to every line in the file. - Set some defaults to z_sound_effect for those games that for some wacky reason assume them (sound.c). - Frotz now correctly sets 0x20 (CONFIG_SOUND) in the header to tell V6 games that the interpreter supports sound (frotz.h ux_init.c). - Correctly fixed the Ctrl-Space bug. The previous fix broke NetBSD's newly-updated curses library. See the BUGS file for more detail. - OSS sound is fixed, thanks to Torbjorn Anderson! Also applied some error-checking from someone whom I forget. Summary of changes between Unix Frotz 2.41 and Unix Frotz 2.42: =============================================================== Unix Frotz 2.42 was released on Monday March 11, 2002. NEW FEATURES - Makefile updated to play nicer with Mac OS X. BUG FIXES - Al Petrofsky sent a fix to prevent Frotz from aborting when Ctrl-Space is entered (in ux_input.c). Actually it was sent to Debian's bug list several months ago, but not to me. He also submitted a patch which prevents picture dimensions from being rounded down to zero (ux_pic.c). Sidebars in Arthur are now visible. - Steven Frank was mistakenly identified as the submitter of the fix to allow Unix Frotz to display properly on screens narrower than 80 columns. The true submitter was Justin Wesley. The machine used was an Agenda PDA running Linux. Summary of changes between Unix Frotz 2.40 and Unix Frotz 2.41: =============================================================== Unix Frotz 2.41 was released on Monday May 21, 2001. NEW FEATURES - Now includes several test zcode programs in the bugtest/ subdirectory. These are crashme, TerpEtude, gntests, and strictz. - Unix Frotz confirmed to compile and run on Mac OS X. See INSTALL for details. (Confirmation by Steven Frank) - Status line padding in screen.c changed to allow for screens narrower than 80 columns. (Patch by Steven Frank) BUG FIXES AND MINOR ENHANCEMENTS - In BUGS, an idiosyncracy of "Journey" by Infocom was mistakenly identified as a bug in Frotz. (Reported by someone whom I forget) - In text.c, static zchar zscii_to_latin1[] was being improperly initialized. The bug caused Latin1-style quotes <> to look >>like this<<. (Reported and fixed by Matteo De Luigi) - In the 2.40 release, I disabled the -i option thinking it wasn't needed. This was probably a mistake. Even if the -Z option is set to '0' (don't report errors), fatal errors will still be reported. Some careless programmers have released buggy games which include fatal errors. Proper interpreters should abort upon encountering them, but some don't. The -i option is intended as a kludge to deal with such games and for debugging by ignoring the requirement to abort. This option should not be used as an excuse to write bad code. Code in object_address() in object.c was modified to complain about attempts to address illegal objects. This was motivated by an illegal object access in the game "Enemies" by Andy Phillips. (Reported by Thomas Troeger and David Picton) Summary of changes between Unix Frotz 2.32R2 and Unix Frotz 2.40: ================================================================= Unix Frotz 2.40 was released on Saturday November 11, 2000. BUG FIXES - Tab-completion now works in the middle of a sentence. - Assorted fixes to make gcc stop complaining when using -Wall and -ansi flags. These fixes included adding curly braces to avoid ambiguous-looking if-then-elses, adding parens to avoid abiguous-looking ands and ors, initializing variables even though there was no need to. - Several functions in ux_text.c used functions that wanted *char. Instead they were being passed *zchar. Irix's cc complained. Casts were added to fix this. - The terminal erase character, as set with stty, now functions as backspace. On many terminals, this character is DEL. - Background colors should now work properly. - timeout() from curses is now used for times input instead of a busy loop. - ^L and ^R now redraw the screen instead of being passed as input to the Z-machine. - ISO-Latin-1 characters are now valid input unless in plain ASCII mode. This makes the meta bit unusable for hotkeys. On terminals with 8-bit character sets, alt usually sends ESC anyway. - With zstrict error-checking tuning, the -i (for ignore errors) is superfluous and therefore has been removed. - A supplementary memmove() function has been added for machines that lack it. As I don't have access to a machine like this, the code is untested. NEW FEATURES - Merged changed from Frotz 2.40 for DOS. - Beginning with 2.40, the reference version of Frotz (the DOS version) is distributed under the GNU Public License rather than the old "no commercial profit" license. - Unix Frotz 2.40, like the reference version, is now distributed under the GNU Public License. - V6 games supported by drawing the outlines of pictures. True display of the graphics are on hold until the X11 with GTK version is complete. - Classical Infocom sound effects supported through the OSS drivers. I'm only aware of "Lurking Horror" and "Sherlock: The Riddle of the Crown Jewels" as having such sound effects. See the INSTALL file for information on where the sound files should go. - System-wide and per-user config files. - Quetzal save format is now the default. Old Zip 2.0 format is still supported by command line option or config file setting. - $ZCODE_PATH and $INFOCOM_PATH environmental variables searched for game files. - Faster undo functionality (borrowed from Nitfol). - History searchs has been added. Type the beginning of a past command and hit the up or down arrow key. frotz-2.44/AUTHORS0000644000175000017500000000207012527042452010602 00000000000000Original Frotz reference code: Stefan Jokisch Original Unix port: Galen Hazelwood New Frotz reference code: Jim Dunleavy David Griffith New Unix port: David Griffith V6 semi-support: Alembic Petrofsky OSS sound support (from xfrotz 2.32.1): Daniel Schepler Thanks also to those who posted to rec.arts.int-fiction feedback on what I was doing with Unix Frotz, people who checked the betas for bugs, and sent in patches. These include, but are not limited to: Torbjorn Anderson, Timo Korvola, Martin Frost, Mihail Milushev, David Picton, Chris Sullivan, Leonard Richardson, Stephen Kitt, Paul E Coad, Paul Janzen, Brad Town, Jason C Penney, Denis Hirschfeldt, Jacob Nevins, Matteo De Luigi, Steven Frank, Thomas Troeger, David Kinder, and others that I've forgotten. Michael Edmonson (author of Rezrov) and Evin Robertson (author of Nitfol) deserve recognition for the ideas that I've borrowed from their programs. frotz-2.44/build.bat0000644000175000017500000000040112527045477011327 00000000000000@ECHO OFF REM This batch file is for compiling Frotz using Turbo C++ 3.00 for DOS. REM It's just a bit of syntactic sugar so I don't have to always REM remember that I need to specify the other Makefile for DOS REM compilation. DG make -f makefile.tc %1 frotz-2.44/DUMB0000644000175000017500000000333212527042452010206 00000000000000The dumb interface was mainly intended for those who wanted to play Interactive Fiction, but had just a C compiler and that's it. You get no screen control; everything is just printed to the terminal line by line. The terminal handles all the scrolling. While this can be an annoying way to play IF, it might be the best you can do for a particular machine, especially when the curses library is inadequate and installing ncurses isn't an option. It's also good for getting Frotz to run when you have a C compiler and know nothing else of how to do things. This way you can run Frotz on VMS, MVS, VM/CMS and all those other stodgy operating system. More on that later. Maybe you'd like to experience what it's like to play Adventure on a teletype. A much cooler use for compiling Frotz with the dumb interface is that it can be wrapped in CGI scripting, PHP, and the like to allow people to play games on webpages. I don't know how this is done, so don't ask me how. If you do know how, send me your code and documentation and I'll include it in the next release of Frotz. Games that move the write little windows of text over the main body of text are hard to read because with the dumb interface, Frotz doesn't know how to produce inverse text. Games that make use of timed-input can be played, but you must manually increment the timer (see the dfrotz manpage). Games that move the cursor around a lot are probably unplayable. These include games like Andrew Plotkin's "Freefall" and Torbjorn Andersson's "Robots". Games that make move the cursor around or write inverse banners in the middle of the text are likely to The dumb interface code was created by Alembic Petrofsky in 1997 for Frotz 2.32. frotz-2.44/PACKAGING0000644000175000017500000000165412527042452010750 00000000000000I heartily encourage people to create packages to make it easier for less computer-savvy people to use Frotz. However, I do have a few rules on this. 1. Email me if you wish to put Unix Frotz into something like a .deb, .rpm, Slackware package, the NetBSD pkgsrc tree, FreeBSD's ports tree, or anything like that. When you do so I can note that on the Unix Frotz webpage at http://www.cs.csubak.edu/~dgriffi/proj/frotz. 2. DO NOT have auto-install programs or scripts grab files from that page. The Interactive Fiction Archive at http://www.ifarchive.org has mirrors around the world for that sort of thing. 3. If you distribute a patched version, SEND ME THE PATCHES. If you don't tell me what bugs you've found and fixed, I can't very well fix those bugs in the main codebase for all the other users of Frotz. Patches that simply change around options in the Makefile obviously don't apply. frotz-2.44/Makefile.tc0000644000175000017500000000274612527045477011623 00000000000000# This file is used to build Frotz for DOS using Borland Turbo C++ 3.00. # The resulting executable runs as a 16-bit program. # -O or higher optimization causes problems in quetzal.c # CC = tcc CFLAGS = -ml -N TLIB = tlib CP = copy RM = del RENAME = ren BINNAME = frotz.exe LIBRARY = frotz.lib SOUND_DEF = -DSOUND_SUPPORT DOS_DEF = -DMSDOS_16BIT DEFS = $(SOUND_DEF) $(DOS_DEF) DOS_DIR = src\dos DOS_OBJECTS = $(DOS_DIR)\bcinit.o \ $(DOS_DIR)\bcinput.o \ $(DOS_DIR)\bcmouse.o \ $(DOS_DIR)\bcpic.o \ $(DOS_DIR)\bcsample.o \ $(DOS_DIR)\bcscreen.o \ $(DOS_DIR)\bctext.o \ $(DOS_DIR)\bcblorb.o CORE_DIR = src\common CORE_OBJECTS = $(CORE_DIR)\buffer.o \ $(CORE_DIR)\fastmem.o \ $(CORE_DIR)\files.o \ $(CORE_DIR)\getopt.o \ $(CORE_DIR)\hotkey.o \ $(CORE_DIR)\input.o \ $(CORE_DIR)\main.o \ $(CORE_DIR)\math.o \ $(CORE_DIR)\object.o \ $(CORE_DIR)\process.o \ $(CORE_DIR)\random.o \ $(CORE_DIR)\redirect.o \ $(CORE_DIR)\screen.o \ $(CORE_DIR)\sound.o \ $(CORE_DIR)\stream.o \ $(CORE_DIR)\table.o \ $(CORE_DIR)\text.o \ $(CORE_DIR)\variable.o \ $(CORE_DIR)\quetzal.o \ $(CORE_DIR)\err.o .SUFFIXES: .c .o .h .c.o: $(CC) $(CFLAGS) $(DEFS) -I$(DOS_DIR) -I$(CORE_DIR) -c -o$@ $< $(TLIB) $(LIBRARY) +-$@ all: frotz clean: $(RM) $(CORE_DIR)\*.o $(RM) $(DOS_DIR)\*.o $(RM) *.lib $(RM) *.exe $(RM) *.bak frotz: $(DOS_OBJECTS) $(CORE_OBJECTS) $(CC) $(CFLAGS) -e$(BINNAME) $(LIBRARY) frotz-2.44/README.1st0000644000175000017500000000200712527045477011132 00000000000000What are all these files for? AUTHORS People who've had a hand in making Frotz what it is. BUGS A list of known bugs and workarounds (if known). build.bat A batch file for building Frotz for PC/MS DOS. COPYING Full text of the GNU General Public License. ChangeLog Summary of changes from one version to another. DOSBUILD.txt How to build and install Frotz for PC/MS DOS. DUMB Description of the dumb-interface version of Frotz. HOW_TO_PLAY How to play Interactive Fiction. INSTALL How to build and install Frotz. Makefile The Makefile (the INSTALL file tells you about this). Makefile.tc Makefile for building Frotz for DOS. PACKAGING Putting Frotz into a binary package or port tree. PORTING Notes about porting Frotz to new machines and operating systems. README An overview of Frotz. README.1st This file. SPEECH Notes about speech synthesis and voice-recognition in Frotz. TODO Stuff to do in no particular order. frotz.lsm An LSM file for Frotz. doc/ Manpages and sample config files. src/ Source code to Frotz. frotz-2.44/COPYING0000644000175000017500000004325412527045477010610 00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. frotz-2.44/SPEECH0000644000175000017500000000545412527042452010435 00000000000000=============================================== ----------------------------------------------- | Speech synthesis and recognition in Frotz | ----------------------------------------------- =============================================== This is highly-experimental code being commissioned by a presently undisclosed party. When complete, Frotz (at least for Linux and NetBSD) will speak its output and accept voice for input. The libraries being used to do this are Flite and Sphinx2. Public release in any meaningful way is on hold until the project is complete and I have been paid. In case you're wondering, this voice-enabled version of Frotz will appear as another make target in the Unix Frotz tarball. Flite (http://cmuflite.org/) is a small run-time speech synthesis engine created by Carnegie Mellon University around 1999. It's intended as a lightweight substitute for University of Edinburgh's Festival Speech Synthesis System and CMU's Festbox project. Flite is somewhat based on Festival, but requires neither of those systems to compile and run. At first I wanted to use Festival for voice output, but this quickly became impractical for various reasons (like the fact it only outputs to NAS). Sphinx2 (http://www.speech.cs.cmu.edu/sphinx/) is also from Carnagie Mellon. It is unique among voice-recognition schemes with which most people are familiar in that it doesn't need to be trained. That's right. Joe Blow can walk in off the street, talk to a program using Sphinx, and be understood. The tradeoff is that the programmer must know beforehand what words are to be recognized. This makes it difficult, if not impossible for voice-input to be used for arbitrary games. The game's dictionary must be parsed and a pronunciation guide made. This must be done manually because of the way the Z-machine recognizes words. Because it only cares about the first six letters, a real person must check for words longer than six letters, figure out what the rest of the letters are, and how the words should be pronounced. This is the core of the problem of supporting arbitrary games. A computer cannot "know" what a story is about in order to guess what the remaining letters are. You've probably encountered programs that do voice recognition like Sphinx does without realizing it. The most common example I can think of is how many locales handle collect calls. You get a phone call and an obviously recorded voice says something like the following: You have a collect call from . To accept the charges, please say "yes". That program is expecting to hear "yes" and is configured with several ways that "yes" might be constructed. For good measure, "yeah", "yep", "yup", "uh-huh", "alright", "okay", and other affirmatives are probably programmed in there too. I don't know. I haven't checked. frotz-2.44/src/0000755000175000017500000000000012527045477010414 500000000000000frotz-2.44/src/dumb/0000755000175000017500000000000012527045477011343 500000000000000frotz-2.44/src/dumb/dumb_output.c0000644000175000017500000003466412527045477014013 00000000000000/* dumb-output.c * $Id: dumb-output.c,v 1.2 2002/03/26 22:52:31 feedle Exp $ * * Copyright 1997,1998 Alfresco Petrofsky . * Any use permitted provided this notice stays intact. */ #include "dumb_frotz.h" f_setup_t f_setup; static bool show_line_numbers = FALSE; static bool show_line_types = -1; static bool show_pictures = TRUE; static bool visual_bell = TRUE; static bool plain_ascii = FALSE; static char latin1_to_ascii[] = " ! c L >o< Y | S '' C a << not - R _ " "^0 +/- ^2 ^3 ' my P . , ^1 o >> 1/4 1/2 3/4 ? " "A A A A Ae A AE C E E E E I I I I " "Th N O O O O Oe * O U U U Ue Y Th ss " "a a a a ae a ae c e e e e i i i i " "th n o o o o oe : o u u u ue y th y " ; /* h_screen_rows * h_screen_cols */ static int screen_cells; /* The in-memory state of the screen. */ /* Each cell contains a style in the upper byte and a char in the lower. */ typedef unsigned short cell; static cell *screen_data; static cell make_cell(int style, char c) {return (style << 8) | (0xff & c);} static char cell_char(cell c) {return c & 0xff;} static int cell_style(cell c) {return c >> 8;} /* A cell's style is REVERSE_STYLE, normal (0), or PICTURE_STYLE. * PICTURE_STYLE means the character is part of an ascii image outline * box. (This just buys us the ability to turn box display on and off * with immediate effect. No, not very useful, but I wanted to give * the rv bit some company in that huge byte I allocated for it.) */ #define PICTURE_STYLE 16 static int current_style = 0; /* Which cells have changed (1 byte per cell). */ static char *screen_changes; static int cursor_row = 0, cursor_col = 0; /* Compression styles. */ static enum { COMPRESSION_NONE, COMPRESSION_SPANS, COMPRESSION_MAX, } compression_mode = COMPRESSION_SPANS; static char *compression_names[] = {"NONE", "SPANS", "MAX"}; static int hide_lines = 0; /* Reverse-video display styles. */ static enum { RV_NONE, RV_DOUBLESTRIKE, RV_UNDERLINE, RV_CAPS, } rv_mode = RV_NONE; static char *rv_names[] = {"NONE", "DOUBLESTRIKE", "UNDERLINE", "CAPS"}; static char rv_blank_char = ' '; static cell *dumb_row(int r) {return screen_data + r * h_screen_cols;} static char *dumb_changes_row(int r) { return screen_changes + r * h_screen_cols; } int os_char_width (zchar z) { if (plain_ascii && z >= ZC_LATIN1_MIN && z <= ZC_LATIN1_MAX) { char *p = latin1_to_ascii + 4 * (z - ZC_LATIN1_MIN); return strchr(p, ' ') - p; } return 1; } int os_string_width (const zchar *s) { int width = 0; zchar c; while ((c = *s++) != 0) { if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) s++; else width += os_char_width(c); } return width; } void os_set_cursor(int row, int col) { cursor_row = row - 1; cursor_col = col - 1; if (cursor_row >= h_screen_rows) cursor_row = h_screen_rows - 1; } /* Set a cell and update screen_changes. */ static void dumb_set_cell(int row, int col, cell c) { dumb_changes_row(row)[col] = (c != dumb_row(row)[col]); dumb_row(row)[col] = c; } void dumb_set_picture_cell(int row, int col, char c) { dumb_set_cell(row, col, make_cell(PICTURE_STYLE, c)); } /* Copy a cell and copy its changedness state. * This is used for scrolling. */ static void dumb_copy_cell(int dest_row, int dest_col, int src_row, int src_col) { dumb_row(dest_row)[dest_col] = dumb_row(src_row)[src_col]; dumb_changes_row(dest_row)[dest_col] = dumb_changes_row(src_row)[src_col]; } void os_set_text_style(int x) { current_style = x & REVERSE_STYLE; } /* put a character in the cell at the cursor and advance the cursor. */ static void dumb_display_char(char c) { dumb_set_cell(cursor_row, cursor_col, make_cell(current_style, c)); if (++cursor_col == h_screen_cols) { if (cursor_row == h_screen_rows - 1) cursor_col--; else { cursor_row++; cursor_col = 0; } } } void dumb_display_user_input(char *s) { /* copy to screen without marking it as a change. */ while (*s) dumb_row(cursor_row)[cursor_col++] = make_cell(0, *s++); } void dumb_discard_old_input(int num_chars) { /* Weird discard stuff. Grep spec for 'pain in my butt'. */ /* The old characters should be on the screen just before the cursor. * Erase them. */ cursor_col -= num_chars; if (cursor_col < 0) cursor_col = 0; os_erase_area(cursor_row + 1, cursor_col + 1, cursor_row + 1, cursor_col + num_chars, -1); } void os_display_char (zchar c) { if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX) { if (plain_ascii) { char *ptr = latin1_to_ascii + 4 * (c - ZC_LATIN1_MIN); do dumb_display_char(*ptr++); while (*ptr != ' '); } else dumb_display_char(c); } else if (c >= 32 && c <= 126) { dumb_display_char(c); } else if (c == ZC_GAP) { dumb_display_char(' '); dumb_display_char(' '); } else if (c == ZC_INDENT) { dumb_display_char(' '); dumb_display_char(' '); dumb_display_char(' '); } return; } /* Haxor your boxor? */ void os_display_string (const zchar *s) { zchar c; while ((c = *s++) != 0) { if (c == ZC_NEW_FONT) s++; else if (c == ZC_NEW_STYLE) os_set_text_style(*s++); else { os_display_char (c); } } } void os_erase_area (int top, int left, int bottom, int right, int win) { int row, col; top--; left--; bottom--; right--; for (row = top; row <= bottom; row++) { for (col = left; col <= right; col++) dumb_set_cell(row, col, make_cell(current_style, ' ')); } } void os_scroll_area (int top, int left, int bottom, int right, int units) { int row, col; top--; left--; bottom--; right--; if (units > 0) { for (row = top; row <= bottom - units; row++) { for (col = left; col <= right; col++) dumb_copy_cell(row, col, row + units, col); } os_erase_area(bottom - units + 2, left + 1, bottom + 1, right + 1, -1 ); } else if (units < 0) { for (row = bottom; row >= top - units; row--) { for (col = left; col <= right; col++) dumb_copy_cell(row, col, row + units, col); } os_erase_area(top + 1, left + 1, top - units, right + 1 , -1); } } int os_font_data(int font, int *height, int *width) { if (font == TEXT_FONT) { *height = 1; *width = 1; return 1; } return 0; } void os_set_colour (int x, int y) {} void os_set_font (int x) {} /* Print a cell to stdout. */ static void show_cell(cell cel) { char c = cell_char(cel); switch (cell_style(cel)) { case 0: putchar(c); break; case PICTURE_STYLE: putchar(show_pictures ? c : ' '); break; case REVERSE_STYLE: if (c == ' ') putchar(rv_blank_char); else switch (rv_mode) { case RV_NONE: putchar(c); break; case RV_CAPS: putchar(toupper(c)); break; case RV_UNDERLINE: putchar('_'); putchar('\b'); putchar(c); break; case RV_DOUBLESTRIKE: putchar(c); putchar('\b'); putchar(c); break; } break; } } static bool will_print_blank(cell c) { return (((cell_style(c) == PICTURE_STYLE) && !show_pictures) || ((cell_char(c) == ' ') && ((cell_style(c) != REVERSE_STYLE) || (rv_blank_char == ' ')))); } static void show_line_prefix(int row, char c) { if (show_line_numbers) printf((row == -1) ? ".." : "%02d", (row + 1) % 100); if (show_line_types) putchar(c); /* Add a separator char (unless there's nothing to separate). */ if (show_line_numbers || show_line_types) putchar(' '); } /* Print a row to stdout. */ static void show_row(int r) { if (r == -1) { show_line_prefix(-1, '.'); } else { int c, last; show_line_prefix(r, (r == cursor_row) ? ']' : ' '); /* Don't print spaces at end of line. */ /* (Saves bandwidth and printhead wear.) */ /* TODO: compress spaces to tabs. */ for (last = h_screen_cols - 1; last >= 0; last--) if (!will_print_blank(dumb_row(r)[last])) break; for (c = 0; c <= last; c++) show_cell(dumb_row(r)[c]); } putchar('\n'); } /* Print the part of the cursor row before the cursor. */ void dumb_show_prompt(bool show_cursor, char line_type) { int i; show_line_prefix(show_cursor ? cursor_row : -1, line_type); if (show_cursor) { for (i = 0; i < cursor_col; i++) show_cell(dumb_row(cursor_row)[i]); } } static void mark_all_unchanged(void) { memset(screen_changes, 0, screen_cells); } /* Check if a cell is a blank or will display as one. * (Used to help decide if contents are worth printing.) */ static bool is_blank(cell c) { return ((cell_char(c) == ' ') || ((cell_style(c) == PICTURE_STYLE) && !show_pictures)); } /* Show the current screen contents, or what's changed since the last * call. * * If compressing, and show_cursor is true, and the cursor is past the * last nonblank character on the last line that would be shown, then * don't show that line (because it will be redundant with the prompt * line just below it). */ void dumb_show_screen(bool show_cursor) { int r, c, first, last; char changed_rows[0x100]; /* Easy case */ if (compression_mode == COMPRESSION_NONE) { for (r = hide_lines; r < h_screen_rows; r++) show_row(r); mark_all_unchanged(); return; } /* Check which rows changed, and where the first and last change is. */ first = last = -1; memset(changed_rows, 0, h_screen_rows); for (r = hide_lines; r < h_screen_rows; r++) { for (c = 0; c < h_screen_cols; c++) if (dumb_changes_row(r)[c] && !is_blank(dumb_row(r)[c])) break; changed_rows[r] = (c != h_screen_cols); if (changed_rows[r]) { first = (first != -1) ? first : r; last = r; } } if (first == -1) return; /* The show_cursor rule described above */ if (show_cursor && (cursor_row == last)) { for (c = cursor_col; c < h_screen_cols; c++) if (!is_blank(dumb_row(last)[c])) break; if (c == h_screen_cols) last--; } /* Display the appropriate rows. */ if (compression_mode == COMPRESSION_MAX) { for (r = first; r <= last; r++) if (changed_rows[r]) show_row(r); } else { /* COMPRESSION_SPANS */ for (r = first; r <= last; r++) { if (changed_rows[r] || changed_rows[r + 1]) show_row(r); else { while (!changed_rows[r + 1]) r++; show_row(-1); } } if (show_cursor && (cursor_row > last + 1)) show_row((cursor_row == last + 2) ? (last + 1) : -1); } mark_all_unchanged(); } /* Unconditionally show whole screen. For \s user command. */ void dumb_dump_screen(void) { int r; for (r = 0; r < h_screen_height; r++) show_row(r); } /* Called when it's time for a more prompt but user has them turned off. */ void dumb_elide_more_prompt(void) { dumb_show_screen(FALSE); if (compression_mode == COMPRESSION_SPANS && hide_lines == 0) { show_row(-1); } } void os_reset_screen(void) { dumb_show_screen(FALSE); } void os_beep (int volume) { if (visual_bell) printf("[%s-PITCHED BEEP]\n", (volume == 1) ? "HIGH" : "LOW"); else putchar('\a'); /* so much for dumb. */ } /* To make the common code happy */ void os_prepare_sample (int a) {} void os_finish_with_sample (int a) {} void os_start_sample (int a, int b, int c, zword d) {} void os_stop_sample (int a) {} /* if val is '0' or '1', set *var accordingly, else toggle it. */ static void toggle(bool *var, char val) { *var = val == '1' || (val != '0' && !*var); } bool dumb_output_handle_setting(const char *setting, bool show_cursor, bool startup) { char *p; int i; if (!strncmp(setting, "pb", 2)) { toggle(&show_pictures, setting[2]); printf("Picture outlines display %s\n", show_pictures ? "ON" : "OFF"); if (startup) return TRUE; for (i = 0; i < screen_cells; i++) screen_changes[i] = (cell_style(screen_data[i]) == PICTURE_STYLE); dumb_show_screen(show_cursor); } else if (!strncmp(setting, "vb", 2)) { toggle(&visual_bell, setting[2]); printf("Visual bell %s\n", visual_bell ? "ON" : "OFF"); os_beep(1); os_beep(2); } else if (!strncmp(setting, "ln", 2)) { toggle(&show_line_numbers, setting[2]); printf("Line numbering %s\n", show_line_numbers ? "ON" : "OFF"); } else if (!strncmp(setting, "lt", 2)) { toggle(&show_line_types, setting[2]); printf("Line-type display %s\n", show_line_types ? "ON" : "OFF"); } else if (*setting == 'c') { switch (setting[1]) { case 'm': compression_mode = COMPRESSION_MAX; break; case 's': compression_mode = COMPRESSION_SPANS; break; case 'n': compression_mode = COMPRESSION_NONE; break; case 'h': hide_lines = atoi(&setting[2]); break; default: return FALSE; } printf("Compression mode %s, hiding top %d lines\n", compression_names[compression_mode], hide_lines); } else if (*setting == 'r') { switch (setting[1]) { case 'n': rv_mode = RV_NONE; break; case 'o': rv_mode = RV_DOUBLESTRIKE; break; case 'u': rv_mode = RV_UNDERLINE; break; case 'c': rv_mode = RV_CAPS; break; case 'b': rv_blank_char = setting[2] ? setting[2] : ' '; break; default: return FALSE; } printf("Reverse-video mode %s, blanks reverse to '%c': ", rv_names[rv_mode], rv_blank_char); for (p = "sample reverse text"; *p; p++) show_cell(make_cell(REVERSE_STYLE, *p)); putchar('\n'); for (i = 0; i < screen_cells; i++) screen_changes[i] = (cell_style(screen_data[i]) == REVERSE_STYLE); dumb_show_screen(show_cursor); } else if (!strcmp(setting, "set")) { printf("Compression Mode %s, hiding top %d lines\n", compression_names[compression_mode], hide_lines); printf("Picture Boxes display %s\n", show_pictures ? "ON" : "OFF"); printf("Visual Bell %s\n", visual_bell ? "ON" : "OFF"); os_beep(1); os_beep(2); printf("Line Numbering %s\n", show_line_numbers ? "ON" : "OFF"); printf("Line-Type display %s\n", show_line_types ? "ON" : "OFF"); printf("Reverse-Video mode %s, Blanks reverse to '%c': ", rv_names[rv_mode], rv_blank_char); for (p = "sample reverse text"; *p; p++) show_cell(make_cell(REVERSE_STYLE, *p)); putchar('\n'); } else return FALSE; return TRUE; } void dumb_init_output(void) { if (h_version == V3) { h_config |= CONFIG_SPLITSCREEN; h_flags &= ~OLD_SOUND_FLAG; } if (h_version >= V5) { h_flags &= ~SOUND_FLAG; } h_screen_height = h_screen_rows; h_screen_width = h_screen_cols; screen_cells = h_screen_rows * h_screen_cols; h_font_width = 1; h_font_height = 1; if (show_line_types == -1) show_line_types = h_version > 3; screen_data = malloc(screen_cells * sizeof(cell)); screen_changes = malloc(screen_cells); os_erase_area(1, 1, h_screen_rows, h_screen_cols, -2); memset(screen_changes, 0, screen_cells); } frotz-2.44/src/dumb/dumb_init.c0000644000175000017500000001431012527045477013400 00000000000000/* dumb-init.c * $Id: dumb-init.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $ * * Copyright 1997,1998 Alva Petrofsky . * Any use permitted provided this notice stays intact. */ #include "dumb_frotz.h" f_setup_t f_setup; #define INFORMATION "\ An interpreter for all Infocom and other Z-Machine games.\n\ Complies with standard 1.0 of Graham Nelson's specification.\n\ \n\ Syntax: dfrotz [options] story-file\n\ -a watch attribute setting \t R xxx do runtime setting \\xxx\n\ -A watch attribute testing \t before starting (can be used repeatedly)\n\ -h # screen height \t -s # random number seed value\n\ -i ignore fatal errors \t -S # transcript width\n\ -I # interpreter number \t -t set Tandy bit\n\ -o watch object movement \t -u # slots for multiple undo\n\ -O watch object locating \t -w # screen width\n\ -p plain ASCII output only \t -x expand abbreviations g/x/z\n\ -P alter piracy opcode" /* A unix-like getopt, but with the names changed to avoid any problems. */ static int zoptind = 1; static int zoptopt = 0; static char *zoptarg = NULL; static int zgetopt (int argc, char *argv[], const char *options) { static int pos = 1; const char *p; if (zoptind >= argc || argv[zoptind][0] != '-' || argv[zoptind][1] == 0) return EOF; zoptopt = argv[zoptind][pos++]; zoptarg = NULL; if (argv[zoptind][pos] == 0) { pos = 1; zoptind++; } p = strchr (options, zoptopt); if (zoptopt == ':' || p == NULL) { fputs ("illegal option -- ", stderr); goto error; } else if (p[1] == ':') { if (zoptind >= argc) { fputs ("option requires an argument -- ", stderr); goto error; } else { zoptarg = argv[zoptind]; if (pos != 1) zoptarg += pos; pos = 1; zoptind++; } } return zoptopt; error: fputc (zoptopt, stderr); fputc ('\n', stderr); return '?'; }/* zgetopt */ static int user_screen_width = 75; static int user_screen_height = 24; static int user_interpreter_number = -1; static int user_random_seed = -1; static int user_tandy_bit = 0; static char *graphics_filename = NULL; static bool plain_ascii = FALSE; void os_process_arguments(int argc, char *argv[]) { int c; do_more_prompts = TRUE; /* Parse the options */ do { c = zgetopt(argc, argv, "aAh:iI:moOpPs:R:S:tu:w:xZ:"); switch(c) { case 'a': f_setup.attribute_assignment = 1; break; case 'A': f_setup.attribute_testing = 1; break; case 'h': user_screen_height = atoi(zoptarg); break; case 'i': f_setup.ignore_errors = 1; break; case 'I': f_setup.interpreter_number = atoi(zoptarg); break; case 'm': do_more_prompts = FALSE; case 'o': f_setup.object_movement = 1; break; case 'O': f_setup.object_locating = 1; break; case 'P': f_setup.piracy = 1; break; case 'p': plain_ascii = 1; break; case 'R': dumb_handle_setting(zoptarg, FALSE, TRUE); break; case 's': user_random_seed = atoi(zoptarg); break; case 'S': f_setup.script_cols = atoi(zoptarg); break; case 't': user_tandy_bit = 1; break; case 'u': f_setup.undo_slots = atoi(zoptarg); break; case 'w': user_screen_width = atoi(zoptarg); break; case 'x': f_setup.expand_abbreviations = 1; break; case 'Z': f_setup.err_report_mode = atoi(zoptarg); if ((f_setup.err_report_mode < ERR_REPORT_NEVER) || (f_setup.err_report_mode > ERR_REPORT_FATAL)) f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; break; } } while (c != EOF); if (((argc - zoptind) != 1) && ((argc - zoptind) != 2)) { printf("FROTZ V%s\tdumb interface.\n", VERSION); puts(INFORMATION); printf("\t-Z # error checking mode (default = %d)\n" "\t %d = don't report errors %d = report first error\n" "\t %d = report all errors %d = exit after any error\n\n", ERR_DEFAULT_REPORT_MODE, ERR_REPORT_NEVER, ERR_REPORT_ONCE, ERR_REPORT_ALWAYS, ERR_REPORT_FATAL); printf("While running, enter \"\\help\" to list the runtime escape sequences\n\n"); exit(1); } /* if (((argc - zoptind) != 1) && ((argc - zoptind) != 2)) { puts(usage); exit(1); } */ f_setup.story_file = argv[zoptind++]; if (zoptind < argc) graphics_filename = argv[zoptind++]; f_setup.save_name = malloc(FILENAME_MAX); } void os_init_screen(void) { if (h_version == V3 && user_tandy_bit) h_config |= CONFIG_TANDY; if (h_version >= V5 && f_setup.undo_slots == 0) h_flags &= ~UNDO_FLAG; h_screen_rows = user_screen_height; h_screen_cols = user_screen_width; if (user_interpreter_number > 0) h_interpreter_number = user_interpreter_number; else { /* Use ms-dos for v6 (because that's what most people have the * graphics files for), but don't use it for v5 (or Beyond Zork * will try to use funky characters). */ h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20; } h_interpreter_version = 'F'; dumb_init_input(); dumb_init_output(); dumb_init_pictures(graphics_filename); } int os_random_seed (void) { if (user_random_seed == -1) /* Use the epoch as seed value */ return (time(0) & 0x7fff); else return user_random_seed; } void os_restart_game (int stage) {} void os_fatal (const char *s, ...) { fprintf(stderr, "\nFatal error: %s\n", s); exit(1); } FILE *os_load_story(void) { return fopen(f_setup.story_file, "rb"); } /* * Seek into a storyfile, either a standalone file or the * ZCODE chunk of a blorb file (dumb does not support blorb * so this is just a wrapper for fseek) */ int os_storyfile_seek(FILE * fp, long offset, int whence) { return fseek(fp, offset, whence); } /* * Tell the position in a storyfile, either a standalone file * or the ZCODE chunk of a blorb file (dumb does not support * blorb so this is just a wrapper for fseek) */ int os_storyfile_tell(FILE * fp) { return ftell(fp); } void os_init_setup(void) { f_setup.attribute_assignment = 0; f_setup.attribute_testing = 0; f_setup.context_lines = 0; f_setup.object_locating = 0; f_setup.object_movement = 0; f_setup.left_margin = 0; f_setup.right_margin = 0; f_setup.ignore_errors = 0; f_setup.piracy = 0; f_setup.undo_slots = MAX_UNDO_SLOTS; f_setup.expand_abbreviations = 0; f_setup.script_cols = 80; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; } frotz-2.44/src/dumb/dumb_frotz.h0000644000175000017500000000234312527045477013611 00000000000000/* dumb-frotz.h * $Id: dumb-frotz.h,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $ * Frotz os functions for a standard C library and a dumb terminal. * Now you can finally play Zork Zero on your Teletype. * * Copyright 1997, 1998 Alembic Petrofsky . * Any use permitted provided this notice stays intact. */ #include "../common/frotz.h" #include #include #include #include #include #include /* from ../common/setup.h */ extern f_setup_t f_setup; bool do_more_prompts; /* From input.c. */ bool is_terminator (zchar); /* dumb-input.c */ bool dumb_handle_setting(const char *setting, bool show_cursor, bool startup); void dumb_init_input(void); /* dumb-output.c */ void dumb_init_output(void); bool dumb_output_handle_setting(const char *setting, bool show_cursor, bool startup); void dumb_show_screen(bool show_cursor); void dumb_show_prompt(bool show_cursor, char line_type); void dumb_dump_screen(void); void dumb_display_user_input(char *); void dumb_discard_old_input(int num_chars); void dumb_elide_more_prompt(void); void dumb_set_picture_cell(int row, int col, char c); /* dumb-pic.c */ void dumb_init_pictures(char *graphics_filename); frotz-2.44/src/dumb/dumb_pic.c0000644000175000017500000001011512527045477013207 00000000000000/* dumb-pic.c * $Id: dumb-pic.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $ * * Copyright 1997,1998 Alcibiades Petrofsky * . * Any use permitted provided this notice stays intact. */ #include "dumb_frotz.h" f_setup_t f_setup; #define PIC_FILE_HEADER_FLAGS 1 #define PIC_FILE_HEADER_NUM_IMAGES 4 #define PIC_FILE_HEADER_ENTRY_SIZE 8 #define PIC_FILE_HEADER_VERSION 14 #define PIC_HEADER_NUMBER 0 #define PIC_HEADER_WIDTH 2 #define PIC_HEADER_HEIGHT 4 static struct { int z_num; int width; int height; int orig_width; int orig_height; } *pict_info; static int num_pictures = 0; static unsigned char lookupb(unsigned char *p, int n) {return p[n];} static unsigned short lookupw(unsigned char *p, int n) { return (p[n + 1] << 8) | p[n]; } void dumb_init_pictures (char *filename) { FILE *file = NULL; int success = FALSE; unsigned char gheader[16]; unsigned char *raw_info = NULL; int i, entry_size, flags; float x_scaler, y_scaler; do { if ((h_version != V6) || !filename || ((file = fopen (filename, "rb")) == NULL) || (fread(&gheader, sizeof (gheader), 1, file) != 1)) break; num_pictures = lookupw(gheader, PIC_FILE_HEADER_NUM_IMAGES); entry_size = lookupb(gheader, PIC_FILE_HEADER_ENTRY_SIZE); flags = lookupb(gheader, PIC_FILE_HEADER_FLAGS); raw_info = malloc(num_pictures * entry_size); if (fread(raw_info, num_pictures * entry_size, 1, file) != 1) break; pict_info = malloc((num_pictures + 1) * sizeof(*pict_info)); pict_info[0].z_num = 0; pict_info[0].height = num_pictures; pict_info[0].width = lookupw(gheader, PIC_FILE_HEADER_VERSION); y_scaler = h_screen_rows / 200.0; x_scaler = h_screen_cols / ((flags & 0x08) ? 640.0 : 320.0); /* Copy and scale. */ for (i = 1; i <= num_pictures; i++) { unsigned char *p = raw_info + entry_size * (i - 1); pict_info[i].z_num = lookupw(p, PIC_HEADER_NUMBER); pict_info[i].orig_height = lookupw(p, PIC_HEADER_HEIGHT); pict_info[i].orig_width = lookupw(p, PIC_HEADER_WIDTH); pict_info[i].height = pict_info[i].orig_height * y_scaler + .5; pict_info[i].width = pict_info[i].orig_width * x_scaler + .5; } success = TRUE; } while (0); if (file) fclose(file); if (raw_info) free(raw_info); if (success) h_config |= CONFIG_PICTURES; else { h_flags &= ~GRAPHICS_FLAG; if (filename) fprintf(stderr, "Warning: could not read graphics file %s\n", filename); } } /* Convert a Z picture number to an index into pict_info. */ static int z_num_to_index(int n) { int i; for (i = 0; i <= num_pictures; i++) if (pict_info[i].z_num == n) return i; return -1; } bool os_picture_data(int num, int *height, int *width) { int index; *height = 0; *width = 0; if (!pict_info) return FALSE; if ((index = z_num_to_index(num)) == -1) return FALSE; *height = pict_info[index].height; *width = pict_info[index].width; return TRUE; } void os_draw_picture (int num, int row, int col) { int width, height, r, c; if (!os_picture_data(num, &height, &width) || !width || !height) return; col--, row--; /* Draw corners */ dumb_set_picture_cell(row, col, '+'); dumb_set_picture_cell(row, col + width - 1, '+'); dumb_set_picture_cell(row + height - 1, col, '+'); dumb_set_picture_cell(row + height - 1, col + width - 1, '+'); /* sides */ for (c = col + 1; c < col + width - 1; c++) { dumb_set_picture_cell(row, c, '-'); dumb_set_picture_cell(row + height - 1, c, '-'); } for (r = row + 1; r < row + height - 1; r++) { dumb_set_picture_cell(r, col, '|'); dumb_set_picture_cell(r, col + width - 1, '|'); } /* body, but for last line */ for (r = row + 1; r < row + height - 2; r++) for (c = col + 1; c < col + width - 1; c++) dumb_set_picture_cell(r, c, ':'); /* Last line of body, including picture number. */ if (height >= 3) for (c = col + width - 2; c > col; c--, (num /= 10)) dumb_set_picture_cell(row + height - 2, c, num ? (num % 10 + '0') : ':'); } int os_peek_colour (void) {return BLACK_COLOUR; } frotz-2.44/src/dumb/dumb_input.c0000644000175000017500000003175512527045477013610 00000000000000/* dumb-input.c * $Id: dumb-input.c,v 1.1.1.1 2002/03/26 22:38:34 feedle Exp $ * Copyright 1997,1998 Alpine Petrofsky . * Any use permitted provided this notice stays intact. */ #include "dumb_frotz.h" f_setup_t f_setup; static char runtime_usage[] = "DUMB-FROTZ runtime help:\n" " General Commands:\n" " \\help Show this message.\n" " \\set Show the current values of runtime settings.\n" " \\s Show the current contents of the whole screen.\n" " \\d Discard the part of the input before the cursor.\n" " \\wN Advance clock N/10 seconds, possibly causing the current\n" " and subsequent inputs to timeout.\n" " \\w Advance clock by the amount of real time since this input\n" " started (times the current speed factor).\n" " \\t Advance clock just enough to timeout the current input\n" " Reverse-Video Display Method Settings:\n" " \\rn none \\rc CAPS \\rd doublestrike \\ru underline\n" " \\rbC show rv blanks as char C (orthogonal to above modes)\n" " Output Compression Settings:\n" " \\cn none: show whole screen before every input.\n" " \\cm max: show only lines that have new nonblank characters.\n" " \\cs spans: like max, but emit a blank line between each span of\n" " screen lines shown.\n" " \\chN Hide top N lines (orthogonal to above modes).\n" " Misc Settings:\n" " \\sfX Set speed factor to X. (0 = never timeout automatically).\n" " \\mp Toggle use of MORE prompts\n" " \\ln Toggle display of line numbers.\n" " \\lt Toggle display of the line type identification chars.\n" " \\vb Toggle visual bell.\n" " \\pb Toggle display of picture outline boxes.\n" " (Toggle commands can be followed by a 1 or 0 to set value ON or OFF.)\n" " Character Escapes:\n" " \\\\ backslash \\# backspace \\[ escape \\_ return\n" " \\< \\> \\^ \\. cursor motion \\1 ..\\0 f1..f10\n" " \\D ..\\X Standard Frotz hotkeys. Use \\H (help) to see the list.\n" " Line Type Identification Characters:\n" " Input lines:\n" " untimed timed\n" " > T A regular line-oriented input\n" " ) t A single-character input\n" " } D A line input with some input before the cursor.\n" " (Use \\d to discard it.)\n" " Output lines:\n" " ] Output line that contains the cursor.\n" " . A blank line emitted as part of span compression.\n" " (blank) Any other output line.\n" ; static float speed = 1; enum input_type { INPUT_CHAR, INPUT_LINE, INPUT_LINE_CONTINUED, }; /* get a character. Exit with no fuss on EOF. */ static int xgetchar(void) { int c = getchar(); if (c == EOF) { if (feof(stdin)) { fprintf(stderr, "\nEOT\n"); exit(0); } os_fatal(strerror(errno)); } return c; } /* Read one line, including the newline, into s. Safely avoids buffer * overruns (but that's kind of pointless because there are several * other places where I'm not so careful). */ static void dumb_getline(char *s) { int c; char *p = s; while (p < s + INPUT_BUFFER_SIZE - 1) { if ((*p++ = xgetchar()) == '\n') { *p = '\0'; return; } } p[-1] = '\n'; p[0] = '\0'; while ((c = xgetchar()) != '\n') ; printf("Line too long, truncated to %s\n", s - INPUT_BUFFER_SIZE); } /* Translate in place all the escape characters in s. */ static void translate_special_chars(char *s) { char *src = s, *dest = s; while (*src) switch(*src++) { default: *dest++ = src[-1]; break; case '\n': *dest++ = ZC_RETURN; break; case '\\': switch (*src++) { case '\n': *dest++ = ZC_RETURN; break; case '\\': *dest++ = '\\'; break; case '?': *dest++ = ZC_BACKSPACE; break; case '[': *dest++ = ZC_ESCAPE; break; case '_': *dest++ = ZC_RETURN; break; case '^': *dest++ = ZC_ARROW_UP; break; case '.': *dest++ = ZC_ARROW_DOWN; break; case '<': *dest++ = ZC_ARROW_LEFT; break; case '>': *dest++ = ZC_ARROW_RIGHT; break; case 'R': *dest++ = ZC_HKEY_RECORD; break; case 'P': *dest++ = ZC_HKEY_PLAYBACK; break; case 'S': *dest++ = ZC_HKEY_SEED; break; case 'U': *dest++ = ZC_HKEY_UNDO; break; case 'N': *dest++ = ZC_HKEY_RESTART; break; case 'X': *dest++ = ZC_HKEY_QUIT; break; case 'D': *dest++ = ZC_HKEY_DEBUG; break; case 'H': *dest++ = ZC_HKEY_HELP; break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': *dest++ = ZC_FKEY_MIN + src[-1] - '0' - 1; break; case '0': *dest++ = ZC_FKEY_MIN + 9; break; default: fprintf(stderr, "DUMB-FROTZ: unknown escape char: %c\n", src[-1]); fprintf(stderr, "Enter \\help to see the list\n"); } } *dest = '\0'; } /* The time in tenths of seconds that the user is ahead of z time. */ static int time_ahead = 0; /* Called from os_read_key and os_read_line if they have input from * a previous call to dumb_read_line. * Returns TRUE if we should timeout rather than use the read-ahead. * (because the user is further ahead than the timeout). */ static bool check_timeout(int timeout) { if ((timeout == 0) || (timeout > time_ahead)) time_ahead = 0; else time_ahead -= timeout; return time_ahead != 0; } /* If val is '0' or '1', set *var accordingly, otherwise toggle it. */ static void toggle(bool *var, char val) { *var = val == '1' || (val != '0' && !*var); } /* Handle input-related user settings and call dumb_output_handle_setting. */ bool dumb_handle_setting(const char *setting, bool show_cursor, bool startup) { if (!strncmp(setting, "sf", 2)) { speed = atof(&setting[2]); printf("Speed Factor %g\n", speed); } else if (!strncmp(setting, "mp", 2)) { toggle(&do_more_prompts, setting[2]); printf("More prompts %s\n", do_more_prompts ? "ON" : "OFF"); } else { if (!strcmp(setting, "set")) { printf("Speed Factor %g\n", speed); printf("More Prompts %s\n", do_more_prompts ? "ON" : "OFF"); } return dumb_output_handle_setting(setting, show_cursor, startup); } return TRUE; } /* Read a line, processing commands (lines that start with a backslash * (that isn't the start of a special character)), and write the * first non-command to s. * Return true if timed-out. */ static bool dumb_read_line(char *s, char *prompt, bool show_cursor, int timeout, enum input_type type, zchar *continued_line_chars) { time_t start_time; if (timeout) { if (time_ahead >= timeout) { time_ahead -= timeout; return TRUE; } timeout -= time_ahead; start_time = time(0); } time_ahead = 0; dumb_show_screen(show_cursor); for (;;) { char *command; if (prompt) fputs(prompt, stdout); else dumb_show_prompt(show_cursor, (timeout ? "tTD" : ")>}")[type]); /* Prompt only shows up after user input if we don't flush stdout */ fflush(stdout); dumb_getline(s); if ((s[0] != '\\') || ((s[1] != '\0') && !islower(s[1]))) { /* Is not a command line. */ translate_special_chars(s); if (timeout) { int elapsed = (time(0) - start_time) * 10 * speed; if (elapsed > timeout) { time_ahead = elapsed - timeout; return TRUE; } } return FALSE; } /* Commands. */ /* Remove the \ and the terminating newline. */ command = s + 1; command[strlen(command) - 1] = '\0'; if (!strcmp(command, "t")) { if (timeout) { time_ahead = 0; s[0] = '\0'; return TRUE; } } else if (*command == 'w') { if (timeout) { int elapsed = atoi(&command[1]); time_t now = time(0); if (elapsed == 0) elapsed = (now - start_time) * 10 * speed; if (elapsed >= timeout) { time_ahead = elapsed - timeout; s[0] = '\0'; return TRUE; } timeout -= elapsed; start_time = now; } } else if (!strcmp(command, "d")) { if (type != INPUT_LINE_CONTINUED) fprintf(stderr, "DUMB-FROTZ: No input to discard\n"); else { dumb_discard_old_input(strlen(continued_line_chars)); continued_line_chars[0] = '\0'; type = INPUT_LINE; } } else if (!strcmp(command, "help")) { if (!do_more_prompts) fputs(runtime_usage, stdout); else { char *current_page, *next_page; current_page = next_page = runtime_usage; for (;;) { int i; for (i = 0; (i < h_screen_rows - 2) && *next_page; i++) next_page = strchr(next_page, '\n') + 1; /* next_page - current_page is width */ printf("%.*s", next_page - current_page, current_page); current_page = next_page; if (!*current_page) break; printf("HELP: Type for more, or q to stop: "); fflush(stdout); dumb_getline(s); if (!strcmp(s, "q\n")) break; } } } else if (!strcmp(command, "s")) { dumb_dump_screen(); } else if (!dumb_handle_setting(command, show_cursor, FALSE)) { fprintf(stderr, "DUMB-FROTZ: unknown command: %s\n", s); fprintf(stderr, "Enter \\help to see the list of commands\n"); } } } /* Read a line that is not part of z-machine input (more prompts and * filename requests). */ static void dumb_read_misc_line(char *s, char *prompt) { dumb_read_line(s, prompt, 0, 0, 0, 0); /* Remove terminating newline */ s[strlen(s) - 1] = '\0'; } /* For allowing the user to input in a single line keys to be returned * for several consecutive calls to read_char, with no screen update * in between. Useful for traversing menus. */ static char read_key_buffer[INPUT_BUFFER_SIZE]; /* Similar. Useful for using function key abbreviations. */ static char read_line_buffer[INPUT_BUFFER_SIZE]; zchar os_read_key (int timeout, bool show_cursor) { char c; int timed_out; /* Discard any keys read for line input. */ read_line_buffer[0] = '\0'; if (read_key_buffer[0] == '\0') { timed_out = dumb_read_line(read_key_buffer, NULL, show_cursor, timeout, INPUT_CHAR, NULL); /* An empty input line is reported as a single CR. * If there's anything else in the line, we report only the line's * contents and not the terminating CR. */ if (strlen(read_key_buffer) > 1) read_key_buffer[strlen(read_key_buffer) - 1] = '\0'; } else timed_out = check_timeout(timeout); if (timed_out) return ZC_TIME_OUT; c = read_key_buffer[0]; memmove(read_key_buffer, read_key_buffer + 1, strlen(read_key_buffer)); /* TODO: error messages for invalid special chars. */ return c; } zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) { char *p; int terminator; static bool timed_out_last_time; int timed_out; /* Discard any keys read for single key input. */ read_key_buffer[0] = '\0'; /* After timing out, discard any further input unless we're continuing. */ if (timed_out_last_time && !continued) read_line_buffer[0] = '\0'; if (read_line_buffer[0] == '\0') timed_out = dumb_read_line(read_line_buffer, NULL, TRUE, timeout, buf[0] ? INPUT_LINE_CONTINUED : INPUT_LINE, buf); else timed_out = check_timeout(timeout); if (timed_out) { timed_out_last_time = TRUE; return ZC_TIME_OUT; } /* find the terminating character. */ for (p = read_line_buffer;; p++) { if (is_terminator(*p)) { terminator = *p; *p++ = '\0'; break; } } /* TODO: Truncate to width and max. */ /* copy to screen */ dumb_display_user_input(read_line_buffer); /* copy to the buffer and save the rest for next time. */ strcat(buf, read_line_buffer); p = read_line_buffer + strlen(read_line_buffer) + 1; memmove(read_line_buffer, p, strlen(p) + 1); /* If there was just a newline after the terminating character, * don't save it. */ if ((read_line_buffer[0] == '\r') && (read_line_buffer[1] == '\0')) read_line_buffer[0] = '\0'; timed_out_last_time = FALSE; return terminator; } int os_read_file_name (char *file_name, const char *default_name, int flag) { char buf[INPUT_BUFFER_SIZE], prompt[INPUT_BUFFER_SIZE]; FILE *fp; sprintf(prompt, "Please enter a filename [%s]: ", default_name); dumb_read_misc_line(buf, prompt); if (strlen(buf) > MAX_FILE_NAME) { printf("Filename too long\n"); return FALSE; } strcpy (file_name, buf[0] ? buf : default_name); /* Warn if overwriting a file. */ if ((flag == FILE_SAVE || flag == FILE_SAVE_AUX || flag == FILE_RECORD) && ((fp = fopen(file_name, "rb")) != NULL)) { fclose (fp); dumb_read_misc_line(buf, "Overwrite existing file? "); return(tolower(buf[0]) == 'y'); } return TRUE; } void os_more_prompt (void) { if (do_more_prompts) { char buf[INPUT_BUFFER_SIZE]; dumb_read_misc_line(buf, "***MORE***"); } else dumb_elide_more_prompt(); } void dumb_init_input(void) { if ((h_version >= V4) && (speed != 0)) h_config |= CONFIG_TIMEDINPUT; if (h_version >= V5) h_flags &= ~(MOUSE_FLAG | MENU_FLAG); } zword os_read_mouse(void) { /* NOT IMPLEMENTED */ return 0; } frotz-2.44/src/misc/0000755000175000017500000000000012527045477011347 500000000000000frotz-2.44/src/misc/findsound.sh0000755000175000017500000000401412527042452013604 00000000000000#!/bin/sh # This thing is intended primarily to find soundcard.h for use with the # OSS soundcard driver. # # Linux typically has # NetBSD and OpenBSD have # FreeBSD seems to have # # This script will be replaced with something more decent when support for # more sound drivers is added. This file will be executed during compile # even if you aren't using sound. INCLUDE1="/usr/include/sys" INCLUDE2="/usr/include" INCLUDE3="/usr/include/machine" SOUNDCARD_H="soundcard.h" OUTFILE="$1/soundcard.h" echo "checking for $SOUNDCARD_H" #if [ -f $OUTFILE ] ; then # echo "$SOUNDCARD_H already made." # exit 0 #fi cat << EOF > $OUTFILE /* * This file automatically generated by findsound.sh which run by the * Makefile found in the Unix Frotz 2.43 source distribution. * Copying this nasty hack to find headers which may be in any of several * places is not recommended. I don't want to use autoconf just yet for * this project. * */ EOF FILE=$INCLUDE1/$SOUNDCARD_H if [ -f $FILE ] ; then echo "I see we have $FILE..." echo if [ -r $FILE ] ; then echo '#include ' >> $OUTFILE else echo "=====================================" echo "Oops... Can't read $FILE!" echo "=====================================" exit 1 fi exit 0 fi # I'm too lazy to iterate properly right now FILE=$INCLUDE2/$SOUNDCARD_H if [ -f $FILE ] ; then echo "I see we have $FILE..." echo if [ -r $FILE ] ; then echo '#include ' >> $OUTFILE else echo "=====================================" echo "Oops... Can't read $FILE!" echo "=====================================" exit 2 fi exit 0 fi # I'm too lazy to iterate properly right now FILE=$INCLUDE3/$SOUNDCARD_H if [ -f $FILE ] ; then echo "I see we have $FILE..." echo if [ -r $FILE ] ; then echo '#include ' >> $OUTFILE else echo "=====================================" echo "Oops... Can't read $FILE!" echo "=====================================" exit 3 fi exit 0 fi frotz-2.44/src/sdl/0000755000175000017500000000000012527045477011176 500000000000000frotz-2.44/src/sdl/sf_font3.c0000644000175000017500000001427712527045477013016 00000000000000#include "sf_frotz.h" #include static byte Zfont3[] = { /* 32*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 33*/ 0x00,0x00,0x20,0x60,0xfe,0x60,0x20,0x00, /* 34*/ 0x00,0x00,0x08,0x0c,0xfe,0x0c,0x08,0x00, /* 35*/ 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, /* 36*/ 0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01, /* 37*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 38*/ 0x00,0x00,0x00,0x00,0xff,0x00,0x00,0x00, /* 39*/ 0x00,0x00,0x00,0xff,0x00,0x00,0x00,0x00, /* 40*/ 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, /* 41*/ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, /* 42*/ 0x08,0x08,0x08,0xff,0x00,0x00,0x00,0x00, /* 43*/ 0x00,0x00,0x00,0x00,0xff,0x08,0x08,0x08, /* 44*/ 0x08,0x08,0x08,0x08,0x0f,0x08,0x08,0x08, /* 45*/ 0x10,0x10,0x10,0x10,0xf0,0x10,0x10,0x10, /* 46*/ 0x10,0x10,0x10,0x10,0x1f,0x00,0x00,0x00, /* 47*/ 0x00,0x00,0x00,0x1f,0x10,0x10,0x10,0x10, /* 48*/ 0x00,0x00,0x00,0xf8,0x08,0x08,0x08,0x08, /* 49*/ 0x08,0x08,0x08,0x08,0xf8,0x00,0x00,0x00, /* 50*/ 0x10,0x10,0x10,0x10,0x1f,0x20,0x40,0x80, /* 51*/ 0x80,0x40,0x20,0x1f,0x10,0x10,0x10,0x10, /* 52*/ 0x01,0x02,0x04,0xf8,0x08,0x08,0x08,0x08, /* 53*/ 0x08,0x08,0x08,0x08,0xf8,0x04,0x02,0x01, /* 54*/ 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, /* 55*/ 0xff,0xff,0xff,0xff,0xff,0x00,0x00,0x00, /* 56*/ 0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff, /* 57*/ 0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8,0xf8, /* 58*/ 0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f,0x1f, /* 59*/ 0x08,0x08,0x08,0xff,0xff,0xff,0xff,0xff, /* 60*/ 0xff,0xff,0xff,0xff,0xff,0x08,0x08,0x08, /* 61*/ 0xf8,0xf8,0xf8,0xf8,0xff,0xf8,0xf8,0xf8, /* 62*/ 0x1f,0x1f,0x1f,0x1f,0xff,0x1f,0x1f,0x1f, /* 63*/ 0x1f,0x1f,0x1f,0x1f,0x1f,0x00,0x00,0x00, /* 64*/ 0x00,0x00,0x00,0x1f,0x1f,0x1f,0x1f,0x1f, /* 65*/ 0x00,0x00,0x00,0xf8,0xf8,0xf8,0xf8,0xf8, /* 66*/ 0xf8,0xf8,0xf8,0xf8,0xf8,0x00,0x00,0x00, /* 67*/ 0x1f,0x1f,0x1f,0x1f,0x1f,0x20,0x40,0x80, /* 68*/ 0x80,0x40,0x20,0x1f,0x1f,0x1f,0x1f,0x1f, /* 69*/ 0x01,0x02,0x04,0xf8,0xf8,0xf8,0xf8,0xf8, /* 70*/ 0xf8,0xf8,0xf8,0xf8,0xf8,0x04,0x02,0x01, /* 71*/ 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 72*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01, /* 73*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80, /* 74*/ 0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 75*/ 0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 76*/ 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff, /* 77*/ 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, /* 78*/ 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01, /* 79*/ 0x00,0xff,0x00,0x00,0x00,0x00,0xff,0x00, /* 80*/ 0x00,0xff,0x80,0x80,0x80,0x80,0xff,0x00, /* 81*/ 0x00,0xff,0xc0,0xc0,0xc0,0xc0,0xff,0x00, /* 82*/ 0x00,0xff,0xe0,0xe0,0xe0,0xe0,0xff,0x00, /* 83*/ 0x00,0xff,0xf0,0xf0,0xf0,0xf0,0xff,0x00, /* 84*/ 0x00,0xff,0xf8,0xf8,0xf8,0xf8,0xff,0x00, /* 85*/ 0x00,0xff,0xfc,0xfc,0xfc,0xfc,0xff,0x00, /* 86*/ 0x00,0xff,0xfe,0xfe,0xfe,0xfe,0xff,0x00, /* 87*/ 0x00,0xff,0xff,0xff,0xff,0xff,0xff,0x00, /* 88*/ 0x00,0x01,0x01,0x01,0x01,0x01,0x01,0x00, /* 89*/ 0x00,0x80,0x80,0x80,0x80,0x80,0x80,0x00, /* 90*/ 0x81,0x42,0x24,0x18,0x18,0x24,0x42,0x81, /* 91*/ 0x08,0x08,0x08,0x08,0xff,0x08,0x08,0x08, /* 92*/ 0x18,0x3c,0xdb,0x18,0x18,0x18,0x18,0x00, /* 93*/ 0x18,0x18,0x18,0x18,0xdb,0x3c,0x18,0x00, /* 94*/ 0x18,0x3c,0xdb,0x18,0xdb,0x3c,0x18,0x00, /* 95*/ 0xff,0x81,0x81,0x81,0x81,0x81,0x81,0xff, /* 96*/ 0x3c,0x66,0x06,0x0c,0x18,0x00,0x18,0x00, /* 97*/ 0xc4,0xa8,0x90,0xc0,0xa0,0x90,0x80,0x00, /* 98*/ 0x60,0x50,0x48,0x70,0x48,0x50,0x60,0x00, /* 99*/ 0x10,0x18,0x14,0x92,0x50,0x30,0x10,0x00, /*100*/ 0x82,0xc6,0xaa,0x92,0xaa,0xc6,0x82,0x00, /*101*/ 0x82,0xc6,0xaa,0x92,0x82,0x82,0x82,0x00, /*102*/ 0x94,0xa8,0xd0,0xa0,0xc0,0x80,0x80,0x00, /*103*/ 0x82,0x44,0x28,0x10,0x28,0x44,0x82,0x00, /*104*/ 0xc2,0xa2,0xd2,0xaa,0x96,0x8a,0x86,0x00, /*105*/ 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x00, /*106*/ 0x10,0x38,0x54,0x92,0x54,0x38,0x10,0x00, /*107*/ 0x10,0x10,0x10,0x38,0x54,0x92,0x92,0x00, /*108*/ 0x10,0x18,0x14,0x12,0x10,0x10,0x10,0x00, /*109*/ 0xc6,0xaa,0x92,0xaa,0xc6,0x82,0x82,0x00, /*110*/ 0x90,0x50,0x38,0x14,0x12,0x10,0x10,0x00, /*111*/ 0xc4,0xac,0xd4,0xa8,0x90,0x80,0x80,0x00, /*112*/ 0x80,0x80,0x80,0x90,0xa8,0xc4,0x82,0x00, /*113*/ 0x40,0x40,0x40,0x78,0x44,0x44,0x44,0x00, /*114*/ 0x60,0x50,0x48,0x50,0x60,0x50,0x48,0x00, /*115*/ 0x40,0x44,0x4c,0x54,0x64,0x44,0x04,0x00, /*116*/ 0x10,0x38,0x54,0x92,0x10,0x10,0x10,0x00, /*117*/ 0x60,0x50,0x48,0x44,0x44,0x44,0x44,0x00, /*118*/ 0x10,0xba,0x54,0x10,0x10,0x10,0x10,0x00, /*119*/ 0x60,0x50,0x48,0x50,0x60,0x40,0x40,0x00, /*120*/ 0x92,0x54,0x38,0x10,0x10,0x10,0x10,0x00, /*121*/ 0xe0,0xd0,0xa8,0x94,0x9a,0x96,0x92,0x00, /*122*/ 0x10,0x28,0x44,0x28,0x10,0x28,0x44,0x00, /*123*/ 0xe7,0xc3,0x24,0xe7,0xe7,0xe7,0xe7,0xff, /*124*/ 0xe7,0xe7,0xe7,0xe7,0x24,0xc3,0xe7,0xff, /*125*/ 0xe7,0xc3,0x24,0xe7,0x24,0xc3,0xe7,0xff, /*126*/ 0xc3,0x99,0xf9,0xf3,0xe7,0xff,0xe7,0xff}; // glyph buffer static struct { byte dx; byte w, h; char xof, yof; byte bitmap[16]; } myglyph = {8,8,8,0,0}; static void nodestroy(SFONT *s){} static int myheight(SFONT *s){ return 8;} static int myascent(SFONT *s){ return 6;} static int mydescent(SFONT *s){ return 2;} static int myminchar(SFONT *s){ return 32;} static int mymaxchar(SFONT *s){ return 126;} static int myhasglyph(SFONT *s, word c, int allowdef) { if (c >= 32 && c <= 126) return 1; if (allowdef) return 1;} static SF_glyph * mygetglyph(SFONT *s, word c, int allowdef) { byte *src; if (c < 32 || c > 126) { if (!allowdef) return NULL; c = 32; } src = Zfont3+8*(c-32); memcpy(&(myglyph.bitmap[0]),src,8); myglyph.h = 8; return (SF_glyph *)&myglyph; } static SFONT myfont3 = { 0, nodestroy, myheight, myascent, mydescent, myminchar, mymaxchar, myhasglyph, mygetglyph, 0, NULL}; SFONT * SF_font3 = &myfont3; static int myheight2(SFONT *s){ return 16;} static int myascent2(SFONT *s){ return 12;} static int mydescent2(SFONT *s){ return 4;} static SF_glyph * mygetglyph2(SFONT *s, word c, int allowdef) { byte *src, *dst; int i; if (c < 32 || c > 126) { if (!allowdef) return NULL; c = 32; } src = Zfont3+8*(c-32); dst = &(myglyph.bitmap[0]); for (i=0;i<8;i++) { dst[0] = dst[1] = src[i]; dst += 2;} myglyph.h = 16; return (SF_glyph *)&myglyph; } static SFONT myfont3dbl = { 0, nodestroy, myheight2, myascent2, mydescent2, myminchar, mymaxchar, myhasglyph, mygetglyph2, 0, NULL}; SFONT * SF_font3double = &myfont3dbl; frotz-2.44/src/sdl/sf_resample.c0000644000175000017500000000363312527045477013567 00000000000000#include #include #include #include #include #include "sf_frotz.h" #include "samplerate.h" static int myconv( CONV *conv, FILE *f, void *dest, int nin, int eod) { int nbrd, i, j, v=0; float *sdata; unsigned char c1, c2; char c; SRC_DATA src; sdata = src.data_in = conv->inbuf; src.data_out = conv->outbuf; src.input_frames = nin; src.output_frames = conv->maxout; src.src_ratio = conv->ratio; src.end_of_input = eod; // load input data nbrd = conv->bytespersam; switch (conv->bytespersam) { case 1: for (i=0;ichannels;i++) { c = fgetc(f); *sdata++ = (((short)c) << 8)/32768.0; } break; case 2: for (i=0;ichannels;i++) { c1 = fgetc(f); c2 = fgetc(f); *sdata++ = ((short)(((unsigned short)c1) << 8 + (unsigned short)c2)) / 32768.0; } } // do conversion src_process ((SRC_STATE *)conv->aux, &src); // save output nbrd = src.output_frames_gen; src_float_to_short_array (conv->outbuf, (short *)dest, nbrd*conv->channels) ; return nbrd; } static void finish( CONV *conv) { if (conv->inbuf) free(conv->inbuf); if (conv->outbuf) free(conv->outbuf); if (conv->aux) src_delete(conv->aux); conv->inbuf = conv->outbuf = NULL; conv->aux = NULL; } extern int (*sfx_exinitconv)( CONV *conv); static int my_exinitconv( CONV *conv) { int err; if (!conv) return 0; conv->inbuf = malloc(conv->maxin*conv->channels*sizeof(float)); if (!conv->inbuf) return 0; conv->outbuf = malloc(conv->maxout*conv->channels*sizeof(float)); if (!conv->outbuf) { free(conv->inbuf); return 0;} conv->aux = src_new(SRC_SINC_FASTEST,conv->channels,&err); if (!conv->aux){ finish(conv); return 0;} conv->finishCONV = finish; conv->doCONV = myconv; return 1; } static void MyInitFunc(void) __attribute__ ((constructor)); static void MyInitFunc(void) { sfx_exinitconv = my_exinitconv; } frotz-2.44/src/sdl/sf_sound.c0000644000175000017500000002013112527045477013077 00000000000000#include #include #include #include #include #include "sf_frotz.h" #include "../blorb/blorblow.h" #include #include #include /////////////// AUDIO!!! enum {SFX_TYPE, MOD_TYPE}; typedef struct EFFECT { void (*destroy)(struct EFFECT *); int number; int type; int active; int voice; Mix_Music * mod; Mix_Chunk * sam; int repeats; int volume; int ended; zword eos; ulong endtime; } EFFECT; // no effects cache static EFFECT *e_sfx = NULL; static EFFECT *e_mod = NULL; int SFaudiorunning = 0; volatile int end_of_sound_flag = 0; int m_no_sound = 0; int m_frequency = 44100; static int audio_rate, audio_channels; // set this to any of 512,1024,2048,4096 // the higher it is, the more FPS shown and CPU needed static int audio_buffers=512; static Uint16 audio_format; static int volume = SDL_MIX_MAXVOLUME; static int bits; static void finishaudio() { if (!SFaudiorunning) return; os_stop_sample(0); SFaudiorunning = 0; // flush cache //printf("Flushing sound cache\n"); if (e_sfx) e_sfx->destroy(e_sfx); if (e_mod) e_mod->destroy(e_mod); e_sfx = e_mod = NULL; Mix_CloseAudio(); } static void music_finished(void); static void channel_finished(int channel); int sf_initsound() { if (SFaudiorunning) return 1; if (m_no_sound) return 0; SFaudiorunning = 1; // initialize sdl mixer, open up the audio device if (Mix_OpenAudio(m_frequency,MIX_DEFAULT_FORMAT,2,audio_buffers) < 0) { SFaudiorunning = 0; return 0; } Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels); bits = audio_format & 0xFF; // hook for end-of-music Mix_HookMusicFinished(music_finished); // hook for end-of-sfx Mix_ChannelFinished(channel_finished); Mix_AllocateChannels(1); CLEANREG(finishaudio); // sf_initresample(); return 1; } static void baredestroy(EFFECT * r) { if (r) { if (r->mod) Mix_FreeMusic(r->mod); if (r->sam) Mix_FreeChunk(r->sam); free(r); } } static EFFECT * new_effect( int type, int num) { EFFECT * reader = (EFFECT *)calloc(1,sizeof(EFFECT)); if (reader) { reader->type = type; reader->number = num; reader->destroy = baredestroy; } return (EFFECT *)reader; } // according to specs, this is only called when music ends "naturally", // which I take for "not halted programmatically" static void music_finished(void) { if (!e_mod) return; if (!e_mod->active) return; e_mod->active = 0; e_mod->ended = 1; } // this may be called also via a Mix_Haltetc. static void channel_finished(int channel) { if (channel != 0) return; if (!e_sfx) return; if (!e_sfx->active) return; e_sfx->active = 0; e_sfx->ended = 1; // stopsample will take care of this... } static void stopsample() { if (!e_sfx) return; e_sfx->active = 0; Mix_HaltChannel(0); e_sfx->ended = 0; } static void stopmodule() { if (!e_mod) return; e_mod->active = 0; // Player_Mute(MUTE_INCLUSIVE,0,64); // Player_SetVolume(0); // bug?? Mix_HaltMusic(); e_mod->ended = 0; } static void startsample() { if (!e_sfx) return; Mix_PlayChannel(0,e_sfx->sam,e_sfx->repeats); Mix_Volume(0,e_sfx->volume); // Voice_SetPanning(v,PAN_CENTER); // while (Voice_Stopped(v)){ // MikMod_Update(); sf_sleep(10); // } // e_sfx->voice = v; e_sfx->active = 1; } //static int samplefinished(){ // if (!Voice_Stopped(e_sfx->voice)) return 0; // return (sf_ticks() >= e_sfx->endtime); // } static void startmodule() { if (!e_mod) return; Mix_PlayMusic(e_mod->mod,e_mod->repeats); Mix_VolumeMusic(e_mod->volume); e_mod->active = 1; } static EFFECT *getaiff( FILE *f, size_t pos, int len, int num) { EFFECT *res; void * data; int size; res = new_effect(SFX_TYPE,num); if (!res) return res; if (sf_aiffwav(f,pos,&data,&size)==0) { res->sam = Mix_LoadWAV_RW(SDL_RWFromMem( data, size),0); if (data) free(data); } if (!res->sam) { //printf("Sample_LoadGeneric failure: %s\n",MikMod_strerror(MikMod_errno)); res->destroy(res); return NULL; } //printf("AIFF loaded\n"); return res; } static EFFECT *getmodule( FILE *f, size_t pos, int len, int num) { EFFECT *res; byte h[2]; res = new_effect(MOD_TYPE,num); if (!res) return NULL; fseek(f,pos,SEEK_SET); fread(h,1,2,f); fseek(f,pos,SEEK_SET); if (h[0] == 'P' && h[1] == 'K') // zipped module { int size; void *data; int st = sf_pkread(f,pos,&data,&size); if (st) { //printf("Error %d from sf_pkread()\n",st); res->destroy(res); return NULL; } res->mod = Mix_LoadMUS_RW(SDL_RWFromMem( data, size)); free(data); } else { FILE *f2 = fdopen(dup(fileno(f)),"rb"); fseek(f2,pos,SEEK_SET); res->mod = Mix_LoadMUS_RW(SDL_RWFromFP(f2,1)); } if (!res->mod) { //printf("Player_Load failure: %s\n",MikMod_strerror(MikMod_errno)); res->destroy(res); return NULL; } //printf("Module loaded\n"); return res; } static EFFECT *geteffect( num) { myresource res; EFFECT *result = NULL; unsigned int id; if ((e_sfx) && (e_sfx->number == num)) return e_sfx; if ((e_mod) && (e_mod->number == num)) return e_mod; if (sf_getresource(num,0,bb_method_FilePos,&res) != bb_err_None) return NULL; // Look for a recognized format id = res.type; if (id == bb_make_id('F','O','R','M')) { result = getaiff( res.file, res.bbres.data.startpos, res.bbres.length, num); } else if (id == bb_make_id('M','O','D',' ') || id == bb_make_id('O','G','G','V')) { result = getmodule( res.file, res.bbres.data.startpos, res.bbres.length, num); } sf_freeresource(&res); return result; } // sound handling /* * os_beep * * Play a beep sound. Ideally, the sound should be high- (number == 1) * or low-pitched (number == 2). * */ void os_beep(int number) { if (m_no_sound) return; printf("\a"); fflush(stdout); if (!SFaudiorunning) return; /* theWnd->FlushDisplay(); ::MessageBeep(MB_ICONEXCLAMATION);*/ } /* * os_finish_with_sample * * Remove the current sample from memory (if any). * */ void os_finish_with_sample(int number) { if (!SFaudiorunning) return; os_stop_sample(number); // FrotzSound::Stop(number); } /* * os_prepare_sample * * Load the given sample from the disk. * */ void os_prepare_sample(int number) { if (!SFaudiorunning) return; } /* * os_start_sample * * Play the given sample at the given volume (ranging from 1 to 8 and * 255 meaning a default volume). The sound is played once or several * times in the background (255 meaning forever). The end_of_sound * function is called as soon as the sound finishes, passing in the * eos argument. * */ void os_start_sample(int number, int volume, int repeats, zword eos) { EFFECT *e; if (!SFaudiorunning) return; //printf("start %d vol %d rep %d\n",number,volume,repeats); // NOTE: geteffect may return an already loaded effect e = geteffect(number); if (!e) return; if (e->type == SFX_TYPE) stopsample(); else stopmodule(); if (repeats < 1) repeats = 1; if (repeats == 255) repeats = -1; if (volume < 0) volume = 0; if (volume > 8) volume = 8; if (e->type == SFX_TYPE && repeats > 0) repeats--; e->repeats = repeats; e->volume = 32*volume; e->eos = eos; e->ended = 0; if (e->type == SFX_TYPE) { if ((e_sfx) && (e_sfx != e)) e_sfx->destroy(e_sfx); e_sfx = e; startsample(); } else { if ((e_mod) && (e_mod != e)) e_mod->destroy(e_mod); e_mod = e; startmodule(); } } /* * os_stop_sample * * Turn off the current sample. * */ void os_stop_sample(int number) { if (!SFaudiorunning) return; if (number == 0) { stopsample(); stopmodule(); return; } if ((e_sfx) && (e_sfx->number == number)) stopsample(); if ((e_mod) && (e_mod->number == number)) stopmodule(); // FrotzSound::Stop(number); } void sf_checksound() { if ((e_sfx) && (e_sfx->ended)) { e_sfx->ended = 0; if (e_sfx->eos) { end_of_sound_flag = 1; end_of_sound(e_sfx->eos); } } if ((e_mod) && (e_mod->ended)) { e_mod->ended = 0; if (e_mod->eos) { end_of_sound_flag = 1; end_of_sound(e_mod->eos); } } } void os_tick() { sf_checksound(); if (SFticked) { SFticked = false; sf_flushdisplay(); } } /////////////////////////////////////// frotz-2.44/src/sdl/sf_resource.c0000644000175000017500000005665412527045477013621 00000000000000#include #include #include // for access() #include #include #include "sf_frotz.h" #include "../blorb/blorb.h" #include "../blorb/blorblow.h" zword hx_flags; zword hx_fore_colour; zword hx_back_colour; // various data bool m_tandy = 0; bool m_quetzal = 1; // CRect m_wndSize; // CString m_propFontName; // CString m_fixedFontName; // int m_fontSize; int m_v6scale; int m_gfxScale = 1; ulong m_defaultFore; ulong m_defaultBack; ulong m_colours[11]; ulong m_nonStdColours[NON_STD_COLS]; int m_nonStdIndex; bool m_exitPause = 0; bool m_lineInput = 0; bool m_IsInfocomV6 = false; // bool m_fastScroll; bool m_morePrompts = 1; // int m_leftMargin; // int m_rightMargin; // FILE* m_blorbFile; // bb_map_t* m_blorbMap; // GameInfo m_gameInfo; bool m_localfiles = false; char * m_fontdir = NULL; bool m_aafonts = 0; char m_names_format = 0; char * m_reslist_file = NULL; char * m_setupfile = ".sfrotzrc"; extern int m_frequency; static int countedpics = 0; static int maxlegalpic = 0; static int releaseno = 0; static char *ResDir = "./"; static char *ResPict = "PIC%d"; static char *ResSnd = "SND%d"; int AcWidth=640, AcHeight=400; int option_scrollback_buffer = 0; static FILE *bfile = NULL; static FILE *zfile = NULL; static bb_map_t *bmap = NULL; static long zoffset; static long zsize; static int zcodeinblorb = 0; static void checkwidths() { bb_resolution_t *reso; reso = bb_get_resolution(bmap); if (reso) { //printf("get_resolution: %dx%d\n",reso->px,reso->py); // ignore small resolution hints if ((reso->px) && (reso->px >= AcWidth)) AcWidth = reso->px; if ((reso->py) && (reso->py >= AcHeight)) AcHeight = reso->py; } } static int tryloadblorb( char *bfn) { bb_err_t err; bfile = fopen(bfn,"rb"); if (!bfile) return -1; err = bb_create_map(bfile,&bmap); if (err){ bmap = NULL; fclose(bfile); bfile = NULL; return err; } return 0; } static void sf_cleanup_resources() { if (zfile) fclose(zfile); zfile = NULL; if (bmap) bb_destroy_map(bmap); bmap = NULL; if (bfile) fclose(bfile); bfile = NULL; } static int checkfile( char *fn, char *ofn) { char *p, *bp, lastch; if (access(fn,F_OK)==0){ strcpy(ofn,fn); return 0; } // if qualified path, don't do anything else if ( (strchr(fn,'\\')) || (strchr(fn,'/')) ) return -1; if ((p = getenv ("ZCODE_PATH")) == NULL) p = getenv ("INFOCOM_PATH"); if (p != NULL) { while (*p) { bp = ofn; while (*p && *p != OS_PATHSEP) lastch = *bp++ = *p++; if (lastch != '\\' && lastch != '/') *bp++ = '\\'; strcpy (bp, fn); if (access(ofn,F_OK)==0) return 0; if (*p) p++; } } return -1; } // must be called as soon as possible (i.e. by os_process_arguments()) static int load_resources( char *givenfn) { char buf[FILENAME_MAX + 1], *p; int st; FILE *f; unsigned char hd2[2]; CLEANREG(sf_cleanup_resources); // first check whether file exists st = checkfile(givenfn, buf); if (st) os_fatal( "File not found"); // check whether it is a story file f = fopen( buf, "rb"); if (!f) os_fatal( "File open failed"); fread(hd2,1,2,f); // blorb file ? if (hd2[0] == 'F' && hd2[1] == 'O'){ bb_result_t result; fclose(f); if (tryloadblorb(buf)) os_fatal("File is neither Zcode nor Blorb"); // Look for an executable chunk if (bb_load_resource(bmap,bb_method_FilePos,&result,bb_ID_Exec,0) == bb_err_None) { unsigned int id = bmap->chunks[result.chunknum].type; if (id == bb_make_id('Z','C','O','D')) { // If this is a Z-code game, set the file pointer and return zoffset = result.data.startpos; zsize = result.length; zcodeinblorb = 1; return 0; } else if (id == bb_make_id('G','L','U','L')) { os_fatal(sf_msgstring(IDS_BLORB_GLULX)); } } // we are given a blorb file with no executable chunck // Tell the user that there was no game in the Blorb file os_fatal(sf_msgstring(IDS_BLORB_NOEXEC)); } // check if possibly Zcode if (hd2[0] < 1 || hd2[0] > 8) os_fatal("Not a Zcode file (or wrong version)"); // OK, assume a bona fide Z code file zfile = f; fseek(f,0,SEEK_END); zsize = ftell(f); zoffset = 0; // try loading a corresponding blorb, but without complaining... p = strrchr(buf,'.'); if (p){ strcpy(p,".blb"); tryloadblorb(buf); } return 0; } static void load_local_resources(); // must be called as soon as possible (i.e. by os_process_arguments()) int sf_load_resources( char *givenfn) { int st; st = load_resources(givenfn); if (st) return st; if (bmap) { checkwidths(); bb_count_resources(bmap,bb_ID_Pict,&countedpics,NULL,&maxlegalpic); releaseno = bb_get_release_num(bmap); } if ((m_reslist_file)) load_local_resources(); return 0; } // this routine is only used for the Z code, so we can safely // ignore its args // NOTE that there is an extra argument (as in WindowsFrotz version) FILE *os_path_open (const char *name, const char *mode, long *size) { FILE *f = NULL; *size = zsize; if (zcodeinblorb) f = bfile; else f = zfile; if (f) fseek(f,zoffset,SEEK_SET); return f; } /* * os_picture_data * * Return true if the given picture is available. If so, store the * picture width and height in the appropriate variables. Picture * number 0 is a special case: Write the highest legal picture number * and the picture file release number into the height and width * variables respectively when this picture number is asked for. * */ int os_picture_data(int picture, int *height, int *width) { if (maxlegalpic) { if (picture == 0) { *height = maxlegalpic; *width = releaseno; return 1; } else { sf_picture *res = sf_getpic(picture); if (res) { *height = m_gfxScale*res->height; *width = m_gfxScale*res->width; return 1; } } } *height = 0; *width = 0; return 0; } /* * os_menu * * Add to or remove a menu item. Action can be: * MENU_NEW - Add a new menu with the given title * MENU_ADD - Add a new menu item with the given text * MENU_REMOVE - Remove the menu at the given index * */ void os_menu(int action, int menu, const zword * text) { /* switch (action) { case MENU_NEW: theWnd->AddNewMenu(menu,text); break; case MENU_ADD: theWnd->AddMenuItem(menu,text); break; case MENU_REMOVE: theWnd->RemoveMenu(menu); break; }*/ } /* * os_random_seed * * Return an appropriate random seed value in the range from 0 to * 32767, possibly by using the current system time. * */ // this is a provisional workaround (time granularity is at best 1s) #include int os_random_seed(void) { // return ::GetTickCount() & 32767; if (m_random_seed == -1) { return ((int)(time(NULL))) & 32767; } return m_random_seed; } // The following assumes Unicode /* * os_scrollback_char * * Write a character to the scrollback buffer. * */ void os_scrollback_char(zword c) { // theApp.ScrollbackChar(c); if (option_scrollback_buffer == 0) return; if (c == 13) c = 10; if (option_scrollback_buffer == 1) // latin-1 { if (c > 255) c = '?'; putchar(c); } else { // UTF8 if (c < 0x80) putchar(c); else { putchar(0xc0+(c>>6)); putchar(0x80+(c & 0x3f)); } } } /* * os_scrollback_erase * * Remove characters from the scrollback buffer. * */ void os_scrollback_erase (int erase) { if (option_scrollback_buffer) while (erase--) putchar(8); // theApp.ScrollbackRemove(erase); } /* * os_restart_game * * This routine allows the interface to interfere with the process of * restarting a game at various stages: * * RESTART_BEGIN - restart has just begun * RESTART_WPROP_SET - window properties have been initialised * RESTART_END - restart is complete * */ void os_restart_game(int stage) { // Show Beyond Zork's title screen if ((stage == RESTART_BEGIN) && (story_id == BEYOND_ZORK)) { int w,h; if (os_picture_data(1,&h,&w)) { sf_fillrect(0,0,0,10000,10000); os_draw_picture(1,1,1); os_read_key(0,0); } } } #define DEFAULT_GAMMA 2.2 double m_gamma = DEFAULT_GAMMA; void sf_initcolours() { int i; sf_setgamma(m_gamma); // Standard Z-Machine colours m_colours[0] = RGB5ToTrue(0x0000); // black m_colours[1] = RGB5ToTrue(0x001D); // red m_colours[2] = RGB5ToTrue(0x0340); // green m_colours[3] = RGB5ToTrue(0x03BD); // yellow m_colours[4] = RGB5ToTrue(0x59A0); // blue m_colours[5] = RGB5ToTrue(0x7C1F); // magenta m_colours[6] = RGB5ToTrue(0x77A0); // cyan m_colours[7] = RGB5ToTrue(0x7FFF); // white m_colours[8] = RGB5ToTrue(0x5AD6); // light grey m_colours[9] = RGB5ToTrue(0x4631); // medium grey m_colours[10] = RGB5ToTrue(0x2D6B); // dark grey for (i = 0; i < NON_STD_COLS; i++) m_nonStdColours[i] = 0xFFFFFFFF; m_nonStdIndex = 0; } // Read in settings void sf_readsettings(void) { char *p; sf_InitProfile(m_setupfile); m_aafonts = sf_GetProfileInt("Fonts","antialias",0); m_fontdir = sf_GetProfileString("Fonts","fontdir",NULL); m_fontfiles[0] = sf_GetProfileString("Fonts","textroman",NULL); m_fontfiles[1] = sf_GetProfileString("Fonts","textbold",NULL); m_fontfiles[2] = sf_GetProfileString("Fonts","textitalic",NULL); m_fontfiles[3] = sf_GetProfileString("Fonts","textbolditalic",NULL); m_fontfiles[4] = sf_GetProfileString("Fonts","fixedroman",NULL); m_fontfiles[5] = sf_GetProfileString("Fonts","fixedbold",NULL); m_fontfiles[6] = sf_GetProfileString("Fonts","fixeditalic",NULL); m_fontfiles[7] = sf_GetProfileString("Fonts","fixedbolditalic",NULL); ResDir = sf_GetProfileString("Resources","Dir",ResDir); ResPict = sf_GetProfileString("Resources","Pict",ResPict); ResSnd = sf_GetProfileString("Resources","Snd",ResSnd); //printf("sf_readsettings\n"); h_interpreter_number = sf_GetProfileInt("Interpreter","Number",INTERP_AMIGA); f_setup.err_report_mode = sf_GetProfileInt("Interpreter","Error Reporting",ERR_REPORT_ONCE); f_setup.ignore_errors = sf_GetProfileInt("Interpreter","Ignore Errors",0); f_setup.expand_abbreviations = sf_GetProfileInt("Interpreter","Expand Abbreviations",0); m_tandy = sf_GetProfileInt("Interpreter","Tandy Bit",0) ? true : false; m_quetzal = sf_GetProfileInt("Interpreter","Quetzal Format",1) ? true : false; f_setup.script_cols = sf_GetProfileInt("Interpreter","Wrap Script Lines",1) ? 80 : 0; if ((p = sf_GetProfileString("Interpreter","SaveNames",NULL))) m_names_format = p[0]; AcWidth = sf_GetProfileInt("Window","AcWidth",AcWidth); AcHeight = sf_GetProfileInt("Window","AcHeight",AcHeight); m_frequency = sf_GetProfileInt("Audio","Frequency",m_frequency); // m_filename = sf_GetProfileString("Files","Initial Game",""); // m_register = sf_GetProfileInt("Files","Register File Types",0) ? true : false; /* m_wndSize.left = sf_GetProfileInt("Window","Left",0); m_wndSize.top = sf_GetProfileInt("Window","Top",0); m_wndSize.right = sf_GetProfileInt("Window","Right",0); m_wndSize.bottom = sf_GetProfileInt("Window","Bottom",0); m_wndState = sf_GetProfileInt("Window","State",SW_SHOWNORMAL); m_toolBar = sf_GetProfileInt("Window","Toolbar",1) ? true : false; m_statusBar = sf_GetProfileInt("Window","Status Bar",1) ? true : false; m_notifyFull = sf_GetProfileInt("Window","Notify Full Screen",1) ? true : false; m_propFontName = sf_GetProfileString("Display","Proportional Font Name", GetDefaultFont()); m_fixedFontName = sf_GetProfileString("Display","Fixed Font Name", "Courier New"); m_fontSize = sf_GetProfileInt("Display","Font Size",10);*/ m_v6scale = sf_GetProfileInt("Display","Infocom V6 Scaling",2); m_gfxScale = 1; m_defaultFore = ( sf_GetProfileInt("Display","Foreground",0xffffff)); m_defaultBack = ( sf_GetProfileInt("Display","Background",0x800000)); //printf("mdff%d mdfb%d\n",m_defaultFore,m_defaultBack); // m_fastScroll = sf_GetProfileInt("Display","Fast Scrolling",0) ? true : false; m_morePrompts = sf_GetProfileInt("Display","Show More Prompts",1) ? true : false; // m_leftMargin = sf_GetProfileInt("Display","Left Margin",0); // m_rightMargin = sf_GetProfileInt("Display","Right Margin",0); m_gamma = sf_GetProfileDouble("Display","Gamma",DEFAULT_GAMMA); sf_initcolours(); sf_FinishProfile(); } // Get a colour ulong sf_GetColour(int colour) { // Standard colours if ((colour >= BLACK_COLOUR) && (colour <= DARKGREY_COLOUR)) return m_colours[colour-BLACK_COLOUR]; // Default colours if (colour == 16) return m_defaultFore; if (colour == 17) return m_defaultBack; // Non standard colours if ((colour >= 18) && (colour < 256)) { if (m_nonStdColours[colour-18] != 0xFFFFFFFF) return m_nonStdColours[colour-18]; } return m_colours[0]; } // Get a default colour ulong sf_GetDefaultColour(bool fore) { if (m_IsInfocomV6) return sf_GetColour(fore ? WHITE_COLOUR : BLACK_COLOUR); return fore ? m_defaultFore : m_defaultBack; } // Get an index for a non-standard colour int sf_GetColourIndex( ulong colour) { int i, index = -1; // Is this a standard colour? for (i = 0; i < 11; i++) { if (m_colours[i] == colour) return i+BLACK_COLOUR; } // Is this a default colour? if (m_defaultFore == colour) return 16; if (m_defaultBack == colour) return 17; // Is this colour already in the table? for (i = 0; i < NON_STD_COLS; i++) { if (m_nonStdColours[i] == colour) return i+18; } // Find a free colour index while (index == -1) { if (colour_in_use(m_nonStdIndex+18) == 0) { m_nonStdColours[m_nonStdIndex] = colour; index = m_nonStdIndex+18; } m_nonStdIndex++; if (m_nonStdIndex >= NON_STD_COLS) m_nonStdIndex = 0; } return index; } /* * os_set_colour * * Set the foreground and background colours which can be: * * 1 * BLACK_COLOUR * RED_COLOUR * GREEN_COLOUR * YELLOW_COLOUR * BLUE_COLOUR * MAGENTA_COLOUR * CYAN_COLOUR * WHITE_COLOUR * TRANSPARENT_COLOUR * * Amiga only: * * LIGHTGREY_COLOUR * MEDIUMGREY_COLOUR * DARKGREY_COLOUR * * There may be more colours in the range from 16 to 255; see the * remarks about os_peek_colour. * */ void os_set_colour(int new_foreground, int new_background) { SF_textsetting *ts = sf_curtextsetting(); sf_flushtext(); // theWnd->ResetOverhang(); //printf("os_set_colour %d %d\n",new_foreground,new_background); if (new_foreground == 1) ts->fore = sf_GetDefaultColour(true); else if (new_foreground < 256) ts->fore = sf_GetColour(new_foreground); ts->foreDefault = (new_foreground == 1); if (new_background == 1) ts->back = sf_GetDefaultColour(false); else if (new_background < 256) ts->back = sf_GetColour(new_background); ts->backDefault = (new_background == 1); ts->backTransparent = (new_background == 15); //printf("os_set_colour %d %d %x %x\n",new_foreground,new_background,ts->fore,ts->back); // theWnd->ApplyTextSettings(); } /* * os_from_true_cursor * * Given a true colour, return an appropriate colour index. * */ int os_from_true_colour(zword colour) { return sf_GetColourIndex(RGB5ToTrue(colour)); } /* * os_to_true_cursor * * Given a colour index, return the appropriate true colour. * */ zword os_to_true_colour(int index) { return TrueToRGB5(sf_GetColour(index)); } /* * os_init_screen * * Initialise the IO interface. Prepare screen and other devices * (mouse, sound card). Set various OS depending story file header * entries: * * h_config (aka flags 1) * h_flags (aka flags 2) * h_screen_cols (aka screen width in characters) * h_screen_rows (aka screen height in lines) * h_screen_width * h_screen_height * h_font_height (defaults to 1) * h_font_width (defaults to 1) * h_default_foreground * h_default_background * h_interpreter_number * h_interpreter_version * h_user_name (optional; not used by any game) * * Finally, set reserve_mem to the amount of memory (in bytes) that * should not be used for multiple undo and reserved for later use. * */ void os_init_screen(void) { sf_initvideo(AcWidth,AcHeight,(m_fullscreen != -1)); // Set the graphics scaling if (sf_IsInfocomV6() || (story_id == BEYOND_ZORK)) m_gfxScale = m_v6scale; else m_gfxScale = 1; // Set the configuration if (h_version == V3) { h_config |= CONFIG_SPLITSCREEN; h_config |= CONFIG_PROPORTIONAL; if (m_tandy) h_config |= CONFIG_TANDY; else h_config &= ~CONFIG_TANDY; } if (h_version >= V4) { h_config |= CONFIG_BOLDFACE; h_config |= CONFIG_EMPHASIS; h_config |= CONFIG_FIXED; h_config |= CONFIG_TIMEDINPUT; } if (h_version >= V5) h_config |= CONFIG_COLOUR; if (h_version == V6) { if (bmap) { h_config |= CONFIG_PICTURES; h_config |= CONFIG_SOUND; } } h_interpreter_version = 'F'; if (h_version == V6) { h_default_foreground = sf_GetColourIndex(sf_GetDefaultColour(true)); h_default_background = sf_GetColourIndex(sf_GetDefaultColour(false)); } else { h_default_foreground = 1; h_default_background = 1; } os_set_font(FIXED_WIDTH_FONT); os_set_text_style(0); /* theWnd->ApplyTextSettings( FrotzWnd::TextSettings(0,FIXED_WIDTH_FONT));*/ { int H, W; os_font_data( FIXED_WIDTH_FONT, &H, &W); h_font_width = (zbyte)W; h_font_height = (zbyte)H; } h_screen_width = (zword)AcWidth; h_screen_height = (zword)AcHeight; h_screen_cols = (zbyte)(h_screen_width / h_font_width); h_screen_rows = (zbyte)(h_screen_height / h_font_height); // Check for sound if ((h_version == V3) && (h_flags & OLD_SOUND_FLAG)) { if (((bmap==NULL) && (m_localfiles==0)) || (!sf_initsound())) h_flags &= ~OLD_SOUND_FLAG; } else if ((h_version >= V4) && (h_flags & SOUND_FLAG)) { if (((bmap==NULL) && (m_localfiles==0)) || (!sf_initsound())) h_flags &= ~SOUND_FLAG; } if (h_version >= V5) { zword mask = 0; if (h_version == V6) mask |= TRANSPARENT_FLAG; // Mask out any unsupported bits in the extended flags hx_flags &= mask; hx_fore_colour = TrueToRGB5(sf_GetDefaultColour(true)); hx_back_colour = TrueToRGB5(sf_GetDefaultColour(false)); } } /* * os_fatal * * Display error message and stop interpreter. * */ void os_fatal(const char *s, ...) { va_list m; // if (theWnd != NULL) // theWnd->FlushDisplay(); sf_cleanup_all(); fprintf(stderr,"\n%s: ",sf_msgstring(IDS_FATAL)); va_start( m, s); vfprintf( stderr, s, m); va_end(m); fprintf(stderr,"\n\n"); exit(EXIT_FAILURE); // ::MessageBox(AfxGetMainWnd()->GetSafeHwnd(),s,CResString(IDS_FATAL),MB_ICONERROR|MB_OK); // throw FrotzApp::AbortFrotz(); } // If true, running one of Infocom's V6 games bool sf_IsInfocomV6() { switch (story_id) { case ARTHUR: case JOURNEY: case SHOGUN: case ZORK_ZERO: return true; default: break; } return false; } #define LOCAL_MEM -1 #define LOCAL_FILE -2 void sf_freeresource( myresource *res) { int cnu; if (!res) return; cnu = res->bbres.chunknum; if (cnu == LOCAL_MEM) { if (res->bbres.data.ptr) free(res->bbres.data.ptr); return; } if (cnu == LOCAL_FILE) { if (res->file) fclose(res->file); return; } if ((bmap) && (cnu >= 0)) bb_unload_chunk(bmap,cnu); } static FILE * findlocal( int ispic, int num, int *size) { FILE *f; char *tpl, buf[MAX_FILE_NAME+1]; tpl = ispic ? ResPict : ResSnd; strcpy(buf,ResDir); sprintf(buf+strlen(buf),tpl,num); f = fopen(buf,"rb"); if (!f) return f; fseek(f,0,SEEK_END); *size = ftell(f); fseek(f,0,SEEK_SET); return f; } static FILE * findfromlist( int ispic, int num, int *size); static int loadlocal( int num, int ispic, int method, myresource * res) { FILE *f; int size; byte hd[4]; f = findlocal( ispic, num, &size); if (!f) f = findfromlist( ispic, num, &size); if (!f) return bb_err_NotFound; fread(hd,1,4,f); fseek(f,0,SEEK_SET); res->type = 0; if (ispic) { if (hd[0] == 0xff && hd[1] == 0xd8) res->type = bb_make_id('J','P','E','G'); else if (hd[0] == 0x89 && hd[1] == 0x50) res->type = bb_make_id('P','N','G',' '); } else { if (memcmp(hd,"FORM",4) == 0) res->type = bb_make_id('F','O','R','M'); else if (memcmp(hd,"OggS",4) == 0) res->type = bb_make_id('O','G','G','V'); else res->type = bb_make_id('M','O','D',' '); } if (!res->type) { fclose(f); return bb_err_NotFound;} res->bbres.data.startpos = 0; res->file = f; res->bbres.length = size; if (method == bb_method_FilePos) res->bbres.chunknum = LOCAL_FILE; else { void *ptr; res->bbres.chunknum = LOCAL_MEM; ptr = res->bbres.data.ptr = malloc(size); if (ptr) fread(ptr,1,size,f); fclose(f); if (!ptr) return bb_err_NotFound; } return bb_err_None; } int sf_getresource( int num, int ispic, int method, myresource * res) { int st; ulong usage; res->bbres.data.ptr = NULL; res->file = NULL; if (m_localfiles) if ((st = loadlocal(num,ispic,method,res)) == bb_err_None) return st; if (!bmap) return bb_err_NotFound; if (ispic) usage = bb_ID_Pict; else usage = bb_ID_Snd; st = bb_load_resource(bmap,method,(bb_result_t *)res,usage,num); if (st == bb_err_None) { res->type = bmap->chunks[res->bbres.chunknum].type; if (method == bb_method_FilePos) res->file = bfile; } return st; } ///////////////// typedef struct { void *next; int num, ispic; ulong type; char *name; } LLENTRY; static LLENTRY *Lpics=NULL, *Lsnds = NULL; static int numlocal = 0, numlocalpic = 0, numlocalsnd = 0; static int p_ispic, p_num; static ulong p_type; static char *p_name; static void cleanLLENTRY( LLENTRY *e) { while (e){ LLENTRY *n = e->next; if (e->name) free(e->name); free(e); e = n;} } static void cleanlocallist() { cleanLLENTRY(Lpics); Lpics = NULL; cleanLLENTRY(Lsnds); Lsnds = NULL; } static int parseline( char *s) { char *p, p3; int n; p = strtok(s," \t\n"); if (!p) return 0; if (strcmp(p,"Pict")==0) p_ispic = 1; else if (strcmp(p,"Snd")==0) p_ispic = 0; else return -1; p = strtok(NULL," \t\n"); if (!p) return -1; p_num = atoi(p); p = strtok(NULL," \t\n"); if (!p) return -1; n = strlen(p); if (n < 3) return -1; if (p[3]) p3 = p[3]; else p3 = ' '; p_type = bb_make_id(p[0],p[1],p[2],p3); p = strtok(NULL," \t\n"); if (!p) return -1; p_name = p; return 1; } static void load_local_resources() { FILE *f; LLENTRY *e; char s[256]; int st; f = fopen(m_reslist_file,"r"); if (!f) return; CLEANREG(cleanlocallist); for (;;) { fgets(s,254,f); if (feof(f)) break; st = parseline(s); if (st < 1) continue; e = calloc(1,sizeof(LLENTRY)); if (e) { e->num = p_num; e->ispic = p_ispic; e->type = p_type; e->name = strdup(p_name); if (p_ispic) { e->next = Lpics; Lpics = e; numlocalpic++; if (p_num > maxlegalpic) maxlegalpic = p_num; } else { e->next = Lsnds; Lsnds = e; numlocalsnd++; } } } numlocal = numlocalpic + numlocalsnd; if (numlocal) m_localfiles = 1; fclose(f); } static FILE * findfromlist( int ispic, int num, int *size) { FILE *f; LLENTRY *l; char buf[MAX_FILE_NAME+1]; if (ispic) l = Lpics; else l = Lsnds; while (l) { if (l->num == num) break; l = l->next; } if (!l) return NULL; strcpy(buf,ResDir); strcat(buf,l->name); f = fopen(buf,"rb"); if (!f) return f; fseek(f,0,SEEK_END); *size = ftell(f); fseek(f,0,SEEK_SET); return f; } void os_init_setup(void) { f_setup.attribute_assignment = 0; f_setup.attribute_testing = 0; f_setup.context_lines = 0; f_setup.object_locating = 0; f_setup.object_movement = 0; f_setup.left_margin = 0; f_setup.right_margin = 0; f_setup.ignore_errors = 0; f_setup.piracy = 0; /* enable the piracy opcode */ f_setup.undo_slots = MAX_UNDO_SLOTS; f_setup.expand_abbreviations = 0; f_setup.script_cols = 80; f_setup.save_quetzal = 1; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; } frotz-2.44/src/sdl/sf_sig.c0000644000175000017500000000524412527045477012541 00000000000000#include #include #include #include "sf_frotz.h" #ifdef WIN32 static void resethandlers() { signal(SIGINT,SIG_DFL); signal(SIGFPE,SIG_DFL); signal(SIGSEGV,SIG_DFL); } static const char * signame( int sig) { switch (sig) { case SIGINT: return "[SIGINT]"; case SIGFPE: return "[SIGFPE]"; case SIGSEGV: return "[SIGSEGV]"; default: return ""; } } static void myhandler( int s) { resethandlers(); os_fatal("Signal %d received %s",s,signame(s)); } void sf_installhandlers() { // CLEANREG(resethandlers); signal(SIGINT,myhandler); signal(SIGFPE,myhandler); signal(SIGSEGV,myhandler); } #else #include /* get REG_EIP from ucontext.h */ #ifndef __USE_GNU #define __USE_GNU #include #endif // REG_EIP does not exist on 64bit CPU #if defined(__amd64__) || defined (__x86_64__) #define _PROG_COUNTER REG_RIP #else #define _PROG_COUNTER REG_EIP #endif static struct { int sig; char *name;} NAMES[] = { {SIGSEGV,"SIGSEGV"}, {SIGFPE,"SIGFPE"}, {SIGILL,"SIGILL"}, {0,NULL}}; static char *getsigname( int s){ int i = 0; while (NAMES[i].name) { if (NAMES[i].sig == s) return NAMES[i].name; i++; } return NULL; } static void bt_sighandler(int sig, siginfo_t *info, void *secret) { void *trace[16]; char **messages = (char **)NULL; char *nam; int i, trace_size = 0; ucontext_t *uc = (ucontext_t *)secret; if (sig == SIGINT) os_fatal("Emergency exit!\n\n(Signal SIGINT received)"); /* Do something useful with siginfo_t */ printf("\nInterpreter bug!\nSignal %d ", sig); if ((nam = getsigname(sig))) printf("[%s] ",nam); printf("from %p", uc->uc_mcontext.gregs[_PROG_COUNTER]); if (sig == SIGSEGV) printf(" [faulty address is %p]",info->si_addr); printf("\n"); trace_size = backtrace(trace, 16); /* overwrite sigaction with caller's address */ trace[1] = (void *) uc->uc_mcontext.gregs[_PROG_COUNTER]; /* skip first stack frame (points here) */ printf("Backtrace:\n"); // messages = backtrace_symbols(trace, trace_size); // for (i=1; i #include #include #include #include #include #include FT_FREETYPE_H #include "sf_frotz.h" ///////////////////////////////////////////////////////////////// static char * sf_searchfile( char *fn, int fnlen, char *buf, char *paths) { char *p; if (!fn) return NULL; if (!paths) paths = ""; if (fnlen < 0) fnlen = strlen(fn); if (!fnlen) return NULL; for (;;) { int plen; p = strchr(paths,OS_PATHSEP); if (p) plen = p-paths; else plen = strlen(paths); if (plen) strncpy(buf,paths,plen); buf[plen] = 0; if ((plen) && (buf[plen-1] != '\\') && (buf[plen-1] != '/')) strcat(buf,"/"); plen = strlen(buf); strncpy(buf+plen,fn,fnlen); buf[plen+fnlen] = 0; //printf("try[%s]\n",buf); if (access(buf,F_OK)==0) return buf; if (p) paths = p+1; else break; } return NULL; } ///////////////////////////////////////////////////////////////// typedef struct { SFONT sfont; int ascent, descent, height; int minchar, maxchar, totglyphs; SF_glyph *glyphs[0]; } MYFONT; // destructor static void bdestroy( SFONT *s) { if (s) { int i; MYFONT *f = (MYFONT *)s; for (i=0;itotglyphs;i++) if (f->glyphs[i]) free(f->glyphs[i]); free(s); } } static int bheight( SFONT *s) { if (s) return ((MYFONT *)s)->height; return 0; } static int bascent( SFONT *s) { if (s) return ((MYFONT *)s)->ascent; return 0; } static int bdescent( SFONT *s) { if (s) return ((MYFONT *)s)->descent; return 0; } static int bminchar( SFONT *s) { if (s) return ((MYFONT *)s)->minchar; return 0; } static int bmaxchar( SFONT *s) { if (s) return ((MYFONT *)s)->maxchar; return 0; } static SF_glyph *getglyph( SFONT *s, word c, int allowdef) { if (s) { int i; MYFONT *f = (MYFONT *)s; if (c < f->minchar || c > f->maxchar) { if (allowdef) c = 0; else return NULL; } return f->glyphs[c]; } return NULL; } static int hasglyph( SFONT *fo, word c, int allowdef) { return (getglyph(fo,c,allowdef) != NULL); } static int inited = 0, initerr = 0; static FT_Library library; static void libfinish() { if (!inited) return; FT_Done_FreeType( library ); inited = 0; } static void libinit() { if (initerr) return; if (inited) return; initerr = FT_Init_FreeType( &library ); /* initialize library */ /* error handling omitted */ if (initerr) printf("FT_Init_FreeType: error %d\n",initerr); else { inited = 1; atexit(libfinish); } } static MYFONT * makefont( int totglyphs) { MYFONT * res; res = calloc(1,sizeof(MYFONT)+totglyphs*sizeof(SF_glyph *)); if (!res) return NULL; res->sfont.destroy = bdestroy; res->sfont.height = bheight; res->sfont.ascent = bascent; res->sfont.descent = bdescent; res->sfont.minchar = bminchar; res->sfont.maxchar = bmaxchar; res->sfont.hasglyph = hasglyph; res->sfont.getglyph = getglyph; res->totglyphs = totglyphs; res->maxchar = totglyphs - 1; return res; } #define MAXUNI 0x153 static void setglyph( MYFONT *f, FT_Face face, int ch) { int err, gid = FT_Get_Char_Index( face, ch); int mode = FT_RENDER_MODE_MONO; SF_glyph *res; FT_GlyphSlot slot = face->glyph; int i,j, nbypr, pitch; unsigned char *s; FT_Bitmap *bitmap; if (m_aafonts) mode = FT_RENDER_MODE_NORMAL; err = FT_Load_Glyph( face, gid, 0); if (err) return; if (slot->format != FT_GLYPH_FORMAT_BITMAP) { err = FT_Render_Glyph(slot, mode); if (err) return; } bitmap = &slot->bitmap; nbypr = m_aafonts ? bitmap->width : (bitmap->width+7)/8; res = calloc(1,sizeof(SF_glyph) + nbypr*bitmap->rows); if (!res) return; for (i=0;irows;i++) for (j=0;jbitmap[i*nbypr+j] = bitmap->buffer[i*bitmap->pitch+j]; //printf("%c %d %p w%d\n",ch,bitmap->pitch,res,bitmap->width); //{ //int i,j; unsigned char *p = &(res->bitmap[0]); //for (i=0;irows;i++){ // for (j=0;jw = bitmap->width; res->h = bitmap->rows; res->dx = slot->advance.x/64; res->xof = slot->bitmap_left; res->yof = slot->bitmap_top - bitmap->rows; f->glyphs[ch] = res; } static SFONT * loadftype( char *fname, int size, int *err) { MYFONT *res; FT_Face face; int i; *err = 0; if (!fname) { *err = -8; return NULL;} libinit(); if (initerr) { *err = -99; return NULL;} res = makefont( MAXUNI+1); if (!res) { *err = -3; return NULL;} *err = FT_New_Face( library, fname, 0, &face ); /* create face object */ if (*err){ res->sfont.destroy(&res->sfont); return NULL; } *err = FT_Set_Pixel_Sizes( face, size, size); if (*err){ res->sfont.destroy(&res->sfont); return NULL; } res->ascent = face->size->metrics.ascender/64; res->descent = -face->size->metrics.descender/64; res->height = res->ascent+res->descent; //face->size->metrics.height/64; res->sfont.antialiased = m_aafonts; res->minchar = 32; setglyph(res,face,0); for (i=32;i<127;i++) setglyph(res,face,i); for (i=0xa0;i<256;i++) setglyph(res,face,i); setglyph(res,face,0x152); setglyph(res,face,0x153); FT_Done_Face( face ); return (SFONT *) res; } #define DEFSIZE 14 #ifdef WIN32 #define SYSFONTS "c:/windows/fonts" #else #define SYSFONTS "/usr/share/fonts/freetype" #endif SFONT * sf_loadftype( char *fspec, int *err) { char buf[FILENAME_MAX], *fn, *at, *fenv; int size = DEFSIZE, fnlen=-1; at = strchr(fspec,'@'); if (at) { fnlen = at-fspec; size = atoi(at+1); } fn = sf_searchfile( fspec, fnlen, buf, ""); if (!fn) fn = sf_searchfile( fspec, fnlen, buf, "./"); if (!fn) if (m_fontdir) fn = sf_searchfile( fspec, fnlen, buf, m_fontdir); if (!fn) fn = sf_searchfile( fspec, fnlen, buf, SYSFONTS); if (!fn) { fenv = getenv("FONTS"); if (fenv) fn = sf_searchfile( fspec, fnlen, buf, fenv); } if (!fn) return NULL; return loadftype(fn,size,err); } ////////////////////////////////////////// static void initloader() __attribute__((constructor)); static void initloader() { ttfontloader = sf_loadftype; ttfontsdone = libfinish; } frotz-2.44/src/sdl/sf_fonts.c0000644000175000017500000004402512527045477013110 00000000000000#include #include #include #include "sf_frotz.h" // font handling /* struct sfontstruct { int refcount; void (*destroy)(SFONT *); int (*height)(SFONT *); int (*ascent)(SFONT *); int (*descent)(SFONT *); int (*minchar)(SFONT *); int (*maxchar)(SFONT *); int (*hasglyph)(SFONT *,int,int); SF_glyph *(*getglyph)(SFONT *,int,int); }; */ typedef struct { int refcount; word minchar, maxchar, defchar; byte ascent, descent; int glyphs[0]; // offsets to glyphs from start of rec } SF_bdffont; char * m_fontfiles[8]; static char s[1026]; static char * starts( char *s, char *id){ int len = strlen(id); while (*s == ' ') s++; if (memcmp(s,id,len)==0) return (s+len); return NULL; } static int hexd( char c){ if (c >= '0' && c <= '9') return c-'0'; if (c >= 'A' && c <= 'F') return c-'A'+10; if (c >= 'a' && c <= 'f') return c-'a'+10; return 0; } static void gethex( char *p, byte *dst, int n){ while (n--){ *dst++ = 16*hexd(p[0])+hexd(p[1]); p += 2; } } #define ERRET(n) { *err = n; return font;} static SF_bdffont *sBDXload( FILE *f, int *err, int *size, int MAXCHAR){ int totb, i, k, wh[4]; byte *po, *pbeg; char *p, *q; char *fontname = "", *copyright = "unknown"; int fngot=0,cpgot=0; int hasenc, hasbbx; SF_bdffont *font = NULL; int foffs, minch, maxch, rejected=0; int defchar = -1, descent = -1, ascent = -1, nprop, nchars; // NOTE: file MUST be opened as binary on MSDOS, otherwise // ftell() won't return correct values if the file does not // have CR's (as is usually the case, since BDF files frequently // come from Unix systems) // header for (;;){ fgets(s,1024,f); if (feof(f)) ERRET(-1) // errorexit(99,"unexpected EOF\n"); if ((p = starts(s,"FONT "))){ while (*p == ' ') p++; q = p; while (*p >= ' ') p++; *p = 0; fontname = strdup(q); fngot = 1; } if ((p = starts(s,"STARTPROPERTIES "))){ nprop = atoi(p); break; } } for (i=0;i= ' ') p++; *p = 0; copyright = strdup(q); cpgot = 1; } if ((p = starts(s,"DEFAULT_CHAR "))) defchar = atoi(p); if ((p = starts(s,"FONT_ASCENT "))) ascent = atoi(p); if ((p = starts(s,"FONT_DESCENT "))) descent = atoi(p); } for (;;){ fgets(s,1024,f); if (feof(f)) ERRET(-61) // errorexit(99,"unexpected EOF\n"); if ((p = starts(s,"CHARS "))){ nchars = atoi(p); break; } } foffs = ftell(f); // first pass totb = 0; minch = 65536; maxch = -1; hasenc = 0; for (i=0;i maxch) maxch = k; } else rejected++; hasenc = 1; } if ((p = starts(s,"BBX "))){ if (!hasenc) ERRET(-10) sscanf(p,"%d %d",wh,wh+1); //printf("c%d %d\n",k,((wh[0]+7)/8)*wh[1] + sizeof(SF_glyph)); if (k <= MAXCHAR){ totb += ((wh[0]+7)/8)*wh[1] + sizeof(SF_glyph); } i++; hasenc = 0; } } //printf("nchars=%d minch=%d maxch=%d\n",nchars,minch,maxch); //printf("sizeof(SF_glyph)=%d\n",sizeof(SF_glyph)); // printf("<%s>\n",fontname); // printf("<%s>\n",copyright); // printf("dc%d a%d d%d %d..%d exp%d read%d totb%d\n", // defchar,ascent,descent,minch,maxch,nchars,i,totb); if (ascent < 0 || descent < 0 || nchars != i) ERRET(-2) // errorexit(99,"??? dc%d a%d d%d %d..%d exp%d read%d\n", // defchar,ascent,descent,minch,maxch,nchars,k); if (defchar < minch || defchar > maxch){ // printf("WARNING: defchar=%d, set to",defchar); if (defchar < minch) defchar = minch; if (defchar > maxch) defchar = maxch; // printf(" %d\n",defchar); } // nchars = i; totb += (maxch-minch+1)*sizeof(int)+sizeof(SF_bdffont)+strlen(fontname)+ strlen(copyright)+2; // printf("totb %d\n",totb); font = calloc(1,totb); if (!font) ERRET(-3) // errorexit(99,"malloc()\n"); //printf("allocated: %p-%p\n",font,((byte *)font)+totb); // memcpy(&(font->magic[0]),"gBDX",4); font->minchar = minch; font->maxchar = maxch; font->defchar = defchar; font->ascent = ascent; font->descent = descent; pbeg = (byte *)font; po = (byte *)(&(font->glyphs[maxch-minch+1])); k = strlen(fontname)+1; memcpy(po,fontname,k); po += k; if (fngot) free(fontname); k = strlen(copyright)+1; memcpy(po,copyright,k); po += k; if (cpgot) free(copyright); for (i=minch;i<=maxch;i++) font->glyphs[i-minch] = 0; // second pass fseek(f,foffs,0); for (k=0;kglyphs[i-minch] = po-pbeg; hasenc = 1; } if ((p = starts(s,"DWIDTH "))){ if (!hasenc) ERRET(-11) dwid = atoi(p); } if ((p = starts(s,"BBX "))){ if (!hasenc) ERRET(-12) sscanf(p,"%d %d %d %d",wh,wh+1,wh+2,wh+3); hasbbx = 1; } if (starts(s,"BITMAP")) break; } if (!hasenc || !hasbbx) ERRET(-13) if (i <= MAXCHAR) { bg->dx = dwid; bg->w = wh[0]; w = (wh[0]+7)/8; bg->h = h = wh[1]; bg->xof = wh[2]; bg->yof = wh[3]; po = (byte *)(&(bg->bitmap[0])); //printf("k=%d ch=%d delta=%d\n",k,i,po-(byte *)bg); for (j=0;jglyphs[i-minch],po); } *err = 0; *size = totb; return font; } // destructor for fonts with all dynamic data static void bdestroy( SFONT *s) { if (s) { if (s->data) free(s->data); free(s); } } static int bheight( SFONT *s) { if (s) if (s->data) { SF_bdffont *f = s->data; return ((int)(f->ascent)+(int)(f->descent)); } return 0; } static int bascent( SFONT *s) { if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->ascent; return 0; } static int bdescent( SFONT *s) { if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->descent; return 0; } static int bminchar( SFONT *s) { if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->minchar; return 0; } static int bmaxchar( SFONT *s) { if (s) if (s->data) return (int)((SF_bdffont *)(s->data))->maxchar; return 0; } static SF_glyph *getglyph( SFONT *fo, word c, int allowdef) { int m; SF_bdffont *b; if (!fo) return NULL; b = fo->data; if (!b) return NULL; if (c < b->minchar || c > b->maxchar) { if (allowdef) c = b->defchar; else return NULL; } m = b->glyphs[c-b->minchar]; if (!m) { if (allowdef) m = b->glyphs[0]; else return NULL; } return (SF_glyph *)(((byte *)b)+m); } static int hasglyph( SFONT *fo, word c, int allowdef) { return (getglyph(fo,c,allowdef) != NULL); } static SFONT * makefont( SF_bdffont *b) { SFONT * res; if (!b) return NULL; res = calloc(1,sizeof(SFONT)); if (!res) { free(b); return NULL; } res->destroy = bdestroy; res->height = bheight; res->data = b; res->ascent = bascent; res->descent = bdescent; res->minchar = bminchar; res->maxchar = bmaxchar; res->hasglyph = hasglyph; res->getglyph = getglyph; return res; } static SFONT * loadfont( char *fname, int *err, int *size){ SF_bdffont * font; SFONT *res; FILE *f; *err = 0; if (!fname) { *err = -8; return NULL;} // NOTE: file MUST be opened as binary on MSDOS, otherwise // ftell() won't return correct values if the file does not // have CR's (as is usually the case, since BDF files frequently // come from Unix systems) f = fopen(fname,"rb"); if (!f) {*err = -7; return NULL;} font = sBDXload(f,err,size,65535); fclose(f); if (*err) { if (font) free(font); return NULL; } res = makefont(font); if (!res) { free(font); *err = -9999; return NULL; } return res; } // these are the 8 fonts needed // PROPORTIONAL FONT // ROMAN // ROMAN BOLD // ITALIC // ITALIC BOLD // FIXED FONT // ROMAN // ROMAN BOLD // ITALIC // ITALIC BOLD #define NUMFONTS 9 static SFONT *myfonts[9] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; static int styleidx( int zfont, int style) { int k = 0; if (zfont == GRAPHICS_FONT) return 8; if (zfont != TEXT_FONT) zfont = FIXED_WIDTH_FONT; if (style & FIXED_WIDTH_STYLE) zfont = FIXED_WIDTH_FONT; if (h_flags & FIXED_FONT_FLAG) zfont = FIXED_WIDTH_FONT; if (zfont != TEXT_FONT) k += 4; if (style & EMPHASIS_STYLE) k += 2; if (style & BOLDFACE_STYLE) k++; return k; } static SF_textsetting current; #define MAXSTACK 16 static SF_textsetting tsstack[MAXSTACK]; static int tsstackptr = 0; // does not increase the refcount of f - user must do that static void setfontk( int k, SFONT *f) { if (myfonts[k]) { myfonts[k]->refcount--; if (myfonts[k]->refcount == 0) myfonts[k]->destroy(myfonts[k]); } myfonts[k] = f; } static void cleanfonts() { int i; for (i=0;i<8;i++) setfontk(i,NULL); /* for (i=0;i<8;i++) if (m_fontfiles[i]){ free(m_fontfiles[i]); m_fontfiles[i] = NULL;}*/ } SF_textsetting * sf_curtextsetting() { return ¤t; } void sf_pushtextsettings() { if (tsstackptr < MAXSTACK) { tsstack[tsstackptr] = current; tsstackptr++; } } void sf_poptextsettings() { if (tsstackptr) { tsstackptr--; current = tsstack[tsstackptr]; } } /* * os_check_unicode * * Return with bit 0 set if the Unicode character can be * displayed, and bit 1 if it can be input. * * */ int os_check_unicode(int font, zword c) { return ((current.font->hasglyph(current.font,c,0) != 0) ? 3 : 2); } static int charwidth( zword c, int *oh) { SF_glyph *g; int ww = 0; if (c == ZC_INDENT) return (3*charwidth(' ',oh)); if (c == ZC_GAP) return (2*charwidth(' ',oh)); *oh = 0; g = current.font->getglyph(current.font,c,1); if (g) { int ext = g->w+g->xof; if (ext > g->dx) *oh = ext-g->dx; ww = (g->dx); } return ww; } /* * os_char_width * * Return the length of the character in screen units. * */ int os_char_width(zchar c) { int w, oh; w = charwidth( c, &oh); return (w+oh); } static void setfont( int zfont){ int k = styleidx(zfont,current.style); //printf("%d.",k); current.font = myfonts[k]; // if (k < 4) zfont = TEXT_FONT; // else zfont = FIXED_WIDTH_FONT; current.zfontnum = zfont; current.proportional = (k < 4); } static void setstyle( int style){ current.style = style; setfont(current.zfontnum); } /* * os_font_data * * Return true if the given font is available. The font can be * * TEXT_FONT * PICTURE_FONT * GRAPHICS_FONT * FIXED_WIDTH_FONT * * The font size should be stored in "height" and "width". If * the given font is unavailable then these values must _not_ * be changed. * */ int os_font_data( int font, int *height, int *width) { switch (font) { case TEXT_FONT: case FIXED_WIDTH_FONT: case GRAPHICS_FONT: { sf_pushtextsettings(); setfont(font); setstyle(0); *height = current.font->height(current.font); *width = os_char_width((zword)('0')); sf_poptextsettings(); return 1; } default: break; } return 0; } /* * os_set_font * * Set the font for text output. The interpreter takes care not to * choose fonts which aren't supported by the interface. * */ void os_set_font( int new_font) { //printf("os_set_font(%d)\n",new_font); sf_flushtext(); setfont(new_font); } /* * os_set_text_style * * Set the current text style. Following flags can be set: * * REVERSE_STYLE * BOLDFACE_STYLE * EMPHASIS_STYLE (aka underline aka italics) * FIXED_WIDTH_STYLE * */ void os_set_text_style( int new_style) { sf_flushtext(); setstyle(new_style); } /* * os_string_width * * Calculate the length of a word in screen units. Apart from letters, * the word may contain special codes: * * ZC_NEW_STYLE - next character is a new text style * ZC_NEW_FONT - next character is a new font * */ int os_string_width(const zchar *s) { int width = 0, wacc = 0, oh = 0; zword c; setfont(current.zfontnum); // Look for style or font changes, or indents sf_pushtextsettings(); while ((c = *s++)) { if (c == ZC_NEW_STYLE) { wacc = width+oh; width = 0; os_set_text_style(*s++); } else if (c == ZC_NEW_FONT) { wacc = width+oh; width = 0; os_set_font(*s++); } else width += charwidth(c,&oh); } sf_poptextsettings(); return (width+oh+wacc); } /* * os_display_string * * Pass a string of characters to os_display_char. * */ void os_display_string(const zchar *s) { zword c; while ((c = *s++) != 0) { if (c == ZC_NEW_FONT) os_set_font(*s++); else if (c == ZC_NEW_STYLE) os_set_text_style(*s++); else os_display_char(c); } } /* * os_display_char * * Display a character of the current font using the current colours and * text style. The cursor moves to the next position. Printable codes are * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph * indentation), and Unicode characters above 255. The screen should not * be scrolled after printing to the bottom right corner. * */ void os_display_char(zchar c) { if (c == ZC_INDENT) { os_display_char(' '); os_display_char(' '); os_display_char(' '); } else if (c == ZC_GAP) { os_display_char(' '); os_display_char(' '); } else if ((c >= 32 && c <= 126) || (c >= 160)) { SF_glyph *g; setfont(current.zfontnum); g = current.font->getglyph(current.font,c,1); //printf("{%c}%d.%p/%p",c,current.zfontnum,current.font,g); if (g) { //printf("[%c]\n",c); fflush(stdout); sf_writeglyph(g); m_exitPause = true; } } } /* * os_buffer_screen * * Set the screen buffering mode, and return the previous mode. * Possible values for mode are: * * 0 - update the display to reflect changes when possible * 1 - do not update the display * -1 - redraw the screen, do not change the mode * */ int os_buffer_screen (int mode) { if (mode == -1) sf_flushdisplay(); return 0; } /* * os_wrap_window * * Return non-zero if the window should have text wrapped. * */ int os_wrap_window (int win) { return 1; } /* * os_window_height * * Called when the height of a window is changed. * */ void os_window_height (int win, int height) { } /* * os_set_cursor * * Place the text cursor at the given coordinates. Top left is (1,1). * */ void os_set_cursor(int row, int col) { sf_flushtext(); // theWnd->ResetOverhang(); current.cx = col-1; current.cy = row-1; } extern SF_bdffont * SF_defaultfont; extern int SF_defaultfontsize; static void destroySFonly( SFONT *f) { if (f) free(f); } extern SFONT *SF_font3, *SF_font3double; SFONT * (*ttfontloader)( char *fspec, int *err) = NULL; void (*ttfontsdone)() = NULL; static SFONT *tryloadfont( char *fspec) { int err,size; char *p; SFONT *b = NULL; for (;;){ p = strchr(fspec,'|'); if (p) *p = 0; if (ttfontloader) b = ttfontloader(fspec,&err); if (!b) b = loadfont(fspec,&err,&size); if (b) break; if (p) { *p = '|'; fspec = p+1;} else break; } return b; } SFONT *sf_VGA_SFONT; // ensure a font loaded void sf_initfonts() { int i, j, size=0; int w,h,nby,m,nocc; byte *cfont, *bmp; SF_glyph *g; SF_bdffont *b, *norm, *emph, *bold, *bemp; SFONT *Norm, *Emph=NULL, *Bold=NULL, *Bemp=NULL; norm = SF_defaultfont; //dumpfont(norm); sf_VGA_SFONT = Norm = makefont(norm); if (!Norm) os_fatal("malloc() failure in initfonts()"); Norm->destroy = destroySFonly; // get size of default font size = SF_defaultfontsize; // copy norm to emphasized emph = malloc(size); if (!emph) os_fatal("malloc() failure in initfonts()"); Emph = makefont(emph); if (!Emph) os_fatal("malloc() failure in initfonts()"); memcpy (emph, norm, size); // emphasize (underline)... cfont = (byte *)emph; for (i = norm->minchar;i <= norm->maxchar;i++){ m = norm->glyphs[i-norm->minchar]; if (!m) continue; g = (SF_glyph *)(cfont + m); w = g->dx; h = g->h; nby = (g->w+7)/8; bmp = (byte *)(&(g->bitmap[0])); bmp[h-2] = 0xff; } // make a copy for bold bold = malloc(size); if (!bold) os_fatal("malloc() failure in initfonts()"); Bold = makefont(bold); if (!Bold) os_fatal("malloc() failure in initfonts()"); memcpy (bold, norm, size); // boldify... cfont = (byte *)bold; for (i = norm->minchar;i <= norm->maxchar;i++){ int c; m = norm->glyphs[i-norm->minchar]; if (!m) continue; g = (SF_glyph *)(cfont + m); w = g->dx; h = g->h; nby = (g->w+7)/8; bmp = (byte *)(&(g->bitmap[0])); for (j=0;j> 1); } } // copy bold to bold, emphasized bemp = malloc(size); if (!bemp) os_fatal("malloc() failure in initfonts()"); Bemp = makefont(bemp); if (!Bemp) os_fatal("malloc() failure in initfonts()"); memcpy (bemp, bold, size); // emphasize (underline)... cfont = (byte *)bemp; for (i = norm->minchar;i <= norm->maxchar;i++){ m = norm->glyphs[i-norm->minchar]; if (!m) continue; g = (SF_glyph *)(cfont + m); w = g->dx; h = g->h; nby = (g->w+7)/8; bmp = (byte *)(&(g->bitmap[0])); bmp[h-2] = 0xff; } myfonts[0] = myfonts[4] = Norm; norm->refcount = 2; myfonts[1] = myfonts[5] = Bold; bold->refcount = 2; myfonts[2] = myfonts[6] = Emph; emph->refcount = 2; myfonts[3] = myfonts[7] = Bemp; bemp->refcount = 2; // for (i=0;i<8;i++) myfonts[i] = SF_defaultfont; // SF_defaultfont->refcount = 9; CLEANREG(cleanfonts); if (!m_vga_fonts) { for (i=0;i<8;i++) if (m_fontfiles[i]) { SFONT *b = tryloadfont(m_fontfiles[i]); if (!b) fprintf(stderr,"WARNING: could not load font%d [%s]\n",i,m_fontfiles[i]); else { setfontk(i,b); b->refcount = 1; } } } if (ttfontsdone) ttfontsdone(); // now set the graphics font if (myfonts[4]->height(myfonts[4]) < 16) myfonts[8] = SF_font3; else myfonts[8] = SF_font3double; //for (i=0;i<8;i++){ SFONT *s = myfonts[i]; printf("%d %p %d %d %d %d %d\n", //i,s,s->minchar(s),s->maxchar(s),s->ascent(s),s->descent(s),s->height(s));} } frotz-2.44/src/sdl/sf_osfdlg.c0000644000175000017500000004525212527045477013240 00000000000000#include #include #include #include #define STATIC static #include "sf_frotz.h" typedef struct { void *left, *right; char *value; } ENTRY; extern SFONT *sf_VGA_SFONT; #define FRAMECOLOR 222275 static char buffer[512]; static char lastdir[FILENAME_MAX] = ""; static char filename[FILENAME_MAX]; static char pattern[64]; static zword pushed = 0; static int wentry; static ulong *sbuffer = NULL; static int sbpitch; // in longs static int ewidth, eheight; static int X,Y,W,H, xdlg,ydlg,wdlg,hdlg; #define HTEXT 18 STATIC void cleanlist( ENTRY *t); STATIC void drawlist(); STATIC ENTRY * dodir( char *dirname, char *pattern, char *resdir, int size, int *ndirs, int *ntot); static int Numdirs, Numtot, First; static ENTRY *curdir = NULL, *selected; STATIC void updatelist() { if (curdir) cleanlist(curdir); curdir = dodir(lastdir,pattern,lastdir,FILENAME_MAX,&Numdirs,&Numtot); First = 0; selected = NULL; drawlist(); } STATIC void goright(); STATIC void goleft(); // assumes a / at end STATIC void goup() { char *p; if (strlen(lastdir) < 2) return; lastdir[strlen(lastdir)-1] = 0; p = strrchr(lastdir,'/'); if (p){ p[1] = 0; updatelist();} else strcat(lastdir,"/"); } typedef struct { int x,y,w,h; // internal zword (*click)(int,int); ulong back; int isbutton; } BAREA; #define MAXBAREA 20 static BAREA bareas[MAXBAREA]; static int nbareas=0; static SF_textsetting *ts; #define BFRAME 2 #define SPC 5 #define WDLG (63*8) #define HDLG 208 #define HCURSOR 8 #define O_BLACK 0 #define O_GRAY1 0x8a8a8a #define O_GRAY2 0xd6d6d6 #define O_GRAY3 0xe2e2e2 #define O_WHITE 0xf5f5f5 STATIC void frame_upframe( int x, int y, int w, int h){ ulong v = O_WHITE; sf_chline(x,y,v,w); sf_cvline(x,y,v,--h); v = O_BLACK; sf_chline(x,y+h,v,w--); sf_cvline(x+w--,y,v,h--); x++; y++; v = O_GRAY3; sf_chline(x,y,v,w); sf_cvline(x,y,v,--h); v = O_GRAY1; sf_chline(x,y+h,v,w--); sf_cvline(x+w,y,v,h); } STATIC void frame_downframe( int x, int y, int w, int h){ ulong v = O_BLACK; sf_chline(x,y,v,w); sf_cvline(x,y,v,--h); v = O_WHITE; sf_chline(x,y+h,v,w--); sf_cvline(x+w--,y,v,h--); x++; y++; v = O_GRAY1; sf_chline(x,y,v,w); sf_cvline(x,y,v,--h); v = O_GRAY3; sf_chline(x,y+h,v,w--); sf_cvline(x+w,y,v,h); } // internal coords STATIC int addarea( int x, int y, int w, int h, zword (*click)(int,int)) { BAREA *a = bareas+nbareas; a->x = x; a->y = y; a->w = w; a->h = h; a->click = click; a->back = O_GRAY2; return nbareas++; } STATIC void clarea( int n) { BAREA *a = bareas+n; sf_fillrect(a->back,a->x,a->y,a->w,a->h); } STATIC void writetext( ulong color, const char *s, int x, int y, int w, int center) { int ox,oy,ow,oh; //printf("W %p [%s]\n",s,s ? s : "??"); if (!s) return; if (!s[0]) return; sf_getclip(&ox,&oy,&ow,&oh); sf_setclip(x,y,w,HTEXT); //printf("1\n"); if (center) { int wt = 8*strlen(s); x += (w-wt)/2; } //printf("2 ts %p\n",ts); fflush(stdout); if (ts < 1000){sf_flushdisplay(); getchar();} ts->cx = x; ts->cy = y; ts->fore = color; //printf("3\n"); fflush(stdout); while (*s) sf_writeglyph(ts->font->getglyph(ts->font,(*s++),1)); //printf("4\n"); sf_setclip(ox,oy,ow,oh); //printf("5\n"); } STATIC int addbutton( int x, int y, int w, int h, char *text, zword (*click)(int,int)) { int b = addarea(x,y,w,h,click); bareas[b].isbutton = 1; frame_upframe(x-2,y-2,w+4,h+4); clarea(b); if (text) writetext(0,text,x,y,w,1); return b; } static int B_up, B_ok, B_cancel; static int A_dir, A_filter, A_entry, A_list; #define BUTTW 60 STATIC void showfilename( int pos) { BAREA *a = bareas+A_entry; clarea(A_entry); writetext(0,filename,a->x,a->y,a->w,0); if (pos >= 0) sf_cvline(a->x+8*pos,a->y,O_BLACK,HTEXT); } STATIC void clicked( BAREA *a) { frame_downframe(a->x-2,a->y-2,a->w+4,a->h+4); sf_flushdisplay(); sf_sleep(100); frame_upframe(a->x-2,a->y-2,a->w+4,a->h+4); sf_flushdisplay(); } STATIC zword checkmouse( int i0) { int x = mouse_x-1, y = mouse_y-1; int i; for (i=i0;i a->x && x < a->x+a->w && y > a->y && y < a->y+a->h) { if (a->click) { if (a->isbutton) clicked(a); return a->click(x-a->x,y-a->y); } else return 0; } } return 0; } STATIC zword Zup( int x, int y) { goup(); return 0; } STATIC zword Zok( int x, int y) { return ZC_RETURN; } STATIC zword Zcanc( int x, int y) { return ZC_ESCAPE; } STATIC zword Zselect( int x, int y); STATIC zword yesnoover( int xc, int yc); STATIC zword Zentry( int x, int y); STATIC zword inputkey() { zword c = sf_read_key(0,0,1); if (c == ZC_SINGLE_CLICK) { switch (mouse_button) { case 4: c = ZC_ARROW_LEFT; break; case 5: c = ZC_ARROW_RIGHT; break; case 1: break; default: c = 0; break; } } // if (os_read_mouse() != 1) c = 0; return c; } int (*sf_sysdialog)( bool existing, const char *def, const char *filt, const char *tit, char **res) = NULL; STATIC int myosdialog( bool existing, const char *def, const char *filt, const char *tit, char **res, ulong *sbuf, int sbp, int ew, int eh, int isfull) { char *pp; ulong *saved; int y0, y1, y2, x1; zword c = 0; // allow system-specific dialog if not fullscreen if (isfull == 0) if (sf_sysdialog) return sf_sysdialog(existing,def,filt,tit,res); ts = sf_curtextsetting(); if (!ts) return SF_NOTIMP; //printf("0 ts %p (%p)\n",ts,&ts); if (!def) def = ""; strcpy(filename,def); pp = strrchr(filename,'/'); if (pp) { *pp = 0; strcpy(lastdir,filename); strcpy(filename,pp+1); } if (!filt) filt = "*|All files"; if (!lastdir[0]) strcpy(lastdir,"./"); strcpy(buffer,filt); pp = strchr(buffer,'|'); if (pp) *pp = 0; strcpy(pattern,buffer); ewidth = ew; eheight = eh; sbuffer = sbuf; sbpitch = sbp; wdlg = WDLG; hdlg = HDLG; nbareas = 0; W = WDLG+4*BFRAME+2*SPC; H = HDLG+4*BFRAME+6*SPC+6*BFRAME+3*(HTEXT+2)+HCURSOR+HTEXT; if (W > ew) return SF_NOTIMP; if (H > eh) return SF_NOTIMP; X = (ew-W)/2; Y = (eh-H)/2; // internal!! xdlg = X+SPC+2*BFRAME; ydlg = Y+2*SPC+4*BFRAME+HTEXT+HTEXT; wentry = wdlg - BUTTW - SPC - 2*BFRAME; saved = sf_savearea(X,Y,W,H); if (!saved) return SF_NOTIMP; //printf("saved: %p %d %d %d %d\n",saved,saved[0],saved[1],saved[2],saved[3]); sf_pushtextsettings(); ts->font = sf_VGA_SFONT; ts->style = 0; ts->oh = 0; ts->fore = 0; ts->backTransparent = 1; sf_fillrect(O_GRAY2,X,Y,W,H); // frame_upframe(X,Y,W,H); sf_rect(FRAMECOLOR,X,Y,W,H); sf_rect(FRAMECOLOR,X+1,Y+1,W-2,H-2); sf_fillrect(FRAMECOLOR,X,Y+2,W,HTEXT); if (tit) writetext(O_WHITE,tit,X+2+SPC,Y+2,W-4,0); A_list = addarea(xdlg,ydlg,wdlg,hdlg,Zselect); bareas[A_list].back = O_WHITE; clarea(A_list); frame_downframe(xdlg-2,ydlg-2,wdlg+4,hdlg+4); y0 = Y+SPC+2*BFRAME+HTEXT; y2 = Y+H-SPC-2*BFRAME-HTEXT; y1 = y2-SPC-HTEXT-2*BFRAME; x1 = xdlg+wentry+2*BFRAME+SPC; A_dir = addarea(xdlg,y0,wentry,HTEXT,NULL); A_entry = addarea(xdlg,y1,wentry,HTEXT,Zentry); bareas[A_entry].back = O_WHITE; clarea(A_entry); frame_downframe(xdlg-2,y1-2,wentry+4,HTEXT+4); B_up = addbutton(x1,y0,BUTTW,HTEXT,"^up^",Zup); A_filter = addarea(xdlg,y2,wentry,HTEXT,NULL); strcpy(buffer,"Filter: "); strcat(buffer,filt); writetext(0,buffer,xdlg,y2,wentry,0); B_cancel = addbutton(x1,y2,BUTTW,HTEXT,"Cancel",Zcanc); B_ok = addbutton(x1,y1,BUTTW,HTEXT,"OK",Zok); showfilename(-1); updatelist(); for (;;) { if (pushed) { c = pushed; pushed = 0;} else c = inputkey(); if (c == ZC_SINGLE_CLICK) c = checkmouse(0); if (c == VK_INS) c = Zentry(0,-1); if (c == ZC_ARROW_LEFT) goleft(); if (c == ZC_ARROW_RIGHT) goright(); if (c == ZC_ESCAPE) break; if (c == ZC_RETURN) { strcpy(buffer,lastdir); strcat(buffer,filename); *res = buffer; if ((existing==0) && (access(buffer,F_OK)==0)) c = yesnoover(xdlg+wdlg/2,ydlg+hdlg/2); if (c == ZC_RETURN) break; } } sf_poptextsettings(); cleanlist(curdir); curdir = NULL; //printf("2saved: %p %d %d %d %d\n",saved,saved[0],saved[1],saved[2],saved[3]); sf_restoreareaandfree(saved); if (c == ZC_ESCAPE) return -1; if (c == ZC_RETURN) { strcpy(buffer,lastdir); strcat(buffer,filename); *res = buffer; return 0; } return SF_NOTIMP; } static void setdialog(void) __attribute__((constructor)); static void setdialog(void) { sf_osdialog = myosdialog; } /////////////////////////////////// #include #include #include #include #ifdef WIN32 #define strcasecmp stricmp #else #include #endif #include #include //#include #include // simplified fnmatch - only allows a single * at beginning STATIC int myfnmatch( const char *pattern, const char *p, int dummy) { int lpat, lp; if (!pattern) return -1; if (!p) return -1; if (pattern[0] != '*') return strcmp(pattern,p); lpat = strlen(pattern); if (lpat == 1) return 0; // * matches anything lpat--; pattern++; lp = strlen(p); if (lp < lpat) return 1; // too short return strcmp(pattern,p+lp-lpat); } STATIC void cleanlist( ENTRY *t) { while (t) { ENTRY *n = t->right; if (t->value) free(t->value); free(t); t = n; } } STATIC ENTRY * newentry( char *s) { ENTRY *r = calloc(1,sizeof(ENTRY)); if (r){ r->value = strdup(s); if (!r->value){ free(r); return NULL;} } return r; } STATIC void addentry( char *s, ENTRY **ae) { ENTRY *t = *ae; if (!t) { *ae = newentry(s); return; } for (;;) { int k = strcasecmp(s,t->value); if (!k) return; if (k > 0) { if (t->right) t = t->right; else { t->right = newentry(s); return; } } else { if (t->left) t = t->left; else { t->left = newentry(s); return; } } } } STATIC char *resolvedir( char *dir, char *res, int size) { char cwd[FILENAME_MAX], *p; int i; if (!getcwd(cwd,FILENAME_MAX)) return NULL; if (chdir(dir)) return NULL; p = getcwd(res,size); for (i=0;p[i];i++) if (p[i]=='\\') p[i] = '/'; chdir(cwd); if (p) { int n = strlen(p); if (n) if (p[n-1] != '/') { p[n] = '/'; p[n+1] = 0;} } return p; } STATIC ENTRY * dodir( char *dirname, char *pattern, char *resdir, int size, int *ndirs, int *ntot) { DIR *dir; ENTRY *dirs = NULL; ENTRY *files = NULL, *res = NULL; struct dirent *d; char *p, *resdend; struct stat fst; int n; void exhaust( ENTRY *e) { if (!e) return; exhaust(e->left); e->left = res; res = e; n++; exhaust(e->right); } //printf("\ndodir\n"); if (!resolvedir(dirname,resdir,size)) return NULL; resdend = resdir+strlen(resdir); //printf("[%s]\n",resdir); // MinGW opendir() does not like the final slash #ifdef WIN32 n = strlen(resdir); if (n > 2 && (resdir[n-2] != ':')) resdir[n-1] = 0; dir = opendir(resdir); resdir[n-1] = '/'; #else dir = opendir(resdir); #endif if (!dir) return NULL; //printf("opened [%s]\n",resdir); for (;;) { d = readdir(dir); if (!d) break; p = d->d_name; if (strcmp(p,".")==0) continue; if (strcmp(p,"..")==0) continue; strcpy(resdend,p); //printf("-%s\n",resdir); if (stat(resdir,&fst)) continue; //printf("--mode %x\n",fst.st_mode); if (fst.st_mode & S_IFDIR) addentry(p,&dirs); else { //printf("--fnmatch: %d\n",fnmatch(pattern,p,0)); if (myfnmatch(pattern,p,0)==0) addentry(p,&files); } } closedir(dir); *resdend = 0; n = 0; exhaust(dirs); *ndirs = n; exhaust(files); *ntot = n; if (res) while (res->left) { ((ENTRY *)(res->left))->right = res; res = res->left; } return res; } ////////////////////////////////////////////// // white,black,gray,yellow static ulong bcolors[4] = {0xfcfcfc,0,0xa0a0a0,0xa0d0e0}; static unsigned char folderbmp[] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,1,1,1,1,1,1, 0,0,0,0,0,0,0,0, 0,1,3,3,3,3,3,3, 1,1,1,1,1,1,0,0, 0,1,3,3,3,3,3,3, 3,3,3,3,3,3,1,0, 0,1,3,3,3,3,3,3, 3,1,1,1,1,1,1,0, 0,1,3,3,1,1,1,1, 1,3,3,3,3,3,1,0, 0,1,3,1,3,3,3,3, 3,3,3,3,3,3,1,0, 0,1,3,1,3,3,3,3, 3,3,3,3,3,3,1,0, 0,1,3,1,3,3,3,3, 3,3,3,3,3,3,1,0, 0,1,3,1,3,3,3,3, 3,3,3,3,3,3,1,0, 0,1,3,1,3,3,3,3, 3,3,3,3,3,3,1,0, 0,1,3,1,3,3,3,3, 3,3,3,3,3,3,1,0, 0,0,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}; static unsigned char docbmp[] = { 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, 0,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0, 0,1,0,2,2,2,2,2, 2,2,2,2,2,0,1,0, 0,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0, 0,1,0,2,2,2,2,2, 2,2,2,2,2,0,1,0, 0,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0, 0,1,0,2,2,2,2,2, 2,2,2,2,2,0,1,0, 0,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0, 0,1,0,2,2,2,2,2, 2,2,2,2,2,0,1,0, 0,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0, 0,1,0,0,0,0,0,0, 0,0,0,0,0,0,1,0, 0,0,1,1,1,1,1,1, 1,1,1,1,1,1,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0}; //////////////////////////////// STATIC void drawit( int x, int y, ENTRY *e, int w, int issub) { int i,j,n,sw,dy,color; unsigned char *bmp; char *s = e->value; bmp = (issub ? folderbmp : docbmp); for (i=0;i<16;i++) for (j=0;j<16;j++) sf_wpixel(x+j,y+i,bcolors[*bmp++]); x += 17; w -= 17; n = w/8; if (n < 1) return; if (strlen(s) > n) { strcpy(buffer,s); buffer[n] = 0; buffer[n-1] = '>'; s = buffer; } if (e == selected) { color = O_WHITE; sf_fillrect(0,x,y,w,16); } else color = O_BLACK; writetext(color,s,x,y,w,0); } static int Nrows, Ncols, Ewid, Fh; STATIC void drawnames( int x, int y, int w, int h, ENTRY *files, int first, int nsub, int ntot, int ewid) { int i; Fh = 16; Ewid = ewid; Ncols = w/ewid; Nrows = h/Fh; sf_fillrect(O_WHITE,x,y,w,h); if (!files) return; if (first < 0) return; if (nsub > ntot) nsub = ntot; while (first > 0) { files = files->right; if (!files) return; nsub--; ntot--; first--; } if (ntot <= 0) return; if (Ncols < 1) return; if (Nrows < 1) return; if (Nrows*Ncols < ntot) ntot = Nrows*Ncols; for (i=0;iright; } } STATIC void drawlist() { BAREA *a = bareas+A_list, *b = bareas+A_dir; clarea(A_dir); writetext(0,lastdir,b->x,b->y,b->w,0); drawnames(a->x,a->y,a->w,a->h,curdir,First,Numdirs,Numtot,21*8); } STATIC void goright() { if (First+Nrows*Ncols > Numtot) return; First += Nrows; drawlist(); } STATIC void goleft() { if (!First) return; First -= Nrows; drawlist(); } STATIC ENTRY *filesat( int n){ ENTRY *e = curdir; while (n--) { if (e) e = e->right; } return e; } STATIC zword Zselect( int x, int y) { int n; x /= Ewid; y /= Fh; n = First + y + x*Nrows; if (n >= Numtot) { if (selected) { selected = NULL; drawlist(); } return 0; } if (n < Numdirs) { ENTRY *e = filesat(n); if (!e) return 0; strcat(lastdir,e->value); updatelist(); return 0; } selected = curdir; while (n--) selected = selected->right; strcpy(filename,selected->value); showfilename(-1); drawlist(); return 0; } extern void sf_videodata( ulong **sb, int *sp, int *ew, int *eh); zword sf_yesnooverlay( int xc, int yc, char *t, int saverest) { zword c = ZC_RETURN; int nsav = nbareas; ulong *saved = NULL; int hx = BUTTW+3*SPC, hy = HTEXT+2*SPC, heff; heff = 8*strlen(t); if (heff > 2*hx) hx = (heff+3)/2; if (saverest) { ts = sf_curtextsetting(); if (!ts) return ZC_ESCAPE; saved = sf_savearea(xc-hx-2,yc-hy-2,2*hx+4,2*hy+4); if (!saved) return ZC_ESCAPE; sf_pushtextsettings(); ts->font = sf_VGA_SFONT; ts->style = 0; ts->oh = 0; ts->fore = 0; ts->backTransparent = 1; sf_videodata(&sbuffer, &sbpitch, &ewidth, &eheight); } sf_fillrect(FRAMECOLOR,xc-hx-2,yc-hy-2,2*hx+4,2*hy+4); sf_fillrect(O_WHITE,xc-hx,yc-hy,2*hx,2*hy); writetext(O_BLACK,t,xc-hx,yc-SPC-HTEXT,2*hx,1); addbutton(xc-SPC-BUTTW,yc+SPC,BUTTW,HTEXT,"Yes",Zok); addbutton(xc+SPC,yc+SPC,BUTTW,HTEXT,"No",Zcanc); for (;;) { c = inputkey(); if (c == 'n' || c == 'N') c = ZC_ESCAPE; if (c == 'y' || c == 'Y') c = ZC_RETURN; if (c == ZC_SINGLE_CLICK) c = checkmouse(nsav); if (c == ZC_ESCAPE) break; if (c == ZC_RETURN) break; } if (saved) { sf_restoreareaandfree(saved); sf_poptextsettings(); } nbareas = nsav; return c; } STATIC zword yesnoover( int xc, int yc) { zword c; c = sf_yesnooverlay(xc,yc,"Overwrite file?",0); drawlist(); return c; } // this is needed for overlapping source and dest in Zentry // (lib does not guarantee correct behaviour in that case) static void mystrcpy( char *d, const char *s) { while (*d++ = *s++); } STATIC zword Zentry( int x, int y) { static int pos = 10000; int i,n,nmax; zword c; nmax = wentry/8; if (nmax >= FILENAME_MAX) nmax = FILENAME_MAX-1; n = strlen(filename); if (n > nmax) { n = nmax; filename[n] = 0;} if (y >= 0) { pos = x/4-1; if (pos < 0) pos = 0; pos /= 2; } if (pos > n) pos = n; showfilename(pos); for (;;) { c = inputkey(); if (c == ZC_SINGLE_CLICK) { pushed = c; c = 0; break; } if (c == ZC_ESCAPE || c == VK_INS) { c = 0; break; } if (c == ZC_RETURN) break; if (c == ZC_ARROW_LEFT) { if (pos){ pos--; showfilename(pos); } continue; } if (c == ZC_ARROW_RIGHT) { if (pos < n){ pos++; showfilename(pos); } continue; } if (c == ZC_BACKSPACE) { if (pos) { // needs mystrcpy() because overlapping src-dst if (pos < n) mystrcpy(filename+pos-1,filename+pos); n--; filename[n] = 0; pos--; showfilename(pos); } continue; } if ((c >= 32 && c < 127) || (c >= 160 && c < 256)) { if (n >= nmax) continue; if (n > pos) for (i=n;i>pos;i--) filename[i] = filename[i-1]; filename[pos] = c; n++; filename[n] = 0; pos++; showfilename(pos); } } showfilename(-1); return c; } frotz-2.44/src/sdl/sf_deffont.c0000644000175000017500000006767412527045477013423 00000000000000static unsigned char myfont[] = { 0x00,0x00,0x00,0x00,0x20,0x00,0x53,0x01, 0x20,0x00,0x0e,0x02,0xdc,0x04,0x00,0x00, 0xf1,0x04,0x00,0x00,0x06,0x05,0x00,0x00, 0x1b,0x05,0x00,0x00,0x30,0x05,0x00,0x00, 0x45,0x05,0x00,0x00,0x5a,0x05,0x00,0x00, 0x6f,0x05,0x00,0x00,0x84,0x05,0x00,0x00, 0x99,0x05,0x00,0x00,0xae,0x05,0x00,0x00, 0xc3,0x05,0x00,0x00,0xd8,0x05,0x00,0x00, 0xed,0x05,0x00,0x00,0x02,0x06,0x00,0x00, 0x17,0x06,0x00,0x00,0x2c,0x06,0x00,0x00, 0x41,0x06,0x00,0x00,0x56,0x06,0x00,0x00, 0x6b,0x06,0x00,0x00,0x80,0x06,0x00,0x00, 0x95,0x06,0x00,0x00,0xaa,0x06,0x00,0x00, 0xbf,0x06,0x00,0x00,0xd4,0x06,0x00,0x00, 0xe9,0x06,0x00,0x00,0xfe,0x06,0x00,0x00, 0x13,0x07,0x00,0x00,0x28,0x07,0x00,0x00, 0x3d,0x07,0x00,0x00,0x52,0x07,0x00,0x00, 0x67,0x07,0x00,0x00,0x7c,0x07,0x00,0x00, 0x91,0x07,0x00,0x00,0xa6,0x07,0x00,0x00, 0xbb,0x07,0x00,0x00,0xd0,0x07,0x00,0x00, 0xe5,0x07,0x00,0x00,0xfa,0x07,0x00,0x00, 0x0f,0x08,0x00,0x00,0x24,0x08,0x00,0x00, 0x39,0x08,0x00,0x00,0x4e,0x08,0x00,0x00, 0x63,0x08,0x00,0x00,0x78,0x08,0x00,0x00, 0x8d,0x08,0x00,0x00,0xa2,0x08,0x00,0x00, 0xb7,0x08,0x00,0x00,0xcc,0x08,0x00,0x00, 0xe1,0x08,0x00,0x00,0xf6,0x08,0x00,0x00, 0x0b,0x09,0x00,0x00,0x20,0x09,0x00,0x00, 0x35,0x09,0x00,0x00,0x4a,0x09,0x00,0x00, 0x5f,0x09,0x00,0x00,0x74,0x09,0x00,0x00, 0x89,0x09,0x00,0x00,0x9e,0x09,0x00,0x00, 0xb3,0x09,0x00,0x00,0xc8,0x09,0x00,0x00, 0xdd,0x09,0x00,0x00,0xf2,0x09,0x00,0x00, 0x07,0x0a,0x00,0x00,0x1c,0x0a,0x00,0x00, 0x31,0x0a,0x00,0x00,0x46,0x0a,0x00,0x00, 0x5b,0x0a,0x00,0x00,0x70,0x0a,0x00,0x00, 0x85,0x0a,0x00,0x00,0x9a,0x0a,0x00,0x00, 0xaf,0x0a,0x00,0x00,0xc4,0x0a,0x00,0x00, 0xd9,0x0a,0x00,0x00,0xee,0x0a,0x00,0x00, 0x03,0x0b,0x00,0x00,0x18,0x0b,0x00,0x00, 0x2d,0x0b,0x00,0x00,0x42,0x0b,0x00,0x00, 0x57,0x0b,0x00,0x00,0x6c,0x0b,0x00,0x00, 0x81,0x0b,0x00,0x00,0x96,0x0b,0x00,0x00, 0xab,0x0b,0x00,0x00,0xc0,0x0b,0x00,0x00, 0xd5,0x0b,0x00,0x00,0xea,0x0b,0x00,0x00, 0xff,0x0b,0x00,0x00,0x14,0x0c,0x00,0x00, 0x29,0x0c,0x00,0x00,0x3e,0x0c,0x00,0x00, 0x53,0x0c,0x00,0x00,0x68,0x0c,0x00,0x00, 0x7d,0x0c,0x00,0x00,0x92,0x0c,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0xa7,0x0c,0x00,0x00, 0xbc,0x0c,0x00,0x00,0xd1,0x0c,0x00,0x00, 0xe6,0x0c,0x00,0x00,0xfb,0x0c,0x00,0x00, 0x10,0x0d,0x00,0x00,0x25,0x0d,0x00,0x00, 0x3a,0x0d,0x00,0x00,0x4f,0x0d,0x00,0x00, 0x64,0x0d,0x00,0x00,0x79,0x0d,0x00,0x00, 0x8e,0x0d,0x00,0x00,0xa3,0x0d,0x00,0x00, 0xb8,0x0d,0x00,0x00,0xcd,0x0d,0x00,0x00, 0xe2,0x0d,0x00,0x00,0xf7,0x0d,0x00,0x00, 0x0c,0x0e,0x00,0x00,0x21,0x0e,0x00,0x00, 0x36,0x0e,0x00,0x00,0x4b,0x0e,0x00,0x00, 0x60,0x0e,0x00,0x00,0x75,0x0e,0x00,0x00, 0x8a,0x0e,0x00,0x00,0x9f,0x0e,0x00,0x00, 0xb4,0x0e,0x00,0x00,0xc9,0x0e,0x00,0x00, 0xde,0x0e,0x00,0x00,0xf3,0x0e,0x00,0x00, 0x08,0x0f,0x00,0x00,0x1d,0x0f,0x00,0x00, 0x32,0x0f,0x00,0x00,0x47,0x0f,0x00,0x00, 0x5c,0x0f,0x00,0x00,0x71,0x0f,0x00,0x00, 0x86,0x0f,0x00,0x00,0x9b,0x0f,0x00,0x00, 0xb0,0x0f,0x00,0x00,0xc5,0x0f,0x00,0x00, 0xda,0x0f,0x00,0x00,0xef,0x0f,0x00,0x00, 0x04,0x10,0x00,0x00,0x19,0x10,0x00,0x00, 0x2e,0x10,0x00,0x00,0x43,0x10,0x00,0x00, 0x58,0x10,0x00,0x00,0x6d,0x10,0x00,0x00, 0x82,0x10,0x00,0x00,0x97,0x10,0x00,0x00, 0xac,0x10,0x00,0x00,0xc1,0x10,0x00,0x00, 0xd6,0x10,0x00,0x00,0xeb,0x10,0x00,0x00, 0x00,0x11,0x00,0x00,0x15,0x11,0x00,0x00, 0x2a,0x11,0x00,0x00,0x3f,0x11,0x00,0x00, 0x54,0x11,0x00,0x00,0x69,0x11,0x00,0x00, 0x7e,0x11,0x00,0x00,0x93,0x11,0x00,0x00, 0xa8,0x11,0x00,0x00,0xbd,0x11,0x00,0x00, 0xd2,0x11,0x00,0x00,0xe7,0x11,0x00,0x00, 0xfc,0x11,0x00,0x00,0x11,0x12,0x00,0x00, 0x26,0x12,0x00,0x00,0x3b,0x12,0x00,0x00, 0x50,0x12,0x00,0x00,0x65,0x12,0x00,0x00, 0x7a,0x12,0x00,0x00,0x8f,0x12,0x00,0x00, 0xa4,0x12,0x00,0x00,0xb9,0x12,0x00,0x00, 0xce,0x12,0x00,0x00,0xe3,0x12,0x00,0x00, 0xf8,0x12,0x00,0x00,0x0d,0x13,0x00,0x00, 0x22,0x13,0x00,0x00,0x37,0x13,0x00,0x00, 0x4c,0x13,0x00,0x00,0x61,0x13,0x00,0x00, 0x76,0x13,0x00,0x00,0x8b,0x13,0x00,0x00, 0xa0,0x13,0x00,0x00,0xb5,0x13,0x00,0x00, 0xca,0x13,0x00,0x00,0xdf,0x13,0x00,0x00, 0xf4,0x13,0x00,0x00,0x09,0x14,0x00,0x00, 0x1e,0x14,0x00,0x00,0x33,0x14,0x00,0x00, 0x48,0x14,0x00,0x00,0x5d,0x14,0x00,0x00, 0x72,0x14,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x87,0x14,0x00,0x00, 0x9c,0x14,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x00,0x08,0x08,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x22,0x22,0x22, 0x22,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x12,0x12,0x12,0x7e, 0x24,0x24,0x7e,0x48,0x48,0x48,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x08,0x3e,0x49,0x48,0x38,0x0e,0x09, 0x49,0x3e,0x08,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x31,0x4a, 0x4a,0x34,0x08,0x08,0x16,0x29,0x29,0x46, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x1c,0x22,0x22,0x22,0x1c, 0x39,0x45,0x42,0x46,0x39,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x08,0x08, 0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x04,0x08,0x08,0x10, 0x10,0x10,0x10,0x10,0x10,0x08,0x08,0x04, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x20,0x10,0x10,0x08,0x08,0x08,0x08, 0x08,0x08,0x10,0x10,0x20,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0x00,0x08,0x49,0x2a,0x1c,0x2a,0x49,0x08, 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08, 0x08,0x7f,0x08,0x08,0x08,0x00,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x18,0x08,0x08,0x10,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x02,0x02,0x04,0x08,0x08,0x10,0x10,0x20, 0x40,0x40,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x18,0x24,0x42, 0x42,0x42,0x42,0x42,0x42,0x24,0x18,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x08,0x18,0x28,0x08,0x08,0x08, 0x08,0x08,0x08,0x3e,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x3c, 0x42,0x42,0x02,0x0c,0x10,0x20,0x40,0x40, 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x02, 0x1c,0x02,0x02,0x42,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x04,0x0c,0x14,0x24,0x44,0x44,0x7e, 0x04,0x04,0x04,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x7e,0x40, 0x40,0x40,0x7c,0x02,0x02,0x02,0x42,0x3c, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x1c,0x20,0x40,0x40,0x7c, 0x42,0x42,0x42,0x42,0x3c,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x7e,0x02,0x02,0x04,0x04,0x04,0x08,0x08, 0x08,0x08,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x3c,0x42,0x42, 0x42,0x3c,0x42,0x42,0x42,0x42,0x3c,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x3c,0x42,0x42,0x42,0x3e,0x02, 0x02,0x02,0x04,0x38,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0x00,0x18,0x18,0x00,0x00,0x00,0x18,0x18, 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x18, 0x00,0x00,0x00,0x18,0x08,0x08,0x10,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x00,0x02,0x04,0x08,0x10,0x20,0x10, 0x08,0x04,0x02,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x7e,0x00,0x00,0x00,0x7e,0x00,0x00, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x40,0x20,0x10,0x08, 0x04,0x08,0x10,0x20,0x40,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x3c,0x42,0x42,0x02,0x04,0x08,0x08,0x00, 0x08,0x08,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x1c,0x22,0x4a, 0x56,0x52,0x52,0x52,0x4e,0x20,0x1e,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x18,0x24,0x24,0x42,0x42,0x7e, 0x42,0x42,0x42,0x42,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x7c, 0x42,0x42,0x42,0x7c,0x42,0x42,0x42,0x42, 0x7c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x40, 0x40,0x40,0x40,0x42,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x78,0x44,0x42,0x42,0x42,0x42,0x42, 0x42,0x44,0x78,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x7e,0x40, 0x40,0x40,0x7c,0x40,0x40,0x40,0x40,0x7e, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x7e,0x40,0x40,0x40,0x7c, 0x40,0x40,0x40,0x40,0x40,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x3c,0x42,0x42,0x40,0x40,0x4e,0x42,0x42, 0x46,0x3a,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x42,0x42,0x42, 0x42,0x7e,0x42,0x42,0x42,0x42,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x3e,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x3e,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x1f, 0x04,0x04,0x04,0x04,0x04,0x04,0x44,0x44, 0x38,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x42,0x44,0x48,0x50, 0x60,0x60,0x50,0x48,0x44,0x42,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x40,0x40,0x40,0x40,0x40,0x40,0x40, 0x40,0x40,0x7e,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x42,0x42, 0x66,0x66,0x5a,0x5a,0x42,0x42,0x42,0x42, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x42,0x62,0x62,0x52,0x52, 0x4a,0x4a,0x46,0x46,0x42,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x3c,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x7c,0x42,0x42, 0x42,0x7c,0x40,0x40,0x40,0x40,0x40,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42, 0x42,0x5a,0x66,0x3c,0x03,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x7c, 0x42,0x42,0x42,0x7c,0x48,0x44,0x44,0x42, 0x42,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x3c,0x42,0x42,0x40, 0x30,0x0c,0x02,0x42,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x7f,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x42,0x42, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3c, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x41,0x41,0x41,0x22,0x22, 0x22,0x14,0x14,0x08,0x08,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x42,0x42,0x42,0x42,0x5a,0x5a,0x66,0x66, 0x42,0x42,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x42,0x42,0x24, 0x24,0x18,0x18,0x24,0x24,0x42,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x41,0x41,0x22,0x22,0x14,0x08, 0x08,0x08,0x08,0x08,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x7e, 0x02,0x02,0x04,0x08,0x10,0x20,0x40,0x40, 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x0e,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x0e,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x40,0x40,0x20,0x10,0x10,0x08,0x08, 0x04,0x02,0x02,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x70,0x10,0x10, 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, 0x70,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x18,0x24,0x42,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x7f,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x18,0x10,0x10,0x08,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x00,0x00,0x3c,0x42,0x02,0x3e, 0x42,0x42,0x46,0x3a,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x40,0x40, 0x40,0x5c,0x62,0x42,0x42,0x42,0x42,0x62, 0x5c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x42, 0x40,0x40,0x40,0x40,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x02,0x02,0x02,0x3a,0x46,0x42,0x42,0x42, 0x42,0x46,0x3a,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, 0x3c,0x42,0x42,0x7e,0x40,0x40,0x42,0x3c, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x0c,0x10,0x10,0x10,0x7c,0x10, 0x10,0x10,0x10,0x10,0x10,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x02,0x3a,0x44,0x44,0x44,0x38,0x20, 0x3c,0x42,0x42,0x3c,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x40,0x40,0x40,0x5c, 0x62,0x42,0x42,0x42,0x42,0x42,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x08,0x08,0x00,0x18,0x08,0x08,0x08, 0x08,0x08,0x08,0x3e,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x04,0x04, 0x00,0x0c,0x04,0x04,0x04,0x04,0x04,0x04, 0x04,0x48,0x30,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x40,0x40,0x44,0x48, 0x50,0x60,0x50,0x48,0x44,0x42,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x18,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x3e,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, 0x76,0x49,0x49,0x49,0x49,0x49,0x49,0x49, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x5c,0x62,0x42, 0x42,0x42,0x42,0x42,0x42,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42, 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x5c, 0x62,0x42,0x42,0x42,0x42,0x62,0x5c,0x40, 0x40,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x00,0x00,0x3a,0x46,0x42,0x42, 0x42,0x42,0x46,0x3a,0x02,0x02,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0x00,0x5c,0x62,0x42,0x40,0x40,0x40,0x40, 0x40,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x00,0x00,0x3c,0x42, 0x40,0x30,0x0c,0x02,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x10,0x10,0x7c,0x10,0x10,0x10,0x10, 0x10,0x10,0x0c,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, 0x42,0x42,0x42,0x42,0x42,0x42,0x46,0x3a, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x42,0x42,0x42, 0x24,0x24,0x24,0x18,0x18,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x00,0x41,0x49,0x49,0x49,0x49,0x49, 0x49,0x36,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x42, 0x42,0x24,0x18,0x18,0x24,0x42,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x00,0x00,0x42,0x42,0x42,0x42, 0x42,0x26,0x1a,0x02,0x02,0x3c,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0x00,0x7e,0x02,0x04,0x08,0x10,0x20,0x40, 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x0c,0x10,0x10,0x08,0x08, 0x10,0x10,0x08,0x08,0x10,0x10,0x0c,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x30,0x08,0x08, 0x10,0x10,0x08,0x08,0x10,0x10,0x08,0x08, 0x30,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x31,0x49,0x46,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x08,0x08,0x00, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x00,0x00,0x08,0x08,0x3e,0x49,0x48,0x48, 0x49,0x3e,0x08,0x08,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x0e, 0x10,0x10,0x10,0x7c,0x10,0x10,0x10,0x3e, 0x61,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x00,0x42,0x24,0x3c, 0x24,0x24,0x3c,0x24,0x42,0x00,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x41,0x22,0x14,0x08,0x7f,0x08,0x7f, 0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x08,0x08, 0x08,0x08,0x00,0x00,0x08,0x08,0x08,0x08, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x3c,0x42,0x40,0x3c,0x42, 0x42,0x3c,0x02,0x42,0x3c,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x24,0x24,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x3c,0x42,0x99, 0xa5,0xa1,0xa1,0xa5,0x99,0x42,0x3c,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x1c,0x02,0x1e,0x22,0x1e,0x00,0x3e,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0x12,0x12,0x24,0x24,0x48,0x24,0x24,0x12, 0x12,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x7e,0x02,0x02,0x02,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x7e,0x00, 0x00,0x00,0x00,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x3c,0x42, 0xb9,0xa5,0xa5,0xb9,0xa9,0xa5,0x42,0x3c, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x7e,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x10,0x28,0x28,0x10, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x08,0x08,0x08, 0x7f,0x08,0x08,0x08,0x00,0x7f,0x00,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x1c,0x22,0x02,0x1c,0x20,0x20,0x3e,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x1c,0x22,0x02, 0x1c,0x02,0x22,0x1c,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x0c,0x30,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x22,0x22,0x22, 0x22,0x36,0x2a,0x20,0x20,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x3e,0x7a,0x7a, 0x7a,0x7a,0x3a,0x0a,0x0a,0x0a,0x0a,0x0a, 0x0e,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18, 0x18,0x00,0x00,0x00,0x00,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x08,0x30,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x08,0x18,0x28,0x08,0x08, 0x08,0x08,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x1c,0x22,0x22,0x22,0x1c,0x00,0x3e,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x00,0x00,0x00, 0x48,0x48,0x24,0x24,0x12,0x24,0x24,0x48, 0x48,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x00,0x00,0x22,0x62,0x24,0x28, 0x28,0x12,0x16,0x2a,0x4e,0x42,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x00, 0x00,0x22,0x62,0x24,0x28,0x28,0x14,0x1a, 0x22,0x44,0x4e,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x62,0x12, 0x24,0x18,0x68,0x12,0x16,0x2a,0x4e,0x42, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x08,0x08,0x00,0x08,0x08, 0x30,0x42,0x42,0x42,0x3c,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x30,0x0c,0x00,0x00, 0x18,0x24,0x24,0x42,0x42,0x7e,0x42,0x42, 0x42,0x42,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x0c,0x30,0x00,0x00,0x18,0x24,0x24, 0x42,0x42,0x7e,0x42,0x42,0x42,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x18,0x24, 0x00,0x00,0x18,0x24,0x24,0x42,0x42,0x7e, 0x42,0x42,0x42,0x42,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x32,0x4c,0x00,0x00,0x18, 0x24,0x24,0x42,0x42,0x7e,0x42,0x42,0x42, 0x42,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x24,0x24,0x00,0x00,0x18,0x24,0x24,0x42, 0x42,0x7e,0x42,0x42,0x42,0x42,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x18,0x24,0x18, 0x00,0x18,0x24,0x24,0x42,0x42,0x7e,0x42, 0x42,0x42,0x42,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x1f,0x28, 0x48,0x48,0x7f,0x48,0x48,0x48,0x48,0x4f, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x3c,0x42,0x42,0x40,0x40, 0x40,0x40,0x42,0x42,0x3c,0x08,0x30,0x08, 0x08,0x10,0x00,0xfe,0x30,0x0c,0x00,0x00, 0x7e,0x40,0x40,0x40,0x7c,0x40,0x40,0x40, 0x40,0x7e,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x0c,0x30,0x00,0x00,0x7e,0x40,0x40, 0x40,0x7c,0x40,0x40,0x40,0x40,0x7e,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x18,0x24, 0x00,0x00,0x7e,0x40,0x40,0x40,0x7c,0x40, 0x40,0x40,0x40,0x7e,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x24,0x24,0x00,0x00,0x7e, 0x40,0x40,0x40,0x7c,0x40,0x40,0x40,0x40, 0x7e,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x18,0x06,0x00,0x00,0x3e,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x0c,0x30,0x00, 0x00,0x3e,0x08,0x08,0x08,0x08,0x08,0x08, 0x08,0x08,0x3e,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x18,0x24,0x00,0x00,0x3e,0x08, 0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x3e, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x24, 0x24,0x00,0x00,0x3e,0x08,0x08,0x08,0x08, 0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x78,0x44,0x42,0x42,0xf2,0x42,0x42,0x42, 0x44,0x78,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x32,0x4c,0x00,0x00,0x42,0x62,0x62, 0x52,0x52,0x4a,0x4a,0x46,0x46,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x30,0x0c, 0x00,0x00,0x3c,0x42,0x42,0x42,0x42,0x42, 0x42,0x42,0x42,0x3c,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x0c,0x30,0x00,0x00,0x3c, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x18,0x24,0x00,0x00,0x3c,0x42,0x42,0x42, 0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x32,0x4c,0x00, 0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x42, 0x42,0x42,0x3c,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x24,0x24,0x00,0x00,0x3c,0x42, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x3c, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x42,0x24, 0x18,0x24,0x42,0x00,0x00,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x02, 0x3a,0x44,0x46,0x4a,0x4a,0x52,0x52,0x62, 0x22,0x5c,0x40,0x00,0x08,0x08,0x10,0x00, 0xfe,0x30,0x0c,0x00,0x00,0x42,0x42,0x42, 0x42,0x42,0x42,0x42,0x42,0x42,0x3c,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x0c,0x30, 0x00,0x00,0x42,0x42,0x42,0x42,0x42,0x42, 0x42,0x42,0x42,0x3c,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x18,0x24,0x00,0x00,0x42, 0x42,0x42,0x42,0x42,0x42,0x42,0x42,0x42, 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x24,0x24,0x00,0x00,0x42,0x42,0x42,0x42, 0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x0c,0x30,0x00, 0x00,0x41,0x41,0x22,0x22,0x14,0x08,0x08, 0x08,0x08,0x08,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x40,0x40,0x78, 0x44,0x42,0x42,0x44,0x78,0x40,0x40,0x40, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x38,0x44,0x44,0x44,0x7c, 0x42,0x42,0x42,0x62,0x5c,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x30,0x0c, 0x00,0x00,0x3c,0x42,0x02,0x3e,0x42,0x42, 0x46,0x3a,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x0c,0x30,0x00,0x00,0x3c, 0x42,0x02,0x3e,0x42,0x42,0x46,0x3a,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x18,0x24,0x00,0x00,0x3c,0x42,0x02,0x3e, 0x42,0x42,0x46,0x3a,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x32,0x4c,0x00, 0x00,0x3c,0x42,0x02,0x3e,0x42,0x42,0x46, 0x3a,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x24,0x24,0x00,0x00,0x3c,0x42, 0x02,0x3e,0x42,0x42,0x46,0x3a,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x18,0x24, 0x18,0x00,0x00,0x3c,0x42,0x02,0x3e,0x42, 0x42,0x46,0x3a,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x00,0x00, 0x3e,0x49,0x09,0x3f,0x48,0x48,0x49,0x3e, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x3c,0x42,0x40, 0x40,0x40,0x40,0x42,0x3c,0x08,0x30,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x30,0x0c, 0x00,0x00,0x3c,0x42,0x42,0x7e,0x40,0x40, 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x0c,0x30,0x00,0x00,0x3c, 0x42,0x42,0x7e,0x40,0x40,0x42,0x3c,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x18,0x24,0x00,0x00,0x3c,0x42,0x42,0x7e, 0x40,0x40,0x42,0x3c,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x24,0x24,0x00, 0x00,0x3c,0x42,0x42,0x7e,0x40,0x40,0x42, 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x30,0x0c,0x00,0x00,0x18,0x08, 0x08,0x08,0x08,0x08,0x08,0x3e,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x0c, 0x30,0x00,0x00,0x18,0x08,0x08,0x08,0x08, 0x08,0x08,0x3e,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x18,0x24,0x00,0x00, 0x18,0x08,0x08,0x08,0x08,0x08,0x08,0x3e, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x24,0x24,0x00,0x00,0x18,0x08,0x08, 0x08,0x08,0x08,0x08,0x3e,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x32,0x0c, 0x14,0x22,0x02,0x3e,0x42,0x42,0x42,0x42, 0x42,0x3c,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x32,0x4c,0x00,0x00,0x5c, 0x62,0x42,0x42,0x42,0x42,0x42,0x42,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x30,0x0c,0x00,0x00,0x3c,0x42,0x42,0x42, 0x42,0x42,0x42,0x3c,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x0c,0x30,0x00, 0x00,0x3c,0x42,0x42,0x42,0x42,0x42,0x42, 0x3c,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x18,0x24,0x00,0x00,0x3c,0x42, 0x42,0x42,0x42,0x42,0x42,0x3c,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x32, 0x4c,0x00,0x00,0x3c,0x42,0x42,0x42,0x42, 0x42,0x42,0x3c,0x00,0x00,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x24,0x24,0x00,0x00, 0x3c,0x42,0x42,0x42,0x42,0x42,0x42,0x3c, 0x00,0x00,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x00,0x00,0x00,0x00,0x18,0x00,0x00, 0x7e,0x00,0x00,0x18,0x00,0x00,0x00,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x00,0x02,0x3c,0x46,0x4a,0x4a,0x52,0x52, 0x62,0x3c,0x40,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x30,0x0c,0x00,0x00,0x42, 0x42,0x42,0x42,0x42,0x42,0x46,0x3a,0x00, 0x00,0x08,0x08,0x10,0x00,0xfe,0x00,0x00, 0x0c,0x30,0x00,0x00,0x42,0x42,0x42,0x42, 0x42,0x42,0x46,0x3a,0x00,0x00,0x08,0x08, 0x10,0x00,0xfe,0x00,0x00,0x18,0x24,0x00, 0x00,0x42,0x42,0x42,0x42,0x42,0x42,0x46, 0x3a,0x00,0x00,0x08,0x08,0x10,0x00,0xfe, 0x00,0x00,0x24,0x24,0x00,0x00,0x42,0x42, 0x42,0x42,0x42,0x42,0x46,0x3a,0x00,0x00, 0x08,0x08,0x10,0x00,0xfe,0x00,0x00,0x0c, 0x30,0x00,0x00,0x42,0x42,0x42,0x42,0x42, 0x26,0x1a,0x02,0x02,0x3c,0x08,0x08,0x10, 0x00,0xfe,0x00,0x00,0x00,0x00,0x20,0x20, 0x3c,0x22,0x22,0x22,0x24,0x28,0x30,0x20, 0x20,0x20,0x08,0x08,0x10,0x00,0xfe,0x00, 0x00,0x24,0x24,0x00,0x00,0x42,0x42,0x42, 0x42,0x42,0x26,0x1a,0x02,0x02,0x3c,0x08, 0x08,0x10,0x00,0xfe,0x00,0x00,0x00,0x00, 0x6e,0x90,0x90,0x90,0x9c,0x90,0x90,0x90, 0x90,0x6e,0x00,0x00,0x08,0x08,0x10,0x00, 0xfe,0x00,0x00,0x00,0x00,0x00,0x00,0x6c, 0x92,0x92,0x9e,0x90,0x90,0x92,0x6c,0x00, 0x00}; unsigned char * SF_defaultfont = &(myfont[0]); int SF_defaultfontsize = sizeof(myfont); frotz-2.44/src/sdl/samplerate.h0000644000175000017500000001262312527045477013430 00000000000000/* ** Copyright (C) 2002-2004 Erik de Castro Lopo ** ** This program is free software; you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation; either version 2 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program; if not, write to the Free Software ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ /* ** API documentation is available here: ** http://www.mega-nerd.com/SRC/api.html */ #ifndef SAMPLERATE_H #define SAMPLERATE_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Opaque data type SRC_STATE. */ typedef struct SRC_STATE_tag SRC_STATE ; /* SRC_DATA is used to pass data to src_simple() and src_process(). */ typedef struct { float *data_in, *data_out ; long input_frames, output_frames ; long input_frames_used, output_frames_gen ; int end_of_input ; double src_ratio ; } SRC_DATA ; /* SRC_CB_DATA is used with callback based API. */ typedef struct { long frames ; float *data_in ; } SRC_CB_DATA ; /* ** User supplied callback function type for use with src_callback_new() ** and src_callback_read(). First parameter is the same pointer that was ** passed into src_callback_new(). Second parameter is pointer to a ** pointer. The user supplied callback function must modify *data to ** point to the start of the user supplied float array. The user supplied ** function must return the number of frames that **data points to. */ typedef long (*src_callback_t) (void *cb_data, float **data) ; /* ** Standard initialisation function : return an anonymous pointer to the ** internal state of the converter. Choose a converter from the enums below. ** Error returned in *error. */ SRC_STATE* src_new (int converter_type, int channels, int *error) ; /* ** Initilisation for callback based API : return an anonymous pointer to the ** internal state of the converter. Choose a converter from the enums below. ** The cb_data pointer can point to any data or be set to NULL. Whatever the ** value, when processing, user supplied function "func" gets called with ** cb_data as first parameter. */ SRC_STATE* src_callback_new (src_callback_t func, int converter_type, int channels, int *error, void* cb_data) ; /* ** Cleanup all internal allocations. ** Always returns NULL. */ SRC_STATE* src_delete (SRC_STATE *state) ; /* ** Standard processing function. ** Returns non zero on error. */ int src_process (SRC_STATE *state, SRC_DATA *data) ; /* ** Callback based processing function. Read up to frames worth of data from ** the converter int *data and return frames read or -1 on error. */ long src_callback_read (SRC_STATE *state, double src_ratio, long frames, float *data) ; /* ** Simple interface for performing a single conversion from input buffer to ** output buffer at a fixed conversion ratio. ** Simple interface does not require initialisation as it can only operate on ** a single buffer worth of audio. */ int src_simple (SRC_DATA *data, int converter_type, int channels) ; /* ** This library contains a number of different sample rate converters, ** numbered 0 through N. ** ** Return a string giving either a name or a more full description of each ** sample rate converter or NULL if no sample rate converter exists for ** the given value. The converters are sequentially numbered from 0 to N. */ const char *src_get_name (int converter_type) ; const char *src_get_description (int converter_type) ; const char *src_get_version (void) ; /* ** Set a new SRC ratio. This allows step responses ** in the conversion ratio. ** Returns non zero on error. */ int src_set_ratio (SRC_STATE *state, double new_ratio) ; /* ** Reset the internal SRC state. ** Does not modify the quality settings. ** Does not free any memory allocations. ** Returns non zero on error. */ int src_reset (SRC_STATE *state) ; /* ** Return TRUE if ratio is a valid conversion ratio, FALSE ** otherwise. */ int src_is_valid_ratio (double ratio) ; /* ** Return an error number. */ int src_error (SRC_STATE *state) ; /* ** Convert the error number into a string. */ const char* src_strerror (int error) ; /* ** The following enums can be used to set the interpolator type ** using the function src_set_converter(). */ enum { SRC_SINC_BEST_QUALITY = 0, SRC_SINC_MEDIUM_QUALITY = 1, SRC_SINC_FASTEST = 2, SRC_ZERO_ORDER_HOLD = 3, SRC_LINEAR = 4 } ; /* ** Extra helper functions for converting from short to float and ** back again. */ void src_short_to_float_array (const short *in, float *out, int len) ; void src_float_to_short_array (const float *in, short *out, int len) ; #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ #endif /* SAMPLERATE_H */ /* ** Do not edit or modify anything in this comment block. ** The arch-tag line is a file identity tag for the GNU Arch ** revision control system. ** ** arch-tag: 5421ef3e-c898-4ec3-8671-ea03d943ee00 */ frotz-2.44/src/sdl/sf_frotz.h0000644000175000017500000001403012527045477013121 00000000000000#ifndef _SF_FROTZ_H #define _SF_FROTZ_H #include "../common/frotz.h" #include "../blorb/blorb.h" // version info #define SFROTZ_MAJOR 0 #define SFROTZ_MINOR 2 // typedef unsigned char byte; // typedef unsigned short word; // typedef unsigned long ulong; #include typedef uint8_t byte; typedef uint16_t word; // typedef uint32_t ulong; #define ulong uint32_t typedef struct { bb_result_t bbres; ulong type; FILE *file; } myresource; int sf_getresource( int num, int ispic, int method, myresource * res); void sf_freeresource( myresource *res); #ifndef true #define true 1 #endif #ifndef false #define false 0 #endif #define NON_STD_COLS 238 // thiss assumes RGBA with lsb = R static inline ulong RGB5ToTrue( word w) { int _r = w & 0x001F; int _g = (w & 0x03E0)>>5; int _b = (w & 0x7C00)>>10; _r = (_r<<3) | (_r>>2); _g = (_g<<3) | (_g>>2); _b = (_b<<3) | (_b>>2); return (ulong) ( _r | (_g<<8) | (_b<<16)); } static inline word TrueToRGB5( ulong u) { return (word)(((u >> 3) & 0x001f) | ((u >> 6) & 0x03e0) | ((u >> 9) & 0x7c00)); } void reset_memory(void); void replay_close(void); void set_header_extension (int entry, zword val); int colour_in_use(zword colour); // various data extern bool m_tandy; extern bool m_quetzal; // CRect m_wndSize; // CString m_propFontName; // CString m_fixedFontName; // int m_fontSize; extern int m_v6scale; extern int m_gfxScale; extern ulong m_defaultFore; extern ulong m_defaultBack; extern ulong m_colours[11]; extern ulong m_nonStdColours[NON_STD_COLS]; extern int m_nonStdIndex; extern bool m_exitPause; extern bool m_lineInput; //extern bool m_IsInfocomV6; // bool m_fastScroll; extern bool m_morePrompts; // int m_leftMargin; // int m_rightMargin; // FILE* m_blorbFile; // bb_map_t* m_blorbMap; // GameInfo m_gameInfo; extern int AcWidth; extern int AcHeight; extern int m_random_seed; extern int m_fullscreen; extern int m_reqW, m_reqH; extern char * m_fontfiles[8]; extern bool m_localfiles; extern int m_no_sound; extern int m_vga_fonts; extern int SFdticks; extern volatile bool SFticked; extern char * m_fontdir; extern bool m_aafonts; extern char * m_setupfile; extern int m_frequency; extern double m_gamma; // sf_resource.c // must be called as soon as possible (i.e. by os_process_arguments()) int sf_load_resources( char *givenfn); typedef struct { int number; // 0 means unallocated int width, height; byte *pixels; } sf_picture; #define DEFAULT_GAMMA 2.2 void sf_setgamma( double gamma); //int sf_loadpic( int num, sf_picture *gfx); // get pointer from cache sf_picture * sf_getpic( int num); void sf_flushtext(); // glyph typedef struct { byte dx; byte w, h; char xof, yof; byte bitmap[0]; } SF_glyph; typedef struct sfontstruct SFONT; extern SFONT * (*ttfontloader)( char *fspec, int *err); extern void (*ttfontsdone)(); struct sfontstruct { int refcount; void (*destroy)(SFONT *); int (*height)(SFONT *); int (*ascent)(SFONT *); int (*descent)(SFONT *); int (*minchar)(SFONT *); int (*maxchar)(SFONT *); int (*hasglyph)(SFONT *,word,int); SF_glyph *(*getglyph)(SFONT *,word,int); int antialiased; void *data; }; typedef struct { SFONT *font; int proportional; int style, zfontnum; int cx, cy; // cursor position - 0 based int oh; // overhang unsigned long fore, back; bool foreDefault, backDefault, backTransparent; } SF_textsetting; SF_textsetting * sf_curtextsetting(); void sf_writeglyph( SF_glyph *g); void sf_fillrect( unsigned long color, int x, int y, int w, int h); int sf_GetProfileInt( const char *sect, const char *id, int def); double sf_GetProfileDouble( const char *sect, const char *id, double def); char * sf_GetProfileString( const char *sect, const char *id, char * def); void sf_readsettings(); ulong sf_GetColour( int colour); ulong sf_GetDefaultColour(bool fore); int sf_GetColourIndex( ulong colour); void sf_initvideo( int w, int h, int full); int sf_initsound(); void sf_cleanup_all(); void sf_regcleanfunc( void *f, const char *nam); #define CLEANREG( f) sf_regcleanfunc( (void *)f, #f) const char * sf_msgstring( int id); // consts for msg ids enum { IDS_BLORB_GLULX, IDS_BLORB_NOEXEC, IDS_MORE, IDS_HIT_KEY_EXIT, IDS_TITLE, IDS_FATAL, IDS_FROTZ, IDS_FAIL_DIRECTSOUND, IDS_FAIL_MODPLUG, IDS_ABOUT_INFO, IDS_SAVE_FILTER, IDS_SAVE_TITLE, IDS_RESTORE_TITLE, IDS_SCRIPT_FILTER, IDS_SCRIPT_TITLE, IDS_RECORD_FILTER, IDS_RECORD_TITLE, IDS_PLAYBACK_TITLE, IDS_AUX_FILTER, IDS_SAVE_AUX_TITLE, IDS_LOAD_AUX_TITLE }; bool sf_IsInfocomV6(); ulong sf_blend( int a, ulong s, ulong d); void sf_sleep( int millisecs); unsigned long sf_ticks (void); void sf_DrawInput(zchar * buffer, int pos, int ptx, int pty, int width, bool cursor); int sf_aiffwav( FILE *f, int foffs, void ** wav, int *size); int sf_pkread( FILE *f, int foffs, void ** out, int *size); ulong * sf_savearea( int x, int y, int w, int h); void sf_restoreareaandfree( ulong *s); #define SF_NOTIMP (-9999) zword sf_read_key( int timeout, int cursor, int allowed); int sf_user_fdialog( bool exist, const char *def, const char *filt, const char *title, char **res); extern int (*sf_osdialog)( bool ex, const char *def, const char *filt, const char *tit, char **res, ulong *sbuf, int sbp, int ew, int eh, int isfull); #ifdef WIN32 #define OS_PATHSEP ';' #else #define OS_PATHSEP ':' #endif // virtual keys #define VK_TAB 0x16 #define VK_INS 0x17 // for AIFF resampling typedef struct CONVstruct CONV; struct CONVstruct { double ratio; // input int channels; int bytespersam; // returns num of output samples int (* doCONV)( CONV *, FILE *, void *, int, int ); void (* finishCONV)( CONV *); int maxin, maxout; float *inbuf, *outbuf; void *aux; }; #endif /*** screen window ***/ /* typedef struct { zword y_pos; zword x_pos; zword y_size; zword x_size; zword y_cursor; zword x_cursor; zword left; zword right; zword nl_routine; zword nl_countdown; zword style; zword colour; zword font; zword font_size; zword attribute; zword line_count; zword true_fore; zword true_back; } Zwindow; */ frotz-2.44/src/sdl/sf_aiffwav.c0000644000175000017500000001656612527045477013413 00000000000000// sf_aiffwav.c // // Functions to read an AIFF file and convert (in memory) to WAV format, // as libmikmod only reads WAV samples // #include #include #include #include #include "sf_frotz.h" #ifndef HUGE_INT32 #define HUGE_INT32 0x7fffffff #endif /* HUGE_VAL */ #ifndef Uint32 #define Uint32 unsigned int #endif /**************************************************************** * Extended precision IEEE floating-point conversion routine. ****************************************************************/ static int IeeeExtendedToLong( unsigned char *bytes){ int f = 0, expon; Uint32 hiMant, loMant; expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); hiMant = ((Uint32) (bytes[2] & 0xFF) << 24) | ((Uint32) (bytes[3] & 0xFF) << 16) | ((Uint32) (bytes[4] & 0xFF) << 8) | ((Uint32) (bytes[5] & 0xFF)); loMant = ((Uint32) (bytes[6] & 0xFF) << 24) | ((Uint32) (bytes[7] & 0xFF) << 16) | ((Uint32) (bytes[8] & 0xFF) << 8) | ((Uint32) (bytes[9] & 0xFF)); if (expon == 0 && hiMant == 0 && loMant == 0) f = 0; else if (expon == 0x7FFF) f = HUGE_INT32; else { expon -= 16382; expon = 32-expon; if (expon < 0) f = HUGE_INT32; else f = hiMant >> expon; } if (bytes[0] & 0x80) return -f; else return f; } static void writeRIFFheader( unsigned char *f, int freq, int numsamples, int numchannels, int bytespersample); // returns 1 if resampling, 0 if not needed, negative if error static int initCONV( CONV *conv, double ratio, int ncha, int bytespersam, int maxinp); static void finishCONV( CONV *conv); #define BLOCKSIZE 1024 // hack to leave resampling to mixer in simpler cases #define HACKING 0 static int hack( double ratio) { int m; double diff; if (HACKING == 0) return 0; if (ratio > 0.9) { m = ratio + 0.499; // nint diff = fabs(ratio - (double)m)/ratio; if (diff < 0.01) return m; } else { m = 1.0/ratio + 0.499; diff = ratio*fabs(1.0/ratio - (double)m); if (diff < 0.01) return (-m); } return 0; } #define MAXCHAN 8 // size of WAV header #define HEADERSIZE 44 #define NPAD 30 #define glong( b) (((int)((b)[0]) << 24) + ((int)((b)[1]) << 16) +\ ((int)((b)[2]) << 8) + (int)((b)[3])) #define gshort( b) (((int)((b)[0]) << 8) + (int)((b)[1])) int sf_aiffwav( FILE *f, int foffs, void ** wav, int *length){ unsigned char chk[18], fid[4]; int len, bps=0, offs, bksize, size; int samples, frequency, channels, found = 0; int bytespersample, reqsize; int noutsam=0, noutreqsam, bytesneeded, outbytespersample=2, mhack; double ratio, cratio; CONV conv; int needCONV; unsigned char *bout = NULL; if (!f) return -20; if (!wav) return -21; fseek(f,foffs,0); samples = 0; frequency = 0; channels = 0; if (fread(chk,1,4,f) < 4) return -1; // FORM if (memcmp(chk,"FORM",4)) return -3; if (fread(chk,1,4,f) < 4) return -1; // size size = glong(chk); if (size & 1) size++; if (size < 20) return -99; if (fread(chk,1,4,f) < 4) return -1; // AIFF if (memcmp(chk,"AIFF",4)) return -3; size -= 4; while (size > 8){ if (fread(fid,1,4,f) < 4) return -1; // chunck id if (fread(chk,1,4,f) < 4) return -1; // and len size -= 8; len = glong(chk); if (len < 0) return -98; if (len & 1) len++; size -= len; if (size < 0) return -97; if (memcmp(fid,"COMM",4)==0){ if (len != 18) return -5; if (fread(chk,1,18,f) < 18) return -1; channels = gshort(chk); if (channels < 1) return -9; if (channels > MAXCHAN) return -9; samples = glong(chk+2); if (samples < 1) return -9; frequency = IeeeExtendedToLong(chk+8); if (frequency <= 0) return -54; bps = gshort(chk+6); if (bps < 1 || bps > 16) return -51; // data = malloc(samples*sizeof(short)); // if (!data) return -55; } else if (memcmp(fid,"SSND",4)==0){ if (!channels) return -11; if (fread(chk,1,4,f) < 4) return -1; offs = glong(chk); if (fread(chk,1,4,f) < 4) return -1; bksize = glong(chk); if (bksize) return -77; if (offs) fseek(f,offs,SEEK_CUR); found = 1; break; } else fseek(f,len,SEEK_CUR); } if (!found) return -69; if (!channels) return -66; // her should check freq ratio = (double)m_frequency / (double)frequency; mhack = hack(ratio); cratio = ratio; if (mhack) { cratio = 1.0; if (mhack > 0) frequency = m_frequency/mhack; else frequency = m_frequency * (-mhack); } noutreqsam = cratio*samples + 0.49; bytesneeded = HEADERSIZE + noutreqsam*channels*outbytespersample; bout = malloc(bytesneeded); if (!bout) return -55; *wav = (void *)bout; bout += HEADERSIZE; noutsam = 0; if ((needCONV = initCONV(&conv,cratio,channels,(bps+7)/8,BLOCKSIZE)) < 0) { free(bout); *wav = NULL; return -997; } while (samples) { int nrd = BLOCKSIZE, nwr; if (nrd > samples) nrd = samples; nwr = conv.doCONV( &conv, f, bout, nrd, nrd == samples); samples -= nrd; noutsam += nwr; bout += nwr*channels*outbytespersample; } conv.finishCONV(&conv); //printf("AIFF %d, req %d\n",frequency,m_frequency); // write RIFF header if (needCONV) frequency = m_frequency; writeRIFFheader( *wav, frequency, noutsam, channels, outbytespersample); *length = HEADERSIZE + noutsam*channels*outbytespersample; /*{ FILE *f; f = fopen("_TMP","w"); fwrite(*wav,1,*length,f); fclose(f); }*/ return 0; } // returns num of output samples static int NOCONV( CONV *conv, FILE *f, void *dest, int nin, int eod) { int nbrd, i, j, v=0; short *sdata = (short *)dest; unsigned char c1, c2; char c; nbrd = conv->bytespersam;; switch (conv->bytespersam) { case 1: for (i=0;ichannels;i++) { c = fgetc(f); *sdata++ = ((short)c) << 8; } break; case 2: for (i=0;ichannels;i++) { c1 = fgetc(f); c2 = fgetc(f); *sdata++ = (short)(((unsigned short)c1) << 8 + (unsigned short)c2); } } return nin; } static void finishCONV( CONV *conv) { if (conv->inbuf) free(conv->inbuf); if (conv->outbuf) free(conv->outbuf); conv->inbuf = conv->outbuf = NULL; } // needs a pre-initialized CONV int (*sfx_exinitconv)( CONV *conv) = NULL; static int initCONV( CONV *conv, double ratio, int ncha, int bytespersam, int maxinp) { int doconv; conv->ratio = ratio; conv->channels = ncha; conv->bytespersam = bytespersam; conv->maxin = maxinp; conv->maxout = ratio*maxinp+5; // a little margin doconv = ((ratio != 1.0) && (sfx_exinitconv != NULL)); conv->inbuf = conv->outbuf = NULL; if (doconv == 0) { conv->doCONV = NOCONV; conv->finishCONV = finishCONV; } else { doconv = sfx_exinitconv(conv); } return doconv; } #define PCwrite4( f, v) \ *f++ = v & 0xff;\ *f++ = (v >> 8) & 0xff;\ *f++ = (v >> 16) & 0xff;\ *f++ = v >> 24; #define PCwrite2( f, v) \ *f++ = v & 0xff;\ *f++ = (v >> 8) & 0xff; static void writeRIFFheader( unsigned char *f, int freq, int numsamples, int numchannels, int bytespersample){ // write header // offset 0 memcpy(f,"RIFF",4); f += 4; // offset 4 PCwrite4(f,(36+numsamples*numchannels*bytespersample)); // offset 8 memcpy(f,"WAVEfmt ",8); f += 8; // offset 16 PCwrite4(f,16); // offset 20 PCwrite2(f,1); // PCM // offset 22 PCwrite2(f,numchannels); // nchannels // offset 24 PCwrite4(f,freq); // offset 28 PCwrite4(f,(freq*numchannels*bytespersample)); // offset 32 PCwrite2(f,(numchannels*bytespersample)); // offset 34 PCwrite2(f,(8*bytespersample)); // offset 36 memcpy(f,"data",4); f += 4; // offset 40 PCwrite4(f,(numsamples*numchannels*bytespersample)); // offset 44 } frotz-2.44/src/sdl/sf_util.c0000644000175000017500000004661012527045477012736 00000000000000#include #include #include #include #include #ifdef __WIN32__ #include #endif #include "sf_frotz.h" typedef void (*CLEANFUNC)(); typedef struct cfstruct cfrec; struct cfstruct { CLEANFUNC func; cfrec *next; const char *name; }; static cfrec *cflist = NULL; void sf_regcleanfunc( void *f, const char *p){ cfrec *n = calloc(1,sizeof(cfrec)); if (n) { if (!p) p = ""; n->func = (CLEANFUNC) f; n->name = p; n->next = cflist; cflist = n; } } void sf_cleanup_all() { while (cflist) { cfrec *n = cflist->next; //printf("cleanup c%p [%s] n%p\n",cflist,cflist->name,n); if (cflist->func) cflist->func(); free(cflist); cflist = n; } //printf("Cleanup done.\n"); } /* * os_reset_screen * * Reset the screen before the program ends. * */ void os_reset_screen(void) { sf_flushdisplay(); // theWnd->FlushDisplay(); // theWnd->ResetOverhang(); if (m_exitPause) { const char *hit = sf_msgstring(IDS_HIT_KEY_EXIT); os_set_font(TEXT_FONT); os_set_text_style(0); screen_new_line(); while (*hit) os_display_char((*hit++)); os_read_key(0,1); } sf_cleanup_all(); } int user_background = -1; int user_foreground = -1; int user_emphasis = -1; int user_bold_typing = -1; int user_reverse_bg = -1; int user_reverse_fg = -1; int user_screen_height = -1; int user_screen_width = -1; int user_tandy_bit = -1; //int user_random_seed = -1; int user_font = 1; int m_random_seed = -1; int m_fullscreen = -1; int m_reqW = 0, m_reqH = 0; int m_vga_fonts = 0; extern char * m_setupfile; extern char m_names_format; static char user_names_format = 0; extern char *m_reslist_file; extern int option_scrollback_buffer; static char *info1 = "\n" "SDL Frotz V%d.%02d build %s - interpreter for z-code games.\n" "Complies with Standard 1.0; supports Blorb resources and Quetzal save files.\n" "Based on Frotz 2.40 by Stefan Jokisch and WindowsFrotz2000 by David Kinder.\n" "\n" "Syntax: sfrotz [options] story-file\n\n"; static char *infos[] = { "-a watch attribute setting", "-A watch attribute testing", "-b # background colour", "-c # context lines", "-f # foreground colour", "-F fullscreen mode", "-h # screen height", "-i ignore runtime errors", "-l # left margin", "-L use local resources", "-o watch object movement", "-O watch object locating", "-p alter piracy opcode", "-q quiet (disable sound)", "-r # right margin", "-R save/restore in old Frotz format", "-s # random number seed value", "-S # transcript width", "-t set Tandy bit", "-u # slots for multiple undo", "-w # screen width", "-x expand abbreviations g/x/z", "-V force VGA fonts", "-Z # error checking (see below)", NULL}; static char *info2 = "\nError checking: 0 none, 1 first only (default), 2 all, 3 exit after any error.\n" "For more options and explanations, please read the HTML manual.\n"; static char * getbuilddatetime( int tf); #define WIDCOL 40 static void usage() { char **p = infos; int i=0,len=0; printf(info1,SFROTZ_MAJOR,SFROTZ_MINOR,getbuilddatetime(1)); while (*p) { if (i) { while (len > 0){ fputc(' ',stdout); len--;} puts(*p); } else { fputs(" ",stdout); fputs(*p,stdout); len = WIDCOL-strlen(*p)-2; } i = 1-i; p++; } if (i) fputc('\n',stdout); puts (info2); } /* * parse_options * * Parse program options and set global flags accordingly. * */ static const char *progname = NULL; extern char script_name[]; extern char command_name[]; extern char save_name[]; extern char auxilary_name[]; char stripped_story_name[100]; extern char *optarg; extern int optind; extern int m_timerinterval; static char *options = "@:aAb:B:c:D:f:Fh:iI:l:Lm:N:oOpqr:Rs:S:tTu:Vw:xZ:"; static int limit( int v, int m, int M) { if (v < m) return m; if (v > M) return M; return v; } static void parse_options (int argc, char **argv) { int c; do { int num = 0, copt = 0;; c = getopt (argc, argv, options); if (optarg != NULL) { num = atoi (optarg); copt = optarg[0]; } if (c == 'a') f_setup.attribute_assignment = 1; if (c == 'A') f_setup.attribute_testing = 1; if (c == 'b') user_background = num; if (c == 'B') option_scrollback_buffer = num; if (c == 'c') f_setup.context_lines = num; if (c == 'D') { if (copt == 'k') m_reqW = -1; else sscanf(optarg,"%dx%d",&m_reqW,&m_reqH); m_fullscreen = 1; } if (c == 'm') m_timerinterval = limit(num,10,1000000); if (c == 'N') user_names_format = copt; if (c == '@') m_reslist_file = optarg; if (c == 'I') m_setupfile = optarg; if (c == 'f') user_foreground = num; if (c == 'F') m_fullscreen = 1; if (c == 'h') user_screen_height = num; if (c == 'i') f_setup.ignore_errors = 1; if (c == 'l') f_setup.left_margin = num; if (c == 'L') m_localfiles = true; if (c == 'q') m_no_sound = 1; if (c == 'o') f_setup.object_movement = 1; if (c == 'O') f_setup.object_locating = 1; if (c == 'p') f_setup.piracy = 1; if (c == 'r') f_setup.right_margin = num; if (c == 'R') f_setup.save_quetzal = 0; if (c == 's') m_random_seed = num; if (c == 'S') f_setup.script_cols = num; if (c == 't') user_tandy_bit = 1; if (c == 'T') sf_osdialog = NULL; if (c == 'u') f_setup.undo_slots = num; if (c == 'V') m_vga_fonts = 1; if (c == 'w') user_screen_width = num; if (c == 'x') f_setup.expand_abbreviations = 1; if (c == 'Z') if (num >= ERR_REPORT_NEVER && num <= ERR_REPORT_FATAL) f_setup.err_report_mode = num; if (c == '?') optind = argc; } while (c != EOF && c != '?'); }/* parse_options */ /* * os_process_arguments * * Handle command line switches. Some variables may be set to activate * special features of Frotz: * * option_attribute_assignment * option_attribute_testing * option_context_lines * option_object_locating * option_object_movement * option_left_margin * option_right_margin * option_ignore_errors * option_piracy * option_undo_slots * option_expand_abbreviations * option_script_cols * * The global pointer "story_name" is set to the story file name. * */ void os_process_arguments (int argc, char *argv[]) { const char *p; int i; // install signal handlers sf_installhandlers(); /* Parse command line options */ parse_options(argc, argv); if (optind != argc - 1) { usage(); exit (EXIT_FAILURE); } /* Set the story file name */ story_name = argv[optind]; // load resources // it's useless to test the retval, as in case of error it does not return sf_load_resources( story_name); /* Strip path and extension off the story file name */ p = story_name; for (i = 0; story_name[i] != 0; i++) if (story_name[i] == '\\' || story_name[i] == '/' || story_name[i] == ':') p = story_name + i + 1; for (i = 0; p[i] != 0 && p[i] != '.'; i++) stripped_story_name[i] = p[i]; stripped_story_name[i] = 0; /* Create nice default file names */ strcpy (script_name, stripped_story_name); strcpy (command_name, stripped_story_name); strcpy (save_name, stripped_story_name); strcpy (auxilary_name, stripped_story_name); strcat (script_name, ".scr"); strcat (command_name, ".rec"); strcat (save_name, ".sav"); strcat (auxilary_name, ".aux"); /* Save the executable file name */ progname = argv[0]; sf_readsettings(); if (user_screen_width > 0) AcWidth = user_screen_width; if (user_screen_height > 0) AcHeight = user_screen_height; if (user_names_format) m_names_format = user_names_format; if (user_background != -1) m_defaultBack = sf_GetColour(user_background); if (user_foreground != -1) m_defaultFore = sf_GetColour(user_foreground); if (user_tandy_bit != -1) m_tandy = user_tandy_bit; sf_initfonts(); }/* os_process_arguments */ #ifdef WIN32 #include #else #include #include #endif #ifdef WIN32 void sf_sleep( int msecs){ Sleep(msecs); } unsigned long sf_ticks( void){ return (GetTickCount()); } #else //#include void sf_sleep( int msecs){ usleep(msecs/1000); } unsigned long sf_ticks (void) { struct timeval now; static struct timeval start; static int started = 0; unsigned long ticks; now.tv_sec = now.tv_usec = 0; gettimeofday(&now, NULL); if (!started){ started = 1; start = now; } ticks = (now.tv_sec-start.tv_sec)*1000 + (now.tv_usec-start.tv_usec)/1000; // ticks = now.tv_sec*1000 + now.tv_usec/1000; return ticks; } #endif /* * os_read_file_name * * Return the name of a file. Flag can be one of: * * FILE_SAVE - Save game file * FILE_RESTORE - Restore game file * FILE_SCRIPT - Transscript file * FILE_RECORD - Command file for recording * FILE_PLAYBACK - Command file for playback * FILE_SAVE_AUX - Save auxilary ("preferred settings") file * FILE_LOAD_AUX - Load auxilary ("preferred settings") file * * The length of the file name is limited by MAX_FILE_NAME. Ideally * an interpreter should open a file requester to ask for the file * name. If it is unable to do that then this function should call * print_string and read_string to ask for a file name. * */ extern char stripped_story_name[]; static char *getextension( int flag) { char *ext = ".aux"; if (flag == FILE_SAVE || flag == FILE_RESTORE) ext = ".sav"; else if (flag == FILE_SCRIPT) ext = ".scr"; else if (flag == FILE_RECORD || flag == FILE_PLAYBACK) ext = ".rec"; return ext; } static bool newfile( int flag) { if (flag == FILE_SAVE || flag == FILE_SAVE_AUX || flag == FILE_RECORD) return true; return false; } static char buf[FILENAME_MAX]; static const char *getnumbername( const char *def, char *ext) { int len, number = 0; strcpy(buf,stripped_story_name); len = strlen(buf); for (;;){ sprintf(buf+len,"%03d%s",number++,ext); if (access(buf,F_OK)) break; } return buf; } static const char *getdatename( const char *def, char *ext) { char *p; time_t t; struct tm *tm; time(&t); tm = localtime(&t); strcpy(buf,stripped_story_name); p = buf + 1; if (*p) p++; sprintf(p,"%04d%02d%02d%02d%02d%s", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, ext); return buf; } // fdialog( existing, defname, filter, title, &resultstr) static int ingame_read_file_name (char *file_name, const char *default_name, int flag); static int dialog_read_file_name (char *file_name, const char *default_name, int flag); int os_read_file_name (char *file_name, const char *default_name, int flag) { int st; const char *initname = default_name; char *ext = ".aux"; if (newfile(flag)) { char *ext = getextension(flag); if (m_names_format == 'd') initname = getdatename(initname,ext); else if (m_names_format == 'n') initname = getnumbername(initname,ext); } st = dialog_read_file_name( file_name, initname, flag); if (st == SF_NOTIMP) st = ingame_read_file_name( file_name, initname, flag); return st; } static int ingame_read_file_name (char *file_name, const char *default_name, int flag) { char *extension; FILE *fp; bool terminal; bool result; bool saved_replay = istream_replay; bool saved_record = ostream_record; /* Turn off playback and recording temporarily */ istream_replay = FALSE; ostream_record = FALSE; /* Select appropriate extension */ extension = getextension(flag); /* Input file name (reserve four bytes for a file name extension) */ print_string ("Enter file name (\""); print_string (extension); print_string ("\" will be added).\nDefault is \""); print_string (default_name); print_string ("\": "); read_string (MAX_FILE_NAME - 4, (byte *) file_name); /* Use the default name if nothing was typed */ if (file_name[0] == 0) strcpy (file_name, default_name); if (strchr (file_name, '.') == NULL) strcat (file_name, extension); /* Make sure it is safe to use this file name */ result = TRUE; /* OK if the file is opened for reading */ if (!newfile(flag)) goto finished; /* OK if the file does not exist */ if ((fp = fopen (file_name, "rb")) == NULL) goto finished; /* OK if this is a pseudo-file (like PRN, CON, NUL) */ terminal = isatty(fileno(fp)); fclose (fp); if (terminal) goto finished; /* OK if user wants to overwrite */ result = read_yes_or_no ("Overwrite existing file"); finished: /* Restore state of playback and recording */ istream_replay = saved_replay; ostream_record = saved_record; return result; }/* os_read_file_name */ static int dialog_read_file_name(char *file_name, const char *default_name, int flag) { int filter = 0; int title = 0, st; char *res; sf_flushdisplay(); // theWnd->ResetOverhang(); switch (flag) { case FILE_SAVE: filter = IDS_SAVE_FILTER; title = IDS_SAVE_TITLE; break; case FILE_RESTORE: filter = IDS_SAVE_FILTER; title = IDS_RESTORE_TITLE; break; case FILE_SCRIPT: filter = IDS_SCRIPT_FILTER; title = IDS_SCRIPT_TITLE; break; case FILE_RECORD: filter = IDS_RECORD_FILTER; title = IDS_RECORD_TITLE; break; case FILE_PLAYBACK: filter = IDS_RECORD_FILTER; title = IDS_PLAYBACK_TITLE; break; case FILE_SAVE_AUX: filter = IDS_AUX_FILTER; title = IDS_SAVE_AUX_TITLE; break; case FILE_LOAD_AUX: filter = IDS_AUX_FILTER; title = IDS_LOAD_AUX_TITLE; break; default: return 0; } // fdialog( existing, defname, filter, title, &resultstr) // returns 0 if OK st = sf_user_fdialog( !newfile(flag), default_name, sf_msgstring(filter), sf_msgstring(title), &res); if (st == SF_NOTIMP) return st; if (st == 0) { strncpy(file_name,res,MAX_FILE_NAME); file_name[MAX_FILE_NAME-1] = 0; return 1; } return 0; } typedef struct { void *link; char *str; } Dynstr; static Dynstr * strings = NULL; static void freestrings() { while (strings) { Dynstr *r = strings->link; if (strings->str) free(strings->str); free(strings); strings = r; } } static char *mystrdup( char *p) { Dynstr *r; if (!p) return p; p = strdup(p); if (!p) return p; r = calloc(1,sizeof(Dynstr)); if (r) { if (!strings) CLEANREG(freestrings); r->link = strings; r->str = p; strings = r; } return p; } static char *rc = NULL; void sf_FinishProfile() { //printf("finishprofile\n"); if (!rc) return; free(rc); rc = NULL; } void sf_InitProfile( const char *fn) { FILE *f; int size; char *s, *d; if (!fn) return; f = fopen(fn,"rb"); if (!f) return; fseek(f,0,SEEK_END); size = ftell(f); if (!size) { fclose(f); return;} rc = malloc(size+1); if (!rc) { fclose(f); return;} fseek(f,0,0); fread(rc,1,size,f); fclose(f); rc[size] = 0; s = d = rc; while (*s) { if (*s == '#') { while ((*s) && (*s != '\n')) s++; if (!*s) break; } else *d++ = *s++; } *d = 0; CLEANREG(sf_FinishProfile); } static char * findsect( const char *sect) { int ns = strlen(sect); char *r = rc; while (r) { //printf("{%s}\n",r); r = strchr(r,'['); if (!r) return NULL; r++; if (strncmp(r,sect,ns)) continue; return (r+ns); } } static char * findid( const char *sect, const char *id) { int nid = strlen(id); char *p, *r, *sav, *rq, *fnd = NULL; r = findsect(sect); //printf("findsect(%s) %p\n",sect,r); if (!r) return NULL; sav = strchr(r,'['); if (sav) *sav = 0; while (r) { r = strstr(r,id); if (!r) break; rq = r+nid; if ((*(byte *)(r-1) <= ' ') && ((*rq == ' ') || (*rq == '='))) { while (*rq) if (*rq++ == '=') break; if (*rq) { fnd = rq; break;} } r = rq; } if (sav) *sav = '['; return fnd; } int sf_GetProfileInt( const char *sect, const char *id, int def) { if (rc) { char *p = findid(sect,id); if (p) def = atoi(p); } return def; } double sf_GetProfileDouble( const char *sect, const char *id, double def) { if (rc) { char *p = findid(sect,id); if (p) def = atof(p); } return def; } char * sf_GetProfileString( const char *sect, const char *id, char * def) { char *q=NULL, sav=0; if (rc) { char *p = findid(sect,id); //printf("findid(%s,%s) %p\n",sect,id,p); if (p) { int quoted = 0; while (*p) { if (*p == '\"') { quoted = 1; p++; break;} if ((byte)(*p) > ' ') break; } if (*p) { if (quoted) q = strchr(p,'\"'); if (!q) { q = p; while (*q > ' ') q++; sav = *q; *q = 0; } } def = p; } } if (def) def = mystrdup(def); if (sav) *q = sav; return def; } // A. Local file header: // // local file header signature 0 4 bytes (0x04034b50) // version needed to extract 4 2 bytes // general purpose bit flag 6 2 bytes // compression method 8 2 bytes // last mod file time 10 2 bytes // last mod file date 12 2 bytes // crc-32 14 4 bytes // compressed size 18 4 bytes // uncompressed size 22 4 bytes // file name length 26 2 bytes // extra field length 28 2 bytes // // file name (variable size) // extra field (variable size) #define plong( b) (((int)((b)[3]) << 24) + ((int)((b)[2]) << 16) +\ ((int)((b)[1]) << 8) + (int)((b)[0])) #define pshort( b) (((int)((b)[1]) << 8) + (int)((b)[0])) static int myunzip( int csize, byte *cdata, byte *udata) { byte window[32768]; z_stream z; int st; unsigned myin( void *d, byte **b){return 0;} int myout( void *d, byte *b, unsigned n) { memcpy(udata,b,n); udata += n; return 0; } memset(&z,0,sizeof(z)); st = inflateBackInit( &z, 15, window); if (st) return st; z.next_in = cdata; z.avail_in = csize; for (;;){ st = inflateBack( &z, myin, NULL, myout, NULL); if (st == Z_STREAM_END) break; if (st) return st; } st = inflateBackEnd(&z); return st; } int sf_pkread( FILE *f, int foffs, void ** out, int *size) { byte hd[30], *dest; byte *data, *cdata; int csize, usize, cmet, skip, st; fseek(f,foffs,SEEK_SET); fread(hd,1,30,f); cmet = pshort(hd+8); if (cmet != 8) return -10; csize = plong(hd+18); usize = plong(hd+22); if (csize <= 0) return -11; if (usize <= 0) return -12; data = malloc(usize); if (!data) return -13; cdata = malloc(csize); if (!cdata){ free(data); return -14;} skip = pshort(hd+26) + pshort(hd+28); fseek(f,foffs+30+skip,SEEK_SET); fread(cdata,1,csize,f); //printf("%02x csize %d usize %d skip %d\n",cdata[0],csize,usize,skip); st = myunzip(csize,cdata,data); free(cdata); if (st) { free(data); return st; } *out = (void *)data; *size = usize; return st; } ////////////////////////// #include #include #include #include #include #ifdef WIN32 #include #else #include #endif static char * getexepath( char *buf){ #ifdef WIN32 buf[0] = 0; GetModuleFileName(NULL,buf,262); #else char baf[80]; int n; sprintf(baf,"/proc/%d/exe",getpid()); n = readlink(baf,buf,262); if (n < 0) n = 0; buf[n] = 0; #endif return buf; } #ifndef WIN32 #define _stat stat #endif static char * getbuilddatetime( int tf){ time_t t; struct tm *tm; struct _stat sta; static char buf[263]; getexepath(buf); _stat(buf,&sta); t = sta.st_mtime; tm = localtime(&t); buf[0] = 0; sprintf(buf,"%04d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday); if (tf){ strcat(buf,"."); sprintf(buf+strlen(buf),"%02d%02d%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); } return buf; } frotz-2.44/src/sdl/sf_msg_en.c0000644000175000017500000000521512527045477013225 00000000000000#include "sf_frotz.h" const char * sf_msgstring( int id) { const char *p = ""; switch (id) { case IDS_BLORB_GLULX: p = "unsupported Glulx code\n\nYou have attempted to load a Blorb file containing a Glulx game.\n" "For this game you need a Glulx interpreter instead."; break; case IDS_BLORB_NOEXEC: p = "no Z code\n\nYou have attempted to load a Blorb file that does not contain any\n" "recognized game data. It may be a Blorb file containing just graphics or\n" "sound data for a game, with the game in a separate file.\nCheck for a " "file with the same name but an extension of .z5, .z6 or .z8\nand try " "loading that into Frotz instead."; break; case IDS_MORE: p = "[More]"; break; case IDS_HIT_KEY_EXIT: p = "[Hit any key to exit.]"; break; case IDS_TITLE: p = "SDL Frotz"; break; case IDS_FATAL: p = "Frotz Fatal Error"; break; case IDS_FROTZ: p = "Frotz"; break; case IDS_FAIL_DIRECTSOUND: p = "Failed to initialize DirectSound"; break; case IDS_FAIL_MODPLUG: p = "Failed to initialize MODPlug"; break; case IDS_ABOUT_INFO: p = "{\\rtf1\\ansi{\\b Windows Frotz 1.10, written by David Kinder.\\line Another fine product of the Frobozz Magic Z-code Interpreter Company.}{\\line\\super{ }\\par}Windows Frotz is released under the terms of the GNU General Public License. See the file COPYING that is included with this program for details.{\\line\\super{ }\\par}Windows Frotz copyright David Kinder 2002-2006.\\line Frotz copyright Stefan Jokisch 1995-1997.{\\line\\super{ }\\par}Frotz was written by Stefan Jokisch, with additions by Jim Dunleavy and David Griffith. Windows Frotz uses jpeglib by the Independent JPEG Group; libpng by Guy Eric Schalnat, Andreas Dilger, Glenn Randers-Pehrson, and others; zlib by Jean-loup Gailly and Mark Adler; ModPlug by Olivier Lapicque; and libogg and libvorbis by the Xiph.org Foundation.}"; break; case IDS_SAVE_TITLE: p = "Save the current game"; break; case IDS_RESTORE_TITLE: p = "Restore a saved game"; break; case IDS_LOAD_AUX_TITLE: p = "Load a portion of z-machine memory"; break; case IDS_SAVE_AUX_TITLE: p = "Save a portion of z-machine memory"; break; case IDS_AUX_FILTER: p = "*|Any file"; break; case IDS_SAVE_FILTER: p = "*.sav|Saved games"; break; case IDS_RECORD_TITLE: p = "Record input to a file"; break; case IDS_PLAYBACK_TITLE: p = "Play back recorded input"; break; case IDS_RECORD_FILTER: p = "*.rec|Record Files"; break; case IDS_SCRIPT_TITLE: p = "Write out a script"; break; case IDS_SCRIPT_FILTER: p = "*.log|Transcript Log Files"; break; default: break; } return p; } frotz-2.44/src/sdl/sf_video.c0000644000175000017500000006726112527045477013074 00000000000000#include #include #include //#include //#define STATIC #include #include "sf_frotz.h" static SDL_Rect blitrect = {0,0,0,0}; static char banner[256]; static int isfullscreen; static ulong *sbuffer = NULL; static int sbpitch; // in longs static int dirty = 0; static int bitsperpixel = 32; static int RBswap = 0; static int ewidth, eheight; static SDL_Surface *screen, *off = NULL; static int mustlockscreen = 0; int m_timerinterval = 100; static void sf_quitconf(); // clipping region static int xmin,xmax,ymin,ymax; void sf_setclip( int x, int y, int w, int h) { if (x < 0){ w += x; x = 0;} if (x+w > ewidth) w = ewidth-x; if (y < 0){ h += y; y = 0;} if (y+h > eheight) h = eheight-y; xmin = x; xmax = x+w; ymin = y; ymax = y+h; } void sf_getclip( int *x, int *y, int *w, int *h) { *x = xmin; *y = ymin; *w = xmax-xmin; *h = ymax-ymin; } static int mywcslen( zchar *b) { int n=0; while (*b++) n++; return n; } static void myGrefresh(){ if (off) { if (mustlockscreen) SDL_LockSurface(screen); SDL_BlitSurface(off,NULL,screen,&blitrect); if (mustlockscreen) SDL_UnlockSurface(screen); } SDL_UpdateRect(screen,0,0,0,0); } void sf_wpixel( int x, int y, ulong c) { if (x < xmin || x >= xmax || y < ymin || y >= ymax) return; sbuffer[x+sbpitch*y] = c; dirty = 1; } ulong sf_rpixel( int x, int y) { if (x < 0 || x >= ewidth || y < 0 || y >= eheight) return 0; return sbuffer[x+sbpitch*y]; } #define MAXCUR 64 static ulong savedcur[MAXCUR]; static void drawthecursor( int x, int y, int onoff) { SF_textsetting * ts = sf_curtextsetting(); int i, h = ts->font->height(ts->font); if (h > MAXCUR) h = MAXCUR; if (onoff) { for (i=0;ifore); } } else { for (i=0;i= ZC_ASCII_MIN && c <= ZC_ASCII_MAX) return true; if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX) return true; if (c >= 0x100) return true; return false; } void sf_drawcursor( bool c) { SF_textsetting * ts = sf_curtextsetting(); drawthecursor(ts->cx,ts->cy,c); } void sf_chline( int x, int y, ulong c, int n) { ulong *s; if (y < ymin || y >= ymax) return; if (x < xmin){ n += x-xmin; x = xmin;} if (x+n > xmax) n = xmax-x; if (n <= 0) return; s = sbuffer+x+sbpitch*y; while (n--) *s++ = c; dirty = 1; } void sf_cvline( int x, int y, ulong c, int n) { ulong *s; if (x < xmin || x >= xmax) return; if (y < xmin){ n += y-ymin; y = ymin;} if (y+n > ymax) n = ymax-y; if (n <= 0) return; s = sbuffer+x+sbpitch*y; while (n--) { *s = c; s += sbpitch;} dirty = 1; } ulong sf_blendlinear( int a, ulong s, ulong d){ ulong r; r = ((s & 0xff)*a + (d & 0xff)*(256-a))>>8; s >>= 8; d >>= 8; r |= (((s & 0xff)*a + (d & 0xff)*(256-a))>>8)<<8; s >>= 8; d >>= 8; r |= (((s & 0xff)*a + (d & 0xff)*(256-a))>>8)<<16; return r; } void sf_writeglyph( SF_glyph *g) { SF_textsetting *ts = sf_curtextsetting(); int i,j,m; int w = g->dx; int weff = g->xof+g->w; byte * bmp = (byte *)(&(g->bitmap[0])); int h = g->h; int nby = (g->w+7)/8; int byw = g->w; int x = ts->cx; int y = ts->cy; int dxpre = g->xof; int dypre = ts->font->ascent(ts->font)-h-(int)g->yof; int height = ts->font->height(ts->font); int width; ulong color, bc; if ((ts->style & REVERSE_STYLE) != 0) { bc = ts->fore; color = ts->back; } else { color = ts->fore; bc = ts->back; } // compute size and position of background rect if (weff < w) weff = w; width = weff - ts->oh; if ((width > 0) && (ts->backTransparent == 0)) sf_fillrect(bc,x+ts->oh,y,width,height); x += dxpre; y += dypre; //printf("\n"); for (i=0;ifont->antialiased) for (m=0;m>7)),sval,sf_rpixel(x+xx,y)); sf_wpixel( x+xx, y, sval); } } xx++; } else for (m=0;mcx += (w); ts->oh = (weff > w) ? weff-w : 0; } void sf_fillrect( unsigned long color, int x, int y, int w, int h) { ulong *dst; int i; //printf("fillrect %x %d %d %d %d\n",color,x,y,w,h); //printf("dst%p sbpitch%d\n",dst,sbpitch); if (x < xmin){ w += x-xmin; x = xmin;} if (x+w > xmax) w = xmax-x; if (w <= 0) return; if (y < ymin){ h += y-ymin; y = ymin;} if (y+h > ymax) h = ymax-y; if (h <= 0) return; dst = sbuffer+x+sbpitch*y; while (h--) { for (i=0;icx += ts->oh; ts->oh = 0; } /* * os_erase_area * * Fill a rectangular area of the screen with the current background * colour. Top left coordinates are (1,1). The cursor does not move. * * The final argument gives the window being changed, -1 if only a * portion of a window is being erased, or -2 if the whole screen is * being erased. * */ void os_erase_area(int top, int left, int bottom, int right, int win) { sf_flushtext(); sf_fillrect((sf_curtextsetting())->back,left-1,top-1,right-left+1,bottom-top+1); // theWnd->FillBackground(CRect(left-1,top-1,right,bottom)); } /* * os_peek_colour * * Return the colour of the screen unit below the cursor. (If the * interface uses a text mode, it may return the background colour * of the character at the cursor position instead.) This is used * when text is printed on top of pictures. Note that this coulor * need not be in the standard set of Z-machine colours. To handle * this situation, Frotz entends the colour scheme: Colours above * 15 (and below 256) may be used by the interface to refer to non * standard colours. Of course, os_set_colour must be able to deal * with these colours. * */ int os_peek_colour(void) { SF_textsetting *ts = sf_curtextsetting(); sf_flushtext(); return sf_GetColourIndex(sf_rpixel(ts->cx,ts->cy)); } static void scroll( int x, int y, int w, int h, int n) { ulong *src, *dst; int nmove, step; if (n > 0) { dst = sbuffer+x+sbpitch*y; src = dst + n*sbpitch; nmove = h-n; step = sbpitch; } else if (n < 0) { n = -n; nmove = h-n; step = -sbpitch; src = sbuffer+x+sbpitch*(y+nmove-1); dst = src + n*sbpitch; } else return; if (nmove > 0) { while (nmove--) { memcpy(dst,src,w*sizeof(ulong)); dst += step; src += step; } dirty = 1; } } void sf_flushdisplay() { if (dirty) myGrefresh(); dirty = 0; } /* * os_scroll_area * * Scroll a rectangular area of the screen up (units > 0) or down * (units < 0) and fill the empty space with the current background * colour. Top left coordinates are (1,1). The cursor stays put. * */ void os_scroll_area(int top, int left, int bottom, int right, int units) { sf_flushtext(); // theWnd->ResetOverhang(); scroll(left-1,top-1,right-left+1,bottom-top+1,units); if (units > 0) sf_fillrect((sf_curtextsetting())->back,left-1,bottom-units,right-left+1,units); else if (units < 0) sf_fillrect((sf_curtextsetting())->back,left-1,top-1,right-left+1,units); // if (theApp.GetFastScrolling() == false) // sf_flushdisplay(); // theWnd->FlushDisplay(); } int SFdticks = 200; volatile bool SFticked = 0; static SDL_TimerID timerid = 0; static Uint32 mytimer( Uint32 inter, void *parm) { SFticked = true; return inter; } static void cleanvideo() { if (timerid) SDL_RemoveTimer(timerid); SDL_Quit(); } #define RM 0x0000ff #define GM 0x00ff00 #define BM 0xff0000 static int check( int R, int G, int B){ if (R==RM && G==GM && B==BM) return 0; if (R==BM && G==GM && B==RM) return -1; return 1; } extern char stripped_story_name[]; void sf_initvideo( int W, int H, int full) { int desired_bpp, needoff, reqW, reqH; Uint32 video_flags; SDL_PixelFormat *format; Uint32 initflags = SDL_INIT_VIDEO | SDL_INIT_NOPARACHUTE | SDL_INIT_TIMER | SDL_INIT_AUDIO; sprintf(banner,"SDL Frotz v%d.%02d - %s (z%d)",SFROTZ_MAJOR,SFROTZ_MINOR, stripped_story_name,h_version); desired_bpp = 32; video_flags = 0; if ( SDL_Init(initflags) < 0 ) { os_fatal("Couldn't initialize SDL: %s", SDL_GetError()); } CLEANREG(cleanvideo); isfullscreen = full; if (full) video_flags = SDL_FULLSCREEN; reqW = W; reqH = H; if (full) { if (m_reqW == -1) { const SDL_VideoInfo * v = SDL_GetVideoInfo(); if (v) { m_reqW = v->current_w; m_reqH = v->current_h;} } if (m_reqW > reqW) reqW = m_reqW; if (m_reqH > reqH) reqH = m_reqH; } screen = SDL_SetVideoMode( reqW, reqH, desired_bpp, video_flags); if ( screen == NULL ) { os_fatal("Couldn't set %dx%dx%d video mode: %s", reqW, reqH, desired_bpp, SDL_GetError()); } SDL_WM_SetCaption(banner,NULL); bitsperpixel = 32; mustlockscreen = SDL_MUSTLOCK(screen); format = screen->format; needoff = (mustlockscreen) || (screen->w != W) || (screen->h != H) || (format->BitsPerPixel != 24) || (screen->pitch != 3*W); RBswap = 0; if (!needoff) { needoff = check(format->Rmask,format->Gmask,format->Bmask); if ((needoff == -1)){ RBswap = 1; needoff = 0; } else needoff = 1; } // printf("setvideo: gm %dx%d rq %dx%d(f%d) got %dx%d needoff %d\n", W,H,reqW,reqH,full,screen->w,screen->h,needoff); if (needoff) { sbuffer = calloc(W*H,sizeof(ulong)); if (!sbuffer){ os_fatal("Could not create gc"); } off = SDL_CreateRGBSurfaceFrom(sbuffer, W,H,32,4*W,0xff,0xff00,0xff0000,0); // off = SDL_CreateRGBSurfaceFrom(sbuffer, // W,H,32,4*screen->w,0xff,0xff00,0xff0000,0); if (!off){ os_fatal("Could not create offscreen surface"); } } else { sbuffer = (ulong *)screen->pixels; } blitrect.w = W; blitrect.h = H; blitrect.x = (screen->w - W)/2; blitrect.y = (screen->h - H)/2; SDL_EnableUNICODE(1); SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); SDL_AddTimer(SFdticks,mytimer,NULL); xmin = ymin = 0; xmax = ewidth = W; ymax = eheight = H; sbpitch = W; dirty = 1; } /* * os_draw_picture * * Display a picture at the given coordinates. * */ void os_draw_picture(int picture, int y, int x) { int ew, eh, xx, yy, ix, iy, d; int ox, oy, ow, oh; Zwindow * winpars; sf_picture * pic = sf_getpic(picture); ulong *src, *dst, sval, dval, alpha; sf_flushtext(); if (!pic) return; if (!pic->pixels) return; // TODO: rect src = (ulong *) pic->pixels; x--; y--; ew = m_gfxScale*pic->width; eh = m_gfxScale*pic->height; // this takes care of the fact taht x, y are really 16 bit values if (x & 0x8000) x |= 0xffff0000; if (y & 0x8000) y |= 0xffff0000; // get current window rect sf_getclip(&ox,&oy,&ow,&oh); winpars = curwinrec(); sf_setclip( winpars->x_pos-1, winpars->y_pos-1, winpars->x_size, winpars->y_size); // clip taking into account possible origin // outside the clipping rect if (x < xmin) { d = xmin-x; ew -= d; x = xmin; src += d;} if (x+ew > xmax) ew = xmax - x; ew /= m_gfxScale; if (y < ymin) { d = ymin-y; eh -= d; y = ymin; src += d*pic->width;} if (y+eh > ymax) eh = ymax-y; eh /= m_gfxScale; sf_setclip(ox,oy,ow,oh); if (ew <= 0) return; if (eh <= 0) return; for (yy=0;yy> 24); if (alpha == 255) dval = sval & 0xffffff; else dval = sf_blend((int)(alpha + (alpha>>7)),sval,dst[0]); for (iy=0;iywidth; } dirty = 1; } static ulong mytimeout; int mouse_button; static int numAltQ = 0; static zword goodzkey( SDL_Event *e, int allowed) { zword c; if (e->type == SDL_QUIT) { sf_quitconf(); // if (allowed) return ZC_HKEY_QUIT; return 0; } if (e->type == SDL_MOUSEBUTTONDOWN) { //printf("down %d\n",e->button.button); if (true) //(e->button.button == SDL_BUTTON_LEFT) { mouse_button = e->button.button; mouse_x = e->button.x+1-blitrect.x; mouse_y = e->button.y+1-blitrect.y; return ZC_SINGLE_CLICK; } return 0; } if (e->type != SDL_KEYDOWN) return 0; // emergency exit if (((e->key.keysym.mod & 0xfff) == (KMOD_LALT | KMOD_LCTRL)) && (e->key.keysym.sym == 'x')) os_fatal("Emergency exit!\n\n(Control-Alt-X pressed)"); if (((e->key.keysym.mod & KMOD_LALT) == KMOD_LALT) || ((e->key.keysym.mod & KMOD_RALT) == KMOD_RALT)) { if (e->key.keysym.sym == 'q') { numAltQ++; if (numAltQ > 2) os_fatal("Emergency exit!\n\n(Alt-Q pressed 3 times in succession)"); } else numAltQ = 0; if (!allowed) return 0; switch (e->key.keysym.sym) { case 'x': return ZC_HKEY_QUIT; case 'p': return ZC_HKEY_PLAYBACK; case 'r': return ZC_HKEY_RECORD; case 's': return ZC_HKEY_SEED; case 'u': return ZC_HKEY_UNDO; case 'n': return ZC_HKEY_RESTART; case 'd': return ZC_HKEY_DEBUG; case 'h': return ZC_HKEY_HELP; default: return 0; } } else numAltQ = 0; switch (e->key.keysym.sym) { case SDLK_INSERT: return (allowed ? VK_INS : 0); case SDLK_BACKSPACE: return ZC_BACKSPACE; case SDLK_ESCAPE: return ZC_ESCAPE; case SDLK_RETURN: return ZC_RETURN; case SDLK_UP: return ZC_ARROW_UP; case SDLK_DOWN: return ZC_ARROW_DOWN; case SDLK_LEFT: return ZC_ARROW_LEFT; case SDLK_RIGHT: return ZC_ARROW_RIGHT; case SDLK_TAB: return (allowed ? VK_TAB : 0); case SDLK_KP0: return ZC_NUMPAD_MIN+0; case SDLK_KP1: return ZC_NUMPAD_MIN+1; case SDLK_KP2: return ZC_NUMPAD_MIN+2; case SDLK_KP3: return ZC_NUMPAD_MIN+3; case SDLK_KP4: return ZC_NUMPAD_MIN+4; case SDLK_KP5: return ZC_NUMPAD_MIN+5; case SDLK_KP6: return ZC_NUMPAD_MIN+6; case SDLK_KP7: return ZC_NUMPAD_MIN+7; case SDLK_KP8: return ZC_NUMPAD_MIN+8; case SDLK_KP9: return ZC_NUMPAD_MIN+9; case SDLK_F1: return ZC_FKEY_MIN+0; case SDLK_F2: return ZC_FKEY_MIN+1; case SDLK_F3: return ZC_FKEY_MIN+2; case SDLK_F4: return ZC_FKEY_MIN+3; case SDLK_F5: return ZC_FKEY_MIN+4; case SDLK_F6: return ZC_FKEY_MIN+5; case SDLK_F7: return ZC_FKEY_MIN+6; case SDLK_F8: return ZC_FKEY_MIN+7; case SDLK_F9: return ZC_FKEY_MIN+8; case SDLK_F10: return ZC_FKEY_MIN+9; case SDLK_F11: return ZC_FKEY_MIN+10; case SDLK_F12: return ZC_FKEY_MIN+11; default: break; } c = e->key.keysym.unicode; if ((c >= 32 && c <= 126) || (c >= 160)) return c; return 0; } zword sf_read_key( int timeout, int cursor, int allowed) { SDL_Event event; zword inch = 0; sf_flushtext(); // theWnd->ResetOverhang(); // theWnd->UpdateMenus(); if (cursor) sf_drawcursor(true); sf_flushdisplay(); if (timeout) mytimeout = sf_ticks() + m_timerinterval*timeout; // InputTimer timer(timeout); // FrotzWnd::Input input; while (true) { // Get the next input while (SDL_PollEvent(&event)) { //if (event.type == SDL_QUIT) printf("got SDL_QUIT\n"); if ((inch = goodzkey(&event,allowed))) break; } if (inch) break; if ((timeout) && (sf_ticks() >= mytimeout)) { inch = ZC_TIME_OUT; break; } sf_checksound(); sf_sleep(10); } if (cursor) sf_drawcursor(false); return inch; } /* * os_read_key * * Read a single character from the keyboard (or a mouse click) and * return it. Input aborts after timeout/10 seconds. * */ zchar os_read_key(int timeout, int cursor) { return sf_read_key(timeout,cursor,0); } #define MAXHISTORY 8192 static zword History[MAXHISTORY] = {0}; static int histptr = 0; static void addtoHistory( zchar *buf) { int n = mywcslen(buf)+2; int avail = MAXHISTORY - histptr; if (n <= 2) return; while (avail < n) { int k; if (histptr == 0) return; k = History[histptr-1]+2; histptr -= k; } if (histptr) memmove(History+n,History,histptr*sizeof(zword)); histptr += n; n -= 2; History[0] = History[n+1] = n; memcpy(History+1,buf,n*sizeof(zword)); } /* * os_read_line * * Read a line of input from the keyboard into a buffer. The buffer * may already be primed with some text. In this case, the "initial" * text is already displayed on the screen. After the input action * is complete, the function returns with the terminating key value. * The length of the input should not exceed "max" characters plus * an extra 0 terminator. * * Terminating keys are the return key (13) and all function keys * (see the Specification of the Z-machine) which are accepted by * the is_terminator function. Mouse clicks behave like function * keys except that the mouse position is stored in global variables * "mouse_x" and "mouse_y" (top left coordinates are (1,1)). * * Furthermore, Frotz introduces some special terminating keys: * * ZC_HKEY_PLAYBACK (Alt-P) * ZC_HKEY_RECORD (Alt-R) * ZC_HKEY_SEED (Alt-S) * ZC_HKEY_UNDO (Alt-U) * ZC_HKEY_RESTART (Alt-N, "new game") * ZC_HKEY_QUIT (Alt-X, "exit game") * ZC_HKEY_DEBUG (Alt-D) * ZC_HKEY_HELP (Alt-H) * * If the timeout argument is not zero, the input gets interrupted * after timeout/10 seconds (and the return value is 0). * * The complete input line including the cursor must fit in "width" * screen units. * * The function may be called once again to continue after timeouts, * misplaced mouse clicks or hot keys. In this case the "continued" * flag will be set. This information can be useful if the interface * implements input line history. * * The screen is not scrolled after the return key was pressed. The * cursor is at the end of the input line when the function returns. * * Since Frotz 2.2 the helper function "completion" can be called * to implement word completion (similar to tcsh under Unix). * */ zchar os_read_line(int max, zchar *buf, int timeout, int width, int continued) { static int prev_pos = 0; static int prev_history = -1; int pos = 0; int history = -1; int ptx,pty; SF_textsetting * ts = sf_curtextsetting(); SDL_Event event; //printf("os_read_line mx%d buf[0]%d tm%d w%d c%d\n",max,buf[0],timeout,width,continued); // LineInput line; sf_flushtext(); // theWnd->ResetOverhang(); // theWnd->UpdateMenus(); // theWnd->RecaseInput(buf); // Find the editing position if (continued) { if (prev_pos <= (int)mywcslen(buf)) pos = prev_pos; } else pos = mywcslen(buf); //printf("pos %d\n",pos); // Find the input history position if (continued) history = prev_history; // Draw the input line ptx = ts->cx; pty = ts->cy; // CPoint point = theWnd->GetTextPoint(); ptx -= os_string_width(buf); //theWnd->GetTextWidth(buf,mywcslen(buf)); sf_DrawInput(buf,pos,ptx,pty,width,true); if (timeout) mytimeout = sf_ticks() + m_timerinterval*timeout; // InputTimer timer(timeout); while (true) { // Get the next input while (SDL_PollEvent(&event)) { zword c; if ((c = goodzkey(&event,1))) { //printf("goodzk %4x\n",c); if (c == ZC_BACKSPACE) { // Delete the character to the left of the cursor if (pos > 0) { memmove(buf+pos-1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1)); pos--; sf_DrawInput(buf,pos,ptx,pty,width,true); } } else if (c == VK_TAB) { if (pos == (int)mywcslen(buf)) { zword extension[10], *s; completion(buf,extension); // Add the completion to the input stream for (s = extension; *s != 0; s++) if (sf_IsValidChar(*s)) buf[pos++] = (*s); buf[pos] = 0; sf_DrawInput(buf,pos,ptx,pty,width,true); } } else if ((c == ZC_ARROW_LEFT)) { // Move the cursor left if (pos > 0) pos--; sf_DrawInput(buf,pos,ptx,pty,width,true); } else if ((c == ZC_ARROW_RIGHT)) { // Move the cursor right if (pos < (int)mywcslen(buf)) pos++; sf_DrawInput(buf,pos,ptx,pty,width,true); } else if ((c == ZC_ARROW_UP)) { int n; zword *p; // Move up through the command history if (history >= 0) { n = History[history]; if (n) history += (n+2); } else history = 0; n = History[history]; p = History + history + 1; pos = 0; while (n) { buf[pos++] = *p++; n--;} buf[pos] = 0; sf_DrawInput(buf,pos,ptx,pty,width,true); } else if ((c == ZC_ARROW_DOWN)) { // Move down through the command history pos = 0; if (history > 0) { int n; zword *p; n = History[history-1]; history -= (n+2); p = History + history + 1; while (n) { buf[pos++] = *p++; n--; } } else history = -1; buf[pos] = 0; sf_DrawInput(buf,pos,ptx,pty,width,true); } else if (is_terminator(c)) { // Terminate the current input m_exitPause = false; sf_DrawInput(buf,pos,ptx,pty,width,false); if ((c == ZC_SINGLE_CLICK) || (c == ZC_DOUBLE_CLICK)) { /* mouse_x = input.mousex+1; mouse_y = input.mousey+1;*/ } else if (c == ZC_RETURN) addtoHistory(buf); //theWnd->AddToInputHistory(buf); // theWnd->SetLastInput(buf); prev_pos = pos; prev_history = history; return c; } else if (sf_IsValidChar(c)) { // Add a valid character to the input line if ((int)mywcslen(buf) < max) { // Get the width of the new input line int len = os_string_width(buf); len += os_char_width(c); len += os_char_width('0'); //printf("l%d w%d p%d\n",len,width,pos); // Only allow if the width limit is not exceeded if (len <= width) { memmove(buf+pos+1,buf+pos,sizeof(zword)*(mywcslen(buf)-pos+1)); *(buf+pos) = c; pos++; sf_DrawInput(buf,pos,ptx,pty,width,true); } } } } } if ((timeout) && (sf_ticks() >= mytimeout)) { prev_pos = pos; prev_history = history; return ZC_TIME_OUT; } sf_checksound(); sf_sleep(10); } return 0; } // Draw the current input line void sf_DrawInput(zchar * buffer, int pos, int ptx, int pty, int width, bool cursor) { int height; SF_textsetting * ts = sf_curtextsetting(); //printf("DrawInput (%d)[%d] %d x%d y%d w%d %d\n",mywcslen(buffer),os_string_width(buffer),pos,ptx,pty,width,cursor); height = ts->font->height(ts->font); // Remove any previous input sf_fillrect(ts->back,ptx,pty,width,height); // Display the input // sf_pushtextsettings(); ts->cx = ptx; ts->cy = pty; os_display_string(buffer); if (cursor) { int wid=0, i=0; while (iGetMenuClick(); //printf("%04x\n",c); // Get the mouse buttons if (c & SDL_BUTTON_LMASK) btn |= 1; if (c & SDL_BUTTON_RMASK) btn |= 2; if (c & SDL_BUTTON_MMASK) btn |= 4; return btn; } /* * os_more_prompt * * Display a MORE prompt, wait for a keypress and remove the MORE * prompt from the screen. * */ void os_more_prompt(void) { if (m_morePrompts) { SF_textsetting * ts; int x,y,h; const char *p = sf_msgstring(IDS_MORE); sf_flushtext(); // theWnd->ResetOverhang(); // Save the current text position sf_pushtextsettings(); ts = sf_curtextsetting(); x = ts->cx; y = ts->cy; h = ts->font->height(ts->font); // Show a [More] prompt while (*p) os_display_char((zword)(*p++)); // theWnd->WriteText(CResString(IDS_MORE)); // sf_drawcursor(true); // sf_flushdisplay(); // Wait for a key press os_read_key(0,1); // Remove the [More] prompt sf_fillrect(ts->back,x,y,ts->cx-x,h); // sf_drawcursor(false); // Restore the current text position sf_poptextsettings(); } } ulong * sf_savearea( int x, int y, int w, int h) { ulong *r, *p, *s; int i; if (x < 0){ w += x; x = 0;} if (x+w > ewidth) w = ewidth-x; if (w <= 0) return NULL; if (y < 0){ h += y; y = 0;} if (y+h > eheight) h = eheight-y; if (h <= 0) return NULL; r = p = malloc((w*h+4)*sizeof(ulong)); if (!r) return NULL; *p++ = x; *p++ = y; *p++ = w; *p++ = h; s = sbuffer+x+y*sbpitch; for (i=0;i #include #include #include #include #include "sf_frotz.h" #include #include "png.h" #include #include "../blorb/blorblow.h" // static double m_gamma = DEFAULT_GAMMA; static byte toLinear[256]; static byte fromLinear[256]; ulong sf_blend( int a, ulong s, ulong d){ ulong r; r = fromLinear[(toLinear[s & 0xff]*a + toLinear[d & 0xff]*(256-a))>>8]; s >>= 8; d >>= 8; r |= (fromLinear[(toLinear[s & 0xff]*a + toLinear[d & 0xff]*(256-a))>>8]) << 8; s >>= 8; d >>= 8; r |= (fromLinear[(toLinear[s & 0xff]*a + toLinear[d & 0xff]*(256-a))>>8]) << 16; return r; } // Set the screen gamma and build gamma correction tables void sf_setgamma(double gamma) { int i; m_gamma = gamma; for (i = 0; i < 256; i++) toLinear[i] = (int)((pow(i/255.0,gamma) * 255.0) + 0.5); gamma = 1.0/gamma; for (i = 0; i < 256; i++) fromLinear[i] = (int)((pow(i/255.0,gamma) * 255.0) + 0.5); } ///////////////////////////////////////////////////////////////////////////// // Loader for PNG images ///////////////////////////////////////////////////////////////////////////// typedef struct { byte * gfxData; unsigned long offset; } PNGData; static void readPNGData( png_structp png_ptr, png_bytep data, png_size_t length) { PNGData* pngData = (PNGData*)png_get_io_ptr(png_ptr); memcpy(data,pngData->gfxData+pngData->offset,length); pngData->offset += length; } static int loadpng( byte *data, int length, sf_picture *graphic) { png_bytep * rowPointers = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_infop end_info = NULL; PNGData pngData; png_uint_32 width, height; int i, bit_depth, color_type, size; double gamma; graphic->pixels = NULL; graphic->width = graphic->height = 0; if (!png_check_sig(data,8)) return 0; png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,(png_voidp)NULL,NULL,NULL); if (!png_ptr) return 0; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL,(png_infopp)NULL); return 0; } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)NULL); return 0; } if (setjmp(png_ptr->jmpbuf)) { png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); if (rowPointers) free(rowPointers); if (graphic->pixels) { free(graphic->pixels); graphic->pixels = NULL; } return 0; } pngData.gfxData = data; pngData.offset = 8; png_set_read_fn(png_ptr,&pngData,readPNGData); png_set_sig_bytes(png_ptr,8); png_read_info(png_ptr,info_ptr); width = png_get_image_width(png_ptr,info_ptr); height = png_get_image_height(png_ptr,info_ptr); bit_depth = png_get_bit_depth(png_ptr,info_ptr); color_type = png_get_color_type(png_ptr,info_ptr); graphic->width = width; graphic->height = height; if (color_type == PNG_COLOR_TYPE_PALETTE && bit_depth <= 8) png_set_palette_to_rgb(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) #ifdef __WIN32__ os_fatal("Missing png_set_gray_1_2_4_to_8\n"); #else png_set_gray_1_2_4_to_8(png_ptr); #endif if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); if (png_get_gAMA(png_ptr,info_ptr,&gamma)) png_set_gamma(png_ptr,m_gamma,gamma); if (bit_depth == 16) png_set_strip_16(png_ptr); if (bit_depth < 8) png_set_packing(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // png_set_bgr(png_ptr); png_set_filler(png_ptr,0xFF,PNG_FILLER_AFTER); // graphic->m_header = new BITMAPINFOHEADER; // ::ZeroMemory(graphic->m_header,sizeof(BITMAPINFOHEADER)); // graphic->m_header->biSize = sizeof(BITMAPINFOHEADER); // graphic->m_header->biWidth = width; // graphic->m_header->biHeight = height*-1; // graphic->m_header->biPlanes = 1; // graphic->m_header->biBitCount = 32; // graphic->m_header->biCompression = BI_RGB; size = width*height*4; graphic->pixels = (byte *)malloc(size); rowPointers = (png_bytep *) malloc(height*sizeof(png_bytep)); for (i = 0; i < (int)height; i++) rowPointers[i] = graphic->pixels+(width*i*4); png_read_image(png_ptr,rowPointers); // Get the palette after reading the image, so that the gamma // correction is applied // png_colorp palette; // int num_palette; // if (png_get_PLTE(png_ptr,info_ptr,&palette,&num_palette)) // { // for (int i = 0; i < num_palette; i++) // { // DWORD colour = // (palette[i].red<<16)|(palette[i].green<<8)|palette[i].blue; // graphic->m_palette.Add(colour); // graphic->m_invPalette[colour] = i; // } // } png_read_end(png_ptr,end_info); png_destroy_read_struct(&png_ptr,&info_ptr,&end_info); if (rowPointers) free( rowPointers); return 1; } ///////////////////////////////////////////////////////////////////////////// // Loader for JPEG images ///////////////////////////////////////////////////////////////////////////// #include // Error Handling struct JPEGErrorInfo { struct jpeg_error_mgr base; jmp_buf errorJump; }; static void errorJPEGExit(j_common_ptr cinfo) { struct JPEGErrorInfo* error = (struct JPEGErrorInfo*)cinfo->err; (*cinfo->err->output_message)(cinfo); longjmp(error->errorJump,1); } static void outputJPEGMessage(j_common_ptr cinfo) { char buffer[JMSG_LENGTH_MAX]; (*cinfo->err->format_message)(cinfo,buffer); // TRACE("JPEG: %s\n",buffer); } // Memory Data Source static void memJPEGInit(j_decompress_ptr unused) { } static int memJPEGFillInput(j_decompress_ptr unused) { return 0; } static void memJPEGSkipInput(j_decompress_ptr cinfo, long num_bytes) { if (num_bytes > 0) { if (num_bytes > (long)cinfo->src->bytes_in_buffer) num_bytes = (long)cinfo->src->bytes_in_buffer; cinfo->src->next_input_byte += num_bytes; cinfo->src->bytes_in_buffer -= num_bytes; } } static void memJPEGTerm(j_decompress_ptr unused) { } static int loadjpeg( byte *data, int length, sf_picture *graphic) { struct jpeg_decompress_struct info; struct JPEGErrorInfo error; int width, height, size; JSAMPARRAY buffer; graphic->pixels = NULL; graphic->width = graphic->height = 0; info.err = jpeg_std_error(&(error.base)); error.base.error_exit = errorJPEGExit; error.base.output_message = outputJPEGMessage; if (setjmp(error.errorJump)) { jpeg_destroy_decompress(&info); if (graphic->pixels) { free(graphic->pixels); graphic->pixels = NULL; } return 0; } jpeg_create_decompress(&info); info.src = (struct jpeg_source_mgr *)(info.mem->alloc_small) ((j_common_ptr)(&info),JPOOL_PERMANENT,sizeof(struct jpeg_source_mgr)); info.src->init_source = memJPEGInit; info.src->fill_input_buffer = memJPEGFillInput; info.src->skip_input_data = memJPEGSkipInput; info.src->resync_to_restart = jpeg_resync_to_restart; info.src->term_source = memJPEGTerm; info.src->bytes_in_buffer = length; info.src->next_input_byte = data; jpeg_read_header(&info,TRUE); jpeg_calc_output_dimensions(&info); width = info.output_width; height = info.output_height; graphic->width = width; graphic->height = height; size = width*height*4; graphic->pixels = (byte *)malloc(size); // graphic->m_header = new BITMAPINFOHEADER; // ::ZeroMemory(graphic->m_header,sizeof(BITMAPINFOHEADER)); // graphic->m_header->biSize = sizeof(BITMAPINFOHEADER); // graphic->m_header->biWidth = width; // graphic->m_header->biHeight = height*-1; // graphic->m_header->biPlanes = 1; // graphic->m_header->biBitCount = 32; // graphic->m_header->biCompression = BI_RGB; // graphic->m_pixels = new BYTE[width*height*4]; // Force RGB output info.out_color_space = JCS_RGB; // Get an output buffer buffer = (*info.mem->alloc_sarray) ((j_common_ptr)&info,JPOOL_IMAGE,width*3,1); jpeg_start_decompress(&info); while ((int)info.output_scanline < height) { byte * pixelRow; int i; jpeg_read_scanlines(&info,buffer,1); pixelRow = graphic->pixels+ (width*(info.output_scanline-1)*4); for (i = 0; i < width; i++) { /* pixelRow[(i*4)+0] = (*buffer)[(i*3)+2]; pixelRow[(i*4)+1] = (*buffer)[(i*3)+1]; pixelRow[(i*4)+2] = (*buffer)[(i*3)+0];*/ pixelRow[(i*4)+0] = (*buffer)[(i*3)+0]; pixelRow[(i*4)+1] = (*buffer)[(i*3)+1]; pixelRow[(i*4)+2] = (*buffer)[(i*3)+2]; pixelRow[(i*4)+3] = 0xFF; } } jpeg_finish_decompress(&info); jpeg_destroy_decompress(&info); return 1; } ///////////////////////////////////////////////////////////////////////////// // Loader for simple rectangles ///////////////////////////////////////////////////////////////////////////// static int loadrect( byte *data, int length, sf_picture *graphic) { graphic->width = (data[0]<<24)|(data[1]<<16)|(data[2]<<8)|data[3]; graphic->height = (data[4]<<24)|(data[5]<<16)|(data[6]<<8)|data[7]; graphic->pixels = NULL; return 1; } /////////////////////////////// // Get a picture from the Blorb resource map static int sf_loadpic( int picture, sf_picture *graphic) { myresource res; int st = 0; if (sf_getresource( picture, 1, bb_method_Memory,&res) == bb_err_None) { byte * data = (byte *)res.bbres.data.ptr; int length = res.bbres.length; unsigned int id = res.type; // Look for a recognized format if (id == bb_make_id('P','N','G',' ')) { st = loadpng( data, length, graphic); if (!st) st = loadjpeg( data, length, graphic); } else if (id == bb_make_id('J','P','E','G')) { st = loadjpeg( data, length, graphic); if (!st) st = loadpng( data, length, graphic); } else if (id == bb_make_id('R','e','c','t')) st = loadrect( data, length, graphic); sf_freeresource(&res); } if (st) graphic->number = picture; return st; } //////////////////// // CACHE #define MAXCACHE 1 static sf_picture cached[MAXCACHE]; static int cacheinited = 0; static void cacheflush() { int i; if (!cacheinited) return; for (i=0;i #include #include #include #include #include "frotz.h" #include "BCfrotz.h" #ifdef SOUND_SUPPORT #define SWAP_BYTES(v) {_AX=v;asm xchg al,ah;v=_AX;} #define READ_DSP(v) {while(!inportb(sound_adr+14)&0x80);v=inportb(sound_adr+10);} #define WRITE_DSP(v) {while(inportb(sound_adr+12)&0x80);outportb(sound_adr+12,v);} extern void end_of_sound (void); static struct { word prefix; byte repeats; byte base_note; word frequency; word unused; word length; } sheader; static current_sample = 0; static void interrupt (*vect) (void) = NULL; static play_part = 0; static play_count = 0; static word sound_adr = 0; static word sound_irq = 0; static word sound_dma = 0; static word sound_int = 0; static word sound_ver = 0; static byte far *sample_data = NULL; static long sample_adr1 = 0; static long sample_adr2 = 0; static word sample_len1 = 0; static word sample_len2 = 0; /* * start_of_dma * * Start the DMA transfer to the sound board. * */ static void start_of_dma (long address, unsigned length) { static unsigned dma_page_port[] = { 0x87, 0x83, 0x81, 0x82 }; length--; /* Set up DMA chip */ outportb (0x0a, 0x04 | sound_dma); outportb (0x0c, 0x00); outportb (0x0b, 0x48 | sound_dma); outportb (2 * sound_dma, byte0 (address)); outportb (2 * sound_dma, byte1 (address)); outportb (dma_page_port[sound_dma], byte2 (address)); outportb (2 * sound_dma + 1, byte0 (length)); outportb (2 * sound_dma + 1, byte1 (length)); outportb (0x0a, sound_dma); /* Play 8-bit mono sample */ WRITE_DSP (0x14) WRITE_DSP (byte0 (length)) WRITE_DSP (byte1 (length)) }/* start_of_dma */ /* * end_of_dma * * This function is called when a hardware interrupt signals the * end of the current sound. We may have to play the second half * of the sound effect, or we may have to repeat it, or call the * end_of_sound function when we are finished. * */ static void interrupt end_of_dma (void) { /* Play the second half, play another cycle or finish */ if (play_part == 1 && sample_len2 != 0) { play_part = 2; start_of_dma (sample_adr2, sample_len2); } else if (play_count == 255 || --play_count != 0) { play_part = 1; start_of_dma (sample_adr1, sample_len1); } else { play_part = 0; end_of_sound (); } /* Tell interrupt controller(s) + sound board we are done */ outportb (0x20, 0x20); if (sound_irq >= 8) outportb (0xa0, 0x20); inportb (sound_adr + 14); }/* end_of_dma */ /* * dos_init_sound * * Initialise the sound board and various sound related variables. * */ bool dos_init_sound (void) { const char *settings; word irc_mask_port; /* Read the IRQ, port address, DMA channel and SB version */ if ((settings = getenv ("BLASTER")) == NULL) return FALSE; sound_irq = dectoi (strchr (settings, 'I') + 1); sound_adr = hextoi (strchr (settings, 'A') + 1); sound_dma = dectoi (strchr (settings, 'D') + 1); sound_ver = dectoi (strchr (settings, 'T') + 1); /* Reset mixer chip and DSP */ outportb (sound_adr + 4, 0); outportb (sound_adr + 5, 0); outportb (sound_adr + 6, 1); inportb (sound_adr + 6); inportb (sound_adr + 6); inportb (sound_adr + 6); outportb (sound_adr + 6, 0); /* Turn on speakers */ WRITE_DSP (0xd1) /* Install the end_of_dma interrupt */ if (sound_irq < 8) { irc_mask_port = 0x21; sound_int = 0x08 + sound_irq; } else { irc_mask_port = 0xa1; sound_int = 0x68 + sound_irq; } vect = getvect (sound_int); setvect (sound_int, end_of_dma); /* Allocate 64KB RAM for sample data */ if ((sample_data = (byte far *) farmalloc (0x10000L)) == NULL) return FALSE; word0 (sample_adr1) = FP_OFF (sample_data) | (FP_SEG (sample_data) << 4); word1 (sample_adr1) = FP_SEG (sample_data) >> 12; word0 (sample_adr2) = 0; word1 (sample_adr2) = word1 (sample_adr1) + 1; /* Enable the end_of_dma interrupt */ outportb (0x20, 0x20); if (sound_irq >= 8) outportb (0xa0, 0x20); outportb (irc_mask_port, inportb (irc_mask_port) & ~(1 << (sound_irq & 7))); /* Indicate success */ return TRUE; }/* init_sound */ /* * dos_reset_sound * * Free resources allocated for playing samples. * */ void dos_reset_sound (void) { os_stop_sample (); if (sample_data != NULL) { farfree (sample_data); sample_data = NULL; } if (sound_adr != 0) { setvect (sound_int, vect); sound_adr = 0; } }/* dos_reset_sound */ #endif /* SOUND_SUPPORT */ /* * os_beep * * Play a beep sound. Ideally, the sound should be high- (number == 1) * or low-pitched (number == 2). * */ void os_beep (int number) { word T = 888 * number; outportb (0x43, 0xb6); outportb (0x42, lo (T)); outportb (0x42, hi (T)); outportb (0x61, inportb (0x61) | 3); delay (75); outportb (0x61, inportb (0x61) & ~3); }/* os_beep */ /* * os_prepare_sample * * Load the sample from the disk. * */ void os_prepare_sample (int number) { #ifdef SOUND_SUPPORT os_stop_sample (); /* Exit if the sound board isn't set up properly */ if (sample_data == NULL) return; if (sound_adr == 0) return; /* Continue only if the desired sample is not already present */ if (current_sample != number) { char sample_name[MAX_FILE_NAME + 1]; char numstr[2]; FILE *fp; /* Build sample file name */ strcpy (sample_name, "sound/"); numstr[0] = '0' + number / 10; numstr[1] = '0' + number % 10; strncat (sample_name, f_setup.story_name, 6); strncat (sample_name, numstr, 2); strncat (sample_name, ".snd", 4); /* Open sample file */ if ((fp = fopen (sample_name, "rb")) == NULL) return; /* Load header and sample data */ fread (&sheader, sizeof (sheader), 1, fp); SWAP_BYTES (sheader.frequency) SWAP_BYTES (sheader.length) fread (sample_data, 1, sheader.length, fp); sample_len1 = -word0 (sample_adr1); if (sample_len1 > sheader.length || sample_len1 == 0) sample_len1 = sheader.length; sample_len2 = sheader.length - sample_len1; WRITE_DSP (0x40) WRITE_DSP (256 - 1000000L / sheader.frequency) current_sample = number; /* Close sample file */ fclose (fp); } #endif /* SOUND_SUPPORT */ }/* os_prepare_sample */ /* * os_start_sample * * Play the given sample at the given volume (ranging from 1 to 8 and * 255 meaning a default volume). The sound is played once or several * times in the background (255 meaning forever). The end_of_sound * function is called as soon as the sound finishes. * */ void os_start_sample (int number, int volume, int repeats, zword eos) { #ifdef SOUND_SUPPORT os_stop_sample (); /* Exit if the sound board isn't set up properly */ if (sample_data == NULL) return; if (sound_adr == 0) return; /* Load new sample */ os_prepare_sample (number); /* Continue only if the sample's in memory now */ if (current_sample == number) { play_count = repeats; if (sound_ver < 6) { /* Set up SB pro mixer chip */ volume = (volume != 255) ? 7 + volume : 15; outportb (sound_adr + 4, 0x04); outportb (sound_adr + 5, (volume << 4) | volume); outportb (sound_adr + 4, 0x22); outportb (sound_adr + 5, 0xff); } else { /* Set up SB16 mixer chip */ /* Many thanks to Linards Ticmanis for writing this part! */ volume = (volume != 255) ? 127 + 16 * volume : 255; outportb (sound_adr + 4, 0x32); outportb (sound_adr + 5, volume); outportb (sound_adr + 4, 0x33); outportb (sound_adr + 5, volume); outportb (sound_adr + 4, 0x30); outportb (sound_adr + 5, 0xff); outportb (sound_adr + 4, 0x31); outportb (sound_adr + 5, 0xff); } play_part = 1; start_of_dma (sample_adr1, sample_len1); } #endif /* SOUND_SUPPORT */ }/* os_start_sample */ /* * os_stop_sample * * Turn off the current sample. * */ void os_stop_sample (void) { #ifdef SOUND_SUPPORT play_part = 0; /* Exit if the sound board isn't set up properly */ if (sample_data == NULL) return; if (sound_adr == 0) return; /* Tell DSP to stop the current sample */ WRITE_DSP (0xd0) #endif /* SOUND_SUPPORT */ }/* os_stop_sample */ /* * os_finish_with_sample * * Remove the current sample from memory (if any). * */ void os_finish_with_sample (void) { #ifdef SOUND_SUPPORT os_stop_sample (); /* we keep 64KB allocated all the time */ #endif /* SOUND_SUPPORT */ }/* os_finish_with_sample */ frotz-2.44/src/dos/blorb.h0000644000175000017500000001124012527045477012370 00000000000000#ifndef BLORB_H #define BLORB_H /* blorb.h: Header file for Blorb library, version 1.0.2. Designed by Andrew Plotkin http://www.eblong.com/zarf/blorb/index.html This is the header that a Z-machine interpreter should include. It defines everything that the interpreter has to know. */ /* Things you (the porter) have to edit: */ /* As you might expect, uint32 must be a 32-bit unsigned numeric type, and uint16 a 16-bit unsigned numeric type. You should also uncomment exactly one of the two ENDIAN definitions. */ /* #define BLORB_BIG_ENDIAN */ #define BLORB_LITTLE_ENDIAN typedef unsigned long uint32; typedef unsigned short uint16; /* End of things you have to edit. */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* Error type and error codes */ typedef int bb_err_t; #define bb_err_None (0) #define bb_err_CompileTime (1) #define bb_err_Alloc (2) #define bb_err_Read (3) #define bb_err_NotAMap (4) #define bb_err_Format (5) #define bb_err_NotFound (6) /* Methods for loading a chunk */ #define bb_method_DontLoad (0) #define bb_method_Memory (1) #define bb_method_FilePos (2) /* Four-byte constants */ /*#define bb_make_id(c1, c2, c3, c4) \ (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4)) */ #define bb_ID_Snd 1399743520 #define bb_ID_Exec 1165518179 #define bb_ID_Pict 1349084020 #define bb_ID_Copyright 677587232 #define bb_ID_AUTH 1096111176 #define bb_ID_ANNO 1095650895 #define bb_ID_ZCOD 1514360644 /* bb_result_t: Result when you try to load a chunk. */ typedef struct bb_result_struct { int chunknum; /* The chunk number (for use in bb_unload_chunk(), etc.) */ union { void *ptr; /* A pointer to the data (if you used bb_method_Memory) */ uint32 startpos; /* The position in the file (if you used bb_method_FilePos) */ } data; uint32 length; /* The length of the data */ } bb_result_t; /* bb_aux_sound_t: Extra data which may be associated with a sound. */ typedef struct bb_aux_sound_struct { char repeats; } bb_aux_sound_t; /* bb_aux_pict_t: Extra data which may be associated with an image. */ typedef struct bb_aux_pict_struct { uint32 ratnum, ratden; uint32 minnum, minden; uint32 maxnum, maxden; } bb_aux_pict_t; /* bb_resolution_t: The global resolution data. */ typedef struct bb_resolution_struct { uint32 px, py; uint32 minx, miny; uint32 maxx, maxy; } bb_resolution_t; /* bb_color_t: Guess what. */ typedef struct bb_color_struct { unsigned char red, green, blue; } bb_color_t; /* bb_palette_t: The palette data. */ typedef struct bb_palette_struct { int isdirect; union { int depth; /* The depth (if isdirect is TRUE). Either 16 or 32. */ struct { int numcolors; bb_color_t *colors; } table; /* The list of colors (if isdirect is FALSE). */ } data; } bb_palette_t; /* bb_zheader_t: Information to identify a Z-code file. */ typedef struct bb_zheader_struct { uint16 releasenum; /* Bytes $2-3 of header. */ char serialnum[6]; /* Bytes $12-17 of header. */ uint16 checksum; /* Bytes $1C-1D of header. */ /* The initpc field is not used by Blorb. */ } bb_zheader_t; /* bb_map_t: Holds the complete description of an open Blorb file. This type is opaque for normal interpreter use. */ typedef struct bb_map_struct bb_map_t; /* Function declarations. These functions are of fairly general use; they would apply to any Blorb file. */ extern bb_err_t bb_create_map(FILE *file, bb_map_t **newmap); extern bb_err_t bb_destroy_map(bb_map_t *map); extern char *bb_err_to_string(bb_err_t err); extern bb_err_t bb_load_chunk_by_type(bb_map_t *map, int method, bb_result_t *res, uint32 chunktype, int count); extern bb_err_t bb_load_chunk_by_number(bb_map_t *map, int method, bb_result_t *res, int chunknum); extern bb_err_t bb_unload_chunk(bb_map_t *map, int chunknum); extern bb_err_t bb_load_resource(bb_map_t *map, int method, bb_result_t *res, uint32 usage, int resnum); extern bb_err_t bb_count_resources(bb_map_t *map, uint32 usage, int *num, int *min, int *max); /* More function declarations. These functions are more or less specific to the Z-machine's use of Blorb. */ extern uint16 bb_get_release_num(bb_map_t *map); extern bb_zheader_t *bb_get_zheader(bb_map_t *map); extern bb_resolution_t *bb_get_resolution(bb_map_t *map); extern bb_err_t bb_get_palette(bb_map_t *map, bb_palette_t **res); extern bb_err_t bb_load_resource_pict(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_pict_t **auxdata); extern bb_err_t bb_load_resource_snd(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_sound_t **auxdata); #endif /* BLORB_H */ frotz-2.44/src/dos/blorblow.h0000644000175000017500000000454012527045477013117 00000000000000#ifndef BLORBLOW_H #define BLORBLOW_H /* blorblow.h: Low-level header file for Blorb library, version 1.0.2. Designed by Andrew Plotkin http://www.eblong.com/zarf/blorb/index.html This header is generally of interest only to the Blorb library code itself (blorblib.c); it defines things internal to the library. An interpreter shouldn't have to include this file. The only time you might need to include this is if you're writing a Blorb file analysis tool (such as blorbscan), or a transformation tool, or some such thing. */ /* More four-byte constants. */ #define bb_ID_FORM 1179603533 #define bb_ID_IFRS 1229345363 #define bb_ID_RIdx 1380541560 #define bb_ID_IFhd 1229351012 #define bb_ID_Reso 1382380399 #define bb_ID_Loop 1282371440 #define bb_ID_RelN 1382378574 #define bb_ID_Plte 1349284965 /* bb_chunkdesc_t: Describes one chunk of the Blorb file. */ typedef struct bb_chunkdesc_struct { uint32 type; uint32 len; uint32 startpos; /* start of chunk header */ uint32 datpos; /* start of data (either startpos or startpos+8) */ void *ptr; /* pointer to malloc'd data, if loaded */ int auxdatnum; /* entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources; */ } bb_chunkdesc_t; /* bb_resdesc_t: Describes one resource in the Blorb file. */ typedef struct bb_resdesc_struct { uint32 usage; int resnum; int chunknum; } bb_resdesc_t; /* bb_map_t: Holds the complete description of an open Blorb file. */ struct bb_map_struct { uint32 inited; /* holds bb_Inited_Magic if the map structure is valid */ FILE *file; int numchunks; bb_chunkdesc_t *chunks; /* list of chunk descriptors */ int numresources; bb_resdesc_t *resources; /* list of resource descriptors */ bb_resdesc_t **ressorted; /* list of pointers to descriptors in map->resources -- sorted by usage and resource number. */ bb_zheader_t *zheader; int releasenum; bb_resolution_t *resolution; int palettechunk; /* chunk number of palette, or -1 if there is none. */ bb_palette_t *palette; bb_aux_sound_t *auxsound; /* extra information about sounds */ bb_aux_pict_t *auxpict; /* extra information about pictures */ }; #define bb_Inited_Magic (0xB7012BED) extern char *bb_id_to_string(uint32 id); #endif /* BLORBLOW_H */ frotz-2.44/src/dos/bcmouse.c0000644000175000017500000000166312527045477012730 00000000000000/* * file "BCmouse.c" * * Borland C front end, mouse support * */ #include #include "frotz.h" #include "bcfrotz.h" /* * detect_mouse * * Return true if a mouse driver is present. * */ bool detect_mouse (void) { asm xor ax,ax asm int 0x33 return _AX; }/* detect_mouse */ /* * read_mouse * * Report any mouse clicks. Return 2 for a double click, 1 for a single * click or 0 if there was no mouse activity at all. * */ int read_mouse (void) { int click; /* Read the current mouse status */ for (click = 0; click < 2; click++) { if (click == 1) delay (222); asm mov ax,6 asm xor bx,bx asm int 0x33 if (_BX == 0) break; mouse_x = _CX; mouse_y = _DX; if (display <= _TEXT_) { mouse_x /= 8; mouse_y /= 8; } if (display == _MCGA_) mouse_x /= 2; mouse_x++; mouse_y++; } /* Return single or double click */ return click; }/* read_mouse */ frotz-2.44/src/dos/bctext.c0000644000175000017500000004012612527045477012561 00000000000000/* * file "BCtext.c" * * Borland C front end, text functions * */ #include #include #include #include #include #include "frotz.h" #include "BCfrotz.h" #include "fontdata.h" extern byte far *get_scrnptr (int); int current_bg = 0; int current_fg = 0; int current_style = 0; int current_font = 0; byte text_bg = 0; byte text_fg = 0; byte bg = 0; byte fg = 0; byte scrn_attr = 0; int cursor_x = 0; int cursor_y = 0; char latin1_to_ascii[] = " ! c L >o> 1/41/23/4? " "A A A A Ae A AE C E E E E I I I I " "Th N O O O O Oe * O U U U Ue Y Th ss " "a a a a ae a ae c e e e e i i i i " "th n o o o o oe : o u u u ue y th y "; char latin1_to_ibm[] = { 0x20, 0xad, 0xbd, 0x9c, 0xcf, 0xbe, 0xdd, 0xf5, 0xf9, 0xb8, 0xa6, 0xae, 0xaa, 0xf0, 0xa9, 0xee, 0xf8, 0xf1, 0xfd, 0xfc, 0xef, 0xe6, 0xf4, 0xfa, 0xf7, 0xfb, 0xa7, 0xaf, 0xac, 0xab, 0xf3, 0xa8, 0xb7, 0xb5, 0xb6, 0xc7, 0x8e, 0x8f, 0x92, 0x80, 0xd4, 0x90, 0xd2, 0xd3, 0xde, 0xd6, 0xd7, 0xd8, 0xd1, 0xa5, 0xe3, 0xe0, 0xe2, 0xe5, 0x99, 0x9e, 0x9d, 0xeb, 0xe9, 0xea, 0x9a, 0xed, 0xe8, 0xe1, 0x85, 0xa0, 0x83, 0xc6, 0x84, 0x86, 0x91, 0x87, 0x8a, 0x82, 0x88, 0x89, 0x8d, 0xa1, 0x8c, 0x8b, 0xd0, 0xa4, 0x95, 0xa2, 0x93, 0xe4, 0x94, 0xf6, 0x9b, 0x97, 0xa3, 0x96, 0x81, 0xec, 0xe7, 0x98 }; static byte far *graphics_font = NULL; static byte far *mcga_font = NULL; static byte far *mcga_width = NULL; static word far *serif_font = NULL; static byte far *serif_width = NULL; /* * load_fonts * * Load the proportional and graphics fonts. In the release version all * font data is appended to the end of the executable. * */ void load_fonts (void) { static chunk_offset[] = { 0x6660, 0x6300, 0x4A40, 0x3180, 0x18C0, 0x00 }; if (display == _MCGA_) { mcga_font = font_data + chunk_offset[1]; mcga_width = (byte *) mcga_font + 0x300; } else graphics_font = font_data + chunk_offset[0]; if (display == _AMIGA_ && user_font != 0) { serif_font = (word *)(font_data + chunk_offset[1 + user_font]); serif_width = (byte *) serif_font + 0x1800; } }/* load_fonts */ /* * os_font_data * * Return true if the given font is available. The font can be * * TEXT_FONT * PICTURE_FONT * GRAPHICS_FONT * FIXED_WIDTH_FONT * * The font size should be stored in "height" and "width". If the given * font is unavailable then these values must _not_ be changed. * */ int os_font_data (int font, int *height, int *width) { /* All fonts of this interface have the same size */ *height = h_font_height; *width = h_font_width; /* Not every font is available in every mode */ if (font == TEXT_FONT) return TRUE; if (font == GRAPHICS_FONT && (display == _CGA_ || display >= _EGA_)) return TRUE; if (font == FIXED_WIDTH_FONT) return TRUE; /* Unavailable font */ return FALSE; }/* os_font_data */ /* * switch_scrn_attr * * Parts of the screen are usually erased to background colour. However, * for deleting text in the input routine it can be useful to erase to * the current text background colour. The two colours can be different, * for example when the reverse text style is used. This helper function * toggles between the two possible behaviours. * */ void switch_scrn_attr (bool flag) { byte scrn_bg; byte scrn_fg; if (flag) { scrn_bg = text_bg; scrn_fg = text_fg; } else { scrn_bg = bg; scrn_fg = fg; } if (display <= _TEXT_) scrn_attr = (scrn_bg << 4) | scrn_fg; else if (display == _CGA_) scrn_attr = (scrn_bg != BLACK) ? 0xff : 0x00; else scrn_attr = scrn_bg; }/* switch_scrn_attr */ /* * adjust_style * * Set the current colours. This combines the current colour selection * and the current text style. * */ static void adjust_style (void) { static byte amiga_palette[][3] = { { 0x00, 0x00, 0x00 }, { 0x2a, 0x00, 0x00 }, { 0x00, 0x2a, 0x00 }, { 0x3f, 0x3f, 0x15 }, { 0x00, 0x00, 0x2a }, { 0x2a, 0x00, 0x2a }, { 0x00, 0x2a, 0x2a }, { 0x3f, 0x3f, 0x3f }, { 0x30, 0x30, 0x30 }, { 0x20, 0x20, 0x20 }, { 0x10, 0x10, 0x10 }, }; static byte pc_colour[] = { BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, DARKGRAY }; static byte palette_bg = 0xff; static byte palette_fg = 0xff; fg = current_fg; bg = current_bg; /* V6 game, Amiga mode: Alter the palette registers if the colours of window 0 have changed. DAC register #79 holds the foreground, DAC register #64 the background colour. */ if (display == _AMIGA_ && h_version == V6 && cwin == 0) { if (fg < 16 && fg != palette_fg) { byte R = amiga_palette[fg - 2][0]; byte G = amiga_palette[fg - 2][1]; byte B = amiga_palette[fg - 2][2]; asm mov ax,0x1010 asm mov bx,79 asm mov dh,R asm mov ch,G asm mov cl,B asm int 0x10 palette_fg = fg; } if (bg < 16 && bg != palette_bg) { byte R = amiga_palette[bg - 2][0]; byte G = amiga_palette[bg - 2][1]; byte B = amiga_palette[bg - 2][2]; asm mov ax,0x1010 asm mov bx,64 asm mov dh,R asm mov ch,G asm mov cl,B asm int 0x10 palette_bg = bg; } } /* Handle colours */ if (fg < 16) if (display == _MONO_) fg = (fg == WHITE_COLOUR) ? LIGHTGRAY : BLACK; else if (h_version == V6 && display == _AMIGA_) fg = (palette_fg == fg) ? 15 : 0; else fg = pc_colour[fg - 2]; else fg -= 16; if (bg < 16) if (display == _MONO_) bg = (bg == WHITE_COLOUR) ? LIGHTGRAY : BLACK; else if (h_version == V6 && display == _AMIGA_) bg = (palette_bg == bg) ? 0 : 15; else bg = pc_colour[bg - 2]; else bg -= 16; /* Handle reverse text style */ if (current_style & REVERSE_STYLE) { text_fg = (user_reverse_fg != -1) ? user_reverse_fg : bg; text_bg = (user_reverse_bg != -1) ? user_reverse_bg : fg; } else { text_fg = fg; text_bg = bg; } /* Handle emphasis style */ if (current_style & EMPHASIS_STYLE) { if (display == _MONO_ && text_bg == BLACK) text_fg = BLUE; /* blue in monochrome mode is underlined */ if (display == _TEXT_) text_fg = (user_emphasis != -1) ? user_emphasis : YELLOW; } /* Handle boldface style */ if (current_style & BOLDFACE_STYLE) { if (display == _MONO_) text_fg = WHITE; if (display == _TEXT_) text_fg ^= 8; } /* Set the screen attribute for scrolling and erasing */ switch_scrn_attr (FALSE); }/* adjust_style */ /* * os_set_colour * * Set the foreground and background colours which can be: * * DEFAULT_COLOUR * BLACK_COLOUR * RED_COLOUR * GREEN_COLOUR * YELLOW_COLOUR * BLUE_COLOUR * MAGENTA_COLOUR * CYAN_COLOUR * WHITE_COLOUR * * MS-DOS 320 columns MCGA mode only: * * GREY_COLOUR * * Amiga only: * * LIGHTGREY_COLOUR * MEDIUMGREY_COLOUR * DARKGREY_COLOUR * * There may be more colours in the range from 16 to 255; see the remarks * on os_peek_colour. * */ void os_set_colour (int new_foreground, int new_background) { current_fg = new_foreground; current_bg = new_background; /* Apply changes */ adjust_style (); }/* os_set_colour */ /* * os_set_text_style * * Set the current text style. Following flags can be set: * * REVERSE_STYLE * BOLDFACE_STYLE * EMPHASIS_STYLE (aka underline aka italics) * FIXED_WIDTH_STYLE * */ void os_set_text_style (int new_style) { current_style = new_style; /* Apply changes */ adjust_style (); }/* os_set_text_style */ /* * os_set_font * * Set the font for text output. The interpreter takes care not to * choose fonts which aren't supported by the interface. * */ void os_set_font (int new_font) { current_font = new_font; }/* os_set_font */ /* * write_pattern * * Helper function for drawing characters in EGA and Amiga mode. * */ void write_pattern (byte far *screen, byte val, byte mask) { if (mask != 0) { if (display == _CGA_) { if (text_bg == BLACK) *screen &= ~mask; if (text_bg == WHITE) *screen |= mask; if (text_fg != text_bg) *screen ^= val; } else if (display == _MCGA_) { byte i; for (i = 0x80; (mask & i) != 0; i >>= 1) *screen++ = (val & i) ? text_fg : text_bg; } else { asm mov dx,0x03cf asm mov al,mask asm out dx,al asm les bx,screen asm mov ch,text_bg asm mov al,es:[bx] asm mov es:[bx],ch asm mov al,val asm out dx,al asm mov ch,text_fg asm mov al,es:[bx] asm mov es:[bx],ch } } }/* write_pattern */ /* * os_display_char * * Display a character of the current font using the current colours and * text style. The cursor moves to the next position. Printable codes are * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph * indentation). The screen should not be scrolled after printing to the * bottom right corner. * */ void os_display_char (zchar c) { int width = os_char_width (c); /* Handle accented characters */ if (c >= ZC_LATIN1_MIN && (story_id != BEYOND_ZORK || (h_flags & GRAPHICS_FLAG))) if (display == _CGA_ || display == _MCGA_) { char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); char c1 = *ptr++; char c2 = *ptr++; char c3 = *ptr++; os_display_char (c1); if (c2 != ' ') os_display_char (c2); if (c3 != ' ') os_display_char (c3); return; } else if (display == _AMIGA_ && current_font == TEXT_FONT && !(current_style & FIXED_WIDTH_STYLE) && user_font != 0) { if (c >= ZC_LATIN1_MIN) c -= 32; } else c = latin1_to_ibm[c - ZC_LATIN1_MIN]; /* Handle special indentations */ if (c == ZC_INDENT) { os_display_char (' '); os_display_char (' '); os_display_char (' '); return; } if (c == ZC_GAP) { os_display_char (' '); os_display_char (' '); return; } /* Display character */ if (display <= _TEXT_) { asm mov ah,2 asm mov bh,0 asm mov dh,byte ptr cursor_y asm mov dl,byte ptr cursor_x asm int 0x10 asm mov ah,9 asm mov bh,0 asm mov bl,byte ptr text_bg asm mov cl,4 asm shl bl,cl asm or bl,byte ptr text_fg asm mov cx,1 asm mov al,byte ptr c asm int 0x10 } else { void far *table; word mask; word val; byte mask0; byte mask1; int align; int underline; int boldface; int type; int shift = (display != _MCGA_) ? cursor_x % 8 : 0; int offset = (display != _MCGA_) ? cursor_x / 8 : cursor_x; int i; if (current_font == GRAPHICS_FONT) { table = graphics_font + 8 * (c - 32); mask = 0xff; underline = -1; boldface = -1; align = 0; type = 1; } else if (display == _AMIGA_ && current_font == TEXT_FONT && !(current_style & FIXED_WIDTH_STYLE) && user_font != 0) { table = serif_font + 16 * (c - 32); mask = 0xffff << (16 - width); underline = 14; boldface = 1; align = 0; type = 2; } else if (display == _CGA_) { table = (byte far *) MK_FP (0xf000, 0xfa6e) + 8 * c; mask = 0xff; underline = 7; boldface = (user_bold_typing != -1) ? 1 : -1; align = 0; type = 3; } else if (display >= _EGA_) { table = (byte far *) getvect (0x43) + h_font_height * c; mask = 0xff; underline = h_font_height - 1; boldface = (user_bold_typing != -1) ? 1 : -1; align = 0; type = 3; } else { table = mcga_font + 8 * (c - 32); mask = 0xff & (0xff << (8 - width)); underline = 7; boldface = -1; align = (width + 1 - mcga_width[c - 32]) / 2; type = 3; } mask0 = mask >> shift; mask1 = mask << (8 - shift); if (!(current_style & BOLDFACE_STYLE)) boldface = -1; if (!(current_style & EMPHASIS_STYLE)) underline = -1; if (display >= _EGA_) { outport (0x03ce, 0x0205); outport (0x03ce, 0xff08); } for (i = 0; i < h_font_height; i++) { byte far *screen = get_scrnptr (cursor_y + i) + offset; if (type == 1) val = *((byte far *) table + 8 * i / h_font_height); if (type == 2) val = *((word far *) table + i); if (type == 3) val = *((byte far *) table + i); if (align != 0) val >>= align; if (boldface == 1) val |= val >> 1; if (underline == i) val ^= mask; if (type == 2) write_pattern (screen++, val >> (8 + shift), mask >> (8 + shift)); write_pattern (screen + 0, val >> shift, mask0); write_pattern (screen + 1, val << (8 - shift), mask1); } } /* Move cursor to next position */ cursor_x += width; }/* os_display_char */ /* * os_display_string * * Pass a string of characters to os_display_char. * */ void os_display_string (const zchar *s) { zchar c; while ((c = *s++) != 0) if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) { int arg = *s++; if (c == ZC_NEW_FONT) os_set_font (arg); if (c == ZC_NEW_STYLE) os_set_text_style (arg); } else os_display_char (c); }/* os_display_string */ /* * os_char_width * * Return the width of the character in screen units. * */ int os_char_width (zchar c) { /* Handle accented characters */ if (c >= ZC_LATIN1_MIN && (story_id != BEYOND_ZORK || (h_flags & GRAPHICS_FLAG))) if (display == _CGA_ || display == _MCGA_) { const char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); int width = 0; char c1 = *ptr++; char c2 = *ptr++; char c3 = *ptr++; width = os_char_width (c1); if (c2 != ' ') width += os_char_width (c2); if (c3 != ' ') width += os_char_width (c3); return width; } else if (display == _AMIGA_ && current_font == TEXT_FONT && !(current_style & FIXED_WIDTH_STYLE) && user_font != 0) if (c >= ZC_LATIN1_MIN) c -= 32; /* Handle special indentations */ if (c == ZC_INDENT) return 3 * os_char_width (' '); if (c == ZC_GAP) return 2 * os_char_width (' '); /* Calculate width */ if (display <= _TEXT_) return 1; if (display == _CGA_) return 8; if (display == _EGA_) return 8; if (current_font == GRAPHICS_FONT) return 8; if (current_font == FIXED_WIDTH_FONT || (current_style & FIXED_WIDTH_STYLE) || (display == _AMIGA_ && user_font == 0)) return (display == _AMIGA_) ? 8 : 5; if (display == _MCGA_) return mcga_width[c - 32]; if (display == _AMIGA_) return serif_width[c - 32] + ((current_style & BOLDFACE_STYLE) ? 1 : 0); return 0; }/* os_char_width */ /* * os_string_width * * Calculate the length of a word in screen units. Apart from letters, * the word may contain special codes: * * ZC_NEW_STYLE - next character is a new text style * ZC_NEW_FONT - next character is a new font * */ int os_string_width (const zchar *s) { int width = 0; int saved_font = current_font; int saved_style = current_style; zchar c; while ((c = *s++) != 0) if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) { int arg = *s++; if (c == ZC_NEW_FONT) current_font = arg; if (c == ZC_NEW_STYLE) current_style = arg; } else width += os_char_width (c); current_font = saved_font; current_style = saved_style; return width; }/* os_string_width */ /* * os_set_cursor * * Place the text cursor at the given coordinates. Top left is (1,1). * */ void os_set_cursor (int y, int x) { cursor_y = y - 1; cursor_x = x - 1; }/* os_set_cursor */ /* * os_more_prompt * * Display a MORE prompt, wait for a keypress and remove the MORE * prompt from the screen. * */ void os_more_prompt (void) { int saved_x; /* Save text font and style */ int saved_font = current_font; int saved_style = current_style; /* Choose plain text style */ current_font = TEXT_FONT; current_style = 0; adjust_style (); /* Wait until the user presses a key */ saved_x = cursor_x; os_display_string ((zchar *) "[MORE]"); os_read_key (0, TRUE); os_erase_area (cursor_y + 1, saved_x + 1, cursor_y + h_font_height, cursor_x + 1, -1); cursor_x = saved_x; /* Restore text font and style */ current_font = saved_font; current_style = saved_style; adjust_style (); }/* os_more_prompt */ frotz-2.44/src/dos/bcfrotz.h0000644000175000017500000000424312527045477012746 00000000000000/* * "BCfrotz.h" * * Borland C interface, declarations * */ #define MASK_LINEAR(addr) (addr & 0x000FFFFF) #define RM_TO_LINEAR(addr) (((addr & 0xFFFF0000) >> 12) + (addr & 0xFFFF)) #define RM_OFFSET(addr) (addr & 0xF) #define RM_SEGMENT(addr) ((addr >> 4) & 0xFFFF) #define OS_PATHSEP ';' #define byte0(v) ((byte *)&v)[0] #define byte1(v) ((byte *)&v)[1] #define byte2(v) ((byte *)&v)[2] #define byte3(v) ((byte *)&v)[3] #define word0(v) ((word *)&v)[0] #define word1(v) ((word *)&v)[1] #ifndef HISTORY_MIN_ENTRY #define HISTORY_MIN_ENTRY 1 #endif #define SPECIAL_KEY_MIN 256 #define SPECIAL_KEY_HOME 256 #define SPECIAL_KEY_END 257 #define SPECIAL_KEY_WORD_LEFT 258 #define SPECIAL_KEY_WORD_RIGHT 259 #define SPECIAL_KEY_DELETE 260 #define SPECIAL_KEY_INSERT 261 #define SPECIAL_KEY_PAGE_UP 262 #define SPECIAL_KEY_PAGE_DOWN 263 #define SPECIAL_KEY_TAB 264 #define SPECIAL_KEY_MAX 264 #define _MONO_ 0 #define _TEXT_ 1 #define _CGA_ 2 #define _MCGA_ 3 #define _EGA_ 4 #define _AMIGA_ 5 typedef unsigned char byte; typedef unsigned short word; extern display; extern cursor_x; extern cursor_y; extern char latin1_to_ibm[]; extern char latin1_to_ascii[]; extern byte text_bg; extern byte text_fg; extern byte scrn_attr; extern user_background; extern user_foreground; extern user_emphasis; extern user_reverse_bg; extern user_reverse_fg; extern user_screen_height; extern user_screen_width; extern user_tandy_bit; extern user_bold_typing; extern user_random_seed; extern user_font; extern char stripped_story_name[]; extern char *prog_name; extern current_bg; extern current_fg; extern current_style; extern current_font; extern scaler; #ifdef SOUND_SUPPORT extern volatile int end_of_sound_flag; #endif /* BCinit */ int dectoi (const char *); /* BCinit */ int hextoi (const char *); /* BCmouse */ bool detect_mouse (void); /* BCmouse */ int read_mouse (void); /* BCpic */ bool init_pictures (void); /* BCpic */ void reset_pictures (void); #ifdef SOUND_SUPPORT /* BCsmpl */ bool dos_init_sound (void); /* BCsmpl */ void dos_reset_sound (void); /* BCinput */ void end_of_sound(void); #endif /* BCtext */ void switch_scrn_attr (bool); /* BCtext */ void load_fonts (void); frotz-2.44/src/dos/bcblorb.c0000644000175000017500000005434212527045477012702 00000000000000/* blorblib.c: Blorb file reader library, version 1.0.2. Designed by Andrew Plotkin http://www.eblong.com/zarf/blorb/index.html This is portable code to read a Blorb file. Add it to your interpreter, #include "blorb.h", and you're ready to go. */ #include #include #include "blorb.h" #include "blorblow.h" /* This endian stuff needs to be fixed with something from * http://www.ibm.com/developerworks/aix/library/au-endianc/index.html */ #ifdef BLORB_BIG_ENDIAN static char contentsticker[] = "\nBlorb Library 1.0 (big-endian)\n"; #define bb_native2(v) (v) #define bb_native4(v) (v) #endif #ifdef BLORB_LITTLE_ENDIAN static char contentsticker[] = "\nBlorb Library 1.0 (little-endian)\n"; #define bb_native2(v) \ ( (((uint16)(v) >> 8) & 0x00ff) \ | (((uint16)(v) << 8) & 0xff00)) #define bb_native4(v) \ ( (((uint32)(v) >> 24) & 0x000000ff) \ | (((uint32)(v) >> 8) & 0x0000ff00) \ | (((uint32)(v) << 8) & 0x00ff0000) \ | (((uint32)(v) << 24) & 0xff000000)) #endif #ifdef CRAPOLA #ifndef bb_native4 #error "You must define either BLORB_BIG_ENDIAN" #error "or BLORB_LITTLE_ENDIAN in blorb.h in order" #error "to compile this library." #endif #endif /* CRAPOLA */ static int lib_inited = FALSE; static bb_err_t bb_initialize_map(bb_map_t *map); static bb_err_t bb_initialize(void); static int sortsplot(const void *p1, const void *p2); /* Do some one-time startup tests. */ static bb_err_t bb_initialize() { union { uint32 val; unsigned char ch[4]; } test; uint32 val; if (sizeof(uint32) != 4 || sizeof(uint16) != 2) return bb_err_CompileTime; /* Basic types are the wrong size. */ test.ch[0] = 0x13; test.ch[1] = 0x57; test.ch[2] = 0x9a; test.ch[3] = 0xce; val = test.val; if (bb_native4(val) != 0x13579ace) return bb_err_CompileTime; /* Wrong endianness. */ return bb_err_None; } bb_err_t bb_create_map(FILE *file, bb_map_t **newmap) { bb_err_t err; bb_map_t *map; size_t readlen; uint32 nextpos, totallength; bb_chunkdesc_t *chunks; int chunks_size, numchunks; uint32 buffer[4]; *newmap = NULL; if (!lib_inited) { err = bb_initialize(); if (err) return err; lib_inited = TRUE; } /* First, chew through the file and index the chunks. */ err = fseek(file, 0, 0); if (err) return bb_err_Read; /* FIXME. Under 16-bit DOS, this fails. * From the following url: "'unsigned long' variables don't work in * 16-bit mode." How do I get around this? * http://www.digitalmars.com/d/archives/c++/dos/16-bits/45.html */ readlen = fread(buffer, sizeof(uint32), 3, file); if (readlen != 3) return bb_err_Read; if (bb_native4(buffer[0]) != bb_ID_FORM) return bb_err_Format; if (bb_native4(buffer[2]) != bb_ID_IFRS) return bb_err_Format; totallength = bb_native4(buffer[1]) + 8; nextpos = 12; chunks_size = 8; numchunks = 0; chunks = (bb_chunkdesc_t *)malloc(sizeof(bb_chunkdesc_t) * chunks_size); while (nextpos < totallength) { uint32 type, len; int chunum; bb_chunkdesc_t *chu; err = fseek(file, nextpos, 0); if (err) return bb_err_Read; readlen = fread(buffer, sizeof(uint32), 2, file); if (readlen != 2) return bb_err_Read; type = bb_native4(buffer[0]); len = bb_native4(buffer[1]); if (numchunks >= chunks_size) { chunks_size *= 2; chunks = (bb_chunkdesc_t *)realloc(chunks, sizeof(bb_chunkdesc_t) * chunks_size); } chunum = numchunks; chu = &(chunks[chunum]); numchunks++; chu->type = type; chu->startpos = nextpos; if (type == bb_ID_FORM) { chu->datpos = nextpos; chu->len = len+8; } else { chu->datpos = nextpos+8; chu->len = len; } chu->ptr = NULL; chu->auxdatnum = -1; nextpos = nextpos + len + 8; if (nextpos & 1) nextpos++; if (nextpos > totallength) return bb_err_Format; } /* The basic IFF structure seems to be ok, and we have a list of chunks. Now we allocate the map structure itself. */ map = (bb_map_t *)malloc(sizeof(bb_map_t)); if (!map) { free(chunks); return bb_err_Alloc; } map->inited = bb_Inited_Magic; map->file = file; map->chunks = chunks; map->numchunks = numchunks; map->resources = NULL; map->ressorted = NULL; map->numresources = 0; map->releasenum = 0; map->zheader = NULL; map->resolution = NULL; map->palettechunk = -1; map->palette = NULL; map->auxsound = NULL; map->auxpict = NULL; /* Now we do everything else involved in loading the Blorb file, such as building resource lists. */ err = bb_initialize_map(map); if (err) { bb_destroy_map(map); return err; } *newmap = map; return bb_err_None; } static bb_err_t bb_initialize_map(bb_map_t *map) { /* It is important that the map structure be kept valid during this function. If this returns an error, bb_destroy_map() will be called. */ int ix, jx; bb_result_t chunkres; bb_err_t err; uint32 *ptr; uint32 len; uint32 val; int numres; int gotindex = FALSE; for (ix=0; ixnumchunks; ix++) { bb_chunkdesc_t *chu = &map->chunks[ix]; switch (chu->type) { case bb_ID_RIdx: /* Resource index chunk: build the resource list and sort it. */ if (gotindex) return bb_err_Format; /* duplicate index chunk */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; ptr = chunkres.data.ptr; len = chunkres.length; val = ptr[0]; numres = bb_native4(val); if (numres) { int ix2; bb_resdesc_t *resources; bb_resdesc_t **ressorted; if (len != numres*12+4) return bb_err_Format; /* bad length field */ resources = (bb_resdesc_t *)malloc(numres * sizeof(bb_resdesc_t)); ressorted = (bb_resdesc_t **)malloc(numres * sizeof(bb_resdesc_t *)); if (!ressorted || !resources) return bb_err_Alloc; ix2 = 0; for (jx=0; jxusage = bb_native4(val); val = ptr[2+jx*3]; res->resnum = bb_native4(val); val = ptr[3+jx*3]; respos = bb_native4(val); while (ix2 < map->numchunks && map->chunks[ix2].startpos < respos) ix2++; if (ix2 >= map->numchunks || map->chunks[ix2].startpos != respos) return bb_err_Format; /* start pos does not match a real chunk */ res->chunknum = ix2; ressorted[jx] = res; } /* Sort a resource list (actually a list of pointers to structures in map->resources.) This makes it easy to find resources by usage and resource number. */ qsort(ressorted, numres, sizeof(bb_resdesc_t *), &sortsplot); map->numresources = numres; map->resources = resources; map->ressorted = ressorted; } bb_unload_chunk(map, ix); gotindex = TRUE; break; case bb_ID_RelN: /* Release number chunk: Get the release number. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; if (chunkres.length != 2) return bb_err_Format; { uint16 val = *((uint16 *)chunkres.data.ptr); map->releasenum = bb_native2(val); } bb_unload_chunk(map, ix); break; case bb_ID_IFhd: /* Z-header chunk: Get the header info. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; if (chunkres.length < 13) return bb_err_Format; { uint16 val; bb_zheader_t *head = (bb_zheader_t *)malloc(sizeof(bb_zheader_t)); if (!head) return bb_err_Alloc; val = ((uint16 *)(chunkres.data.ptr))[0]; head->releasenum = bb_native2(val); val = ((uint16 *)(chunkres.data.ptr))[4]; head->checksum = bb_native2(val); for (jx=0; jx<6; jx++) { head->serialnum[jx] = ((char *)(chunkres.data.ptr))[2+jx]; } map->zheader = head; } bb_unload_chunk(map, ix); break; case bb_ID_Reso: /* Resolution chunk: Get the window size data, and resolution ratios for images. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; if (chunkres.length < 24) return bb_err_Format; ptr = chunkres.data.ptr; len = chunkres.length; { bb_resolution_t *reso = (bb_resolution_t *)malloc(sizeof(bb_resolution_t)); if (!reso) return bb_err_Alloc; reso->px = bb_native4(ptr[0]); reso->py = bb_native4(ptr[1]); reso->minx = bb_native4(ptr[2]); reso->miny = bb_native4(ptr[3]); reso->maxx = bb_native4(ptr[4]); reso->maxy = bb_native4(ptr[5]); map->resolution = reso; } ptr += 6; len -= 6*4; len = len / 28; if (len) { bb_aux_pict_t *aux = (bb_aux_pict_t *)malloc(len * sizeof(bb_aux_pict_t)); for (jx=0; jxchunks[res.chunknum]); if (chu->auxdatnum != -1) return bb_err_Format; /* two image entries for this resource */ chu->auxdatnum = jx; aux[jx].ratnum = bb_native4(ptr[1]); aux[jx].ratden = bb_native4(ptr[2]); aux[jx].minnum = bb_native4(ptr[3]); aux[jx].minden = bb_native4(ptr[4]); aux[jx].maxnum = bb_native4(ptr[5]); aux[jx].maxden = bb_native4(ptr[6]); } } map->auxpict = aux; } bb_unload_chunk(map, ix); break; case bb_ID_Loop: /* Looping chunk: Get looping data for sounds. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; ptr = chunkres.data.ptr; len = chunkres.length; len = len / 8; if (len) { bb_aux_sound_t *aux = (bb_aux_sound_t *)malloc(len * sizeof(bb_aux_sound_t)); for (jx=0; jxchunks[res.chunknum]); if (chu->auxdatnum != -1) return bb_err_Format; /* two looping entries for this resource */ chu->auxdatnum = jx; aux[jx].repeats = bb_native4(ptr[1]); } } map->auxsound = aux; } bb_unload_chunk(map, ix); break; case bb_ID_Plte: /* Palette chunk: Don't get the palette info now, since it may be large and the interpreter may not care. But remember the chunk number in case the interpreter asks later. */ map->palettechunk = ix; break; } } return bb_err_None; } bb_err_t bb_destroy_map(bb_map_t *map) { int ix; if (!map || !map->chunks || map->inited != bb_Inited_Magic) return bb_err_NotAMap; for (ix=0; ixnumchunks; ix++) { bb_chunkdesc_t *chu = &(map->chunks[ix]); if (chu->ptr) { free(chu->ptr); chu->ptr = NULL; } } if (map->chunks) { free(map->chunks); map->chunks = NULL; } map->numchunks = 0; if (map->resources) { free(map->resources); map->resources = NULL; } if (map->ressorted) { free(map->ressorted); map->ressorted = NULL; } map->numresources = 0; if (map->zheader) { free(map->zheader); map->zheader = NULL; } if (map->resolution) { free(map->resolution); map->resolution = NULL; } if (map->palette) { if (!map->palette->isdirect && map->palette->data.table.colors) { free(map->palette->data.table.colors); map->palette->data.table.colors = NULL; } free(map->palette); map->palette = NULL; } if (map->auxsound) { free(map->auxsound); map->auxsound = NULL; } if (map->auxpict) { free(map->auxpict); map->auxpict = NULL; } map->file = NULL; map->inited = 0; free(map); return bb_err_None; } /* Turn a four-byte constant into a string. This returns a static buffer, so if you call it twice, the old value gets overwritten. */ char *bb_id_to_string(uint32 id) { static char buf[5]; buf[0] = (id >> 24) & 0xff; buf[1] = (id >> 16) & 0xff; buf[2] = (id >> 8) & 0xff; buf[3] = (id) & 0xff; buf[4] = '\0'; return buf; } /* Turn an error code into a string describing the error. */ char *bb_err_to_string(bb_err_t err) { switch (err) { case bb_err_None: return "ok"; case bb_err_CompileTime: return "library compiled wrong"; case bb_err_Alloc: return "cannot allocate memory"; case bb_err_Read: return "cannot read from file"; case bb_err_NotAMap: return "map structure is bad"; case bb_err_Format: return "bad format in Blorb file"; case bb_err_NotFound: return "data not found"; default: return "unknown error"; } } /* This is used for binary searching and quicksorting the resource pointer list. */ static int sortsplot(const void *p1, const void *p2) { bb_resdesc_t *v1 = *(bb_resdesc_t **)p1; bb_resdesc_t *v2 = *(bb_resdesc_t **)p2; if (v1->usage < v2->usage) return -1; if (v1->usage > v2->usage) return 1; return v1->resnum - v2->resnum; } bb_err_t bb_load_chunk_by_type(bb_map_t *map, int method, bb_result_t *res, uint32 type, int count) { int ix; for (ix=0; ix < map->numchunks; ix++) { if (map->chunks[ix].type == type) { if (count == 0) break; count--; } } if (ix >= map->numchunks) { return bb_err_NotFound; } return bb_load_chunk_by_number(map, method, res, ix); } bb_err_t bb_load_chunk_by_number(bb_map_t *map, int method, bb_result_t *res, int chunknum) { bb_chunkdesc_t *chu; if (chunknum < 0 || chunknum >= map->numchunks) return bb_err_NotFound; chu = &(map->chunks[chunknum]); switch (method) { case bb_method_DontLoad: /* do nothing */ break; case bb_method_FilePos: res->data.startpos = chu->datpos; break; case bb_method_Memory: if (!chu->ptr) { bb_err_t err; size_t readlen; void *dat = malloc(chu->len); if (!dat) return bb_err_Alloc; err = fseek(map->file, chu->datpos, 0); if (err) return bb_err_Read; readlen = fread(dat, 1, chu->len, map->file); if (readlen != chu->len) return bb_err_Read; chu->ptr = dat; } res->data.ptr = chu->ptr; break; } res->chunknum = chunknum; res->length = chu->len; return bb_err_None; } bb_err_t bb_load_resource(bb_map_t *map, int method, bb_result_t *res, uint32 usage, int resnum) { bb_resdesc_t sample; bb_resdesc_t *ptr; bb_resdesc_t **found; sample.usage = usage; sample.resnum = resnum; ptr = &sample; found = bsearch(&ptr, map->ressorted, map->numresources, sizeof(bb_resdesc_t *), &sortsplot); if (!found) return bb_err_NotFound; return bb_load_chunk_by_number(map, method, res, (*found)->chunknum); } bb_err_t bb_unload_chunk(bb_map_t *map, int chunknum) { bb_chunkdesc_t *chu; if (chunknum < 0 || chunknum >= map->numchunks) return bb_err_NotFound; chu = &(map->chunks[chunknum]); if (chu->ptr) { free(chu->ptr); chu->ptr = NULL; } return bb_err_None; } bb_err_t bb_count_resources(bb_map_t *map, uint32 usage, int *num, int *min, int *max) { int ix; int count, minval, maxval, val; count = 0; minval = 0; maxval = 0; for (ix=0; ixnumresources; ix++) { if (map->resources[ix].usage == usage) { val = map->resources[ix].resnum; if (count == 0) { count++; minval = val; maxval = val; } else { count++; if (val < minval) minval = val; if (val > maxval) maxval = val; } } } if (num) *num = count; if (min) *min = minval; if (max) *max = maxval; return bb_err_None; } uint16 bb_get_release_num(bb_map_t *map) { return map->releasenum; } bb_zheader_t *bb_get_zheader(bb_map_t *map) { return map->zheader; } bb_resolution_t *bb_get_resolution(bb_map_t *map) { return map->resolution; } bb_err_t bb_get_palette(bb_map_t *map, bb_palette_t **res) { int ix; bb_err_t err; if (res) *res = NULL; if (map->palettechunk < 0) { return bb_err_None; } if (!map->palette) { bb_result_t chunkres; bb_palette_t *pal; unsigned char *ptr; pal = (bb_palette_t *)malloc(sizeof(bb_palette_t)); if (!pal) return bb_err_Alloc; err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, map->palettechunk); if (err) return err; ptr = chunkres.data.ptr; if (chunkres.length == 1) { int val = ptr[0]; if (val != 16 && val != 32) return bb_err_Format; pal->isdirect = TRUE; pal->data.depth = val; } else { int size = chunkres.length / 3; bb_color_t *colors = (bb_color_t *)malloc(size * sizeof(bb_color_t)); if (!colors) return bb_err_Alloc; if (size < 1 || size > 256) return bb_err_Format; for (ix=0; ixisdirect = FALSE; pal->data.table.numcolors = size; pal->data.table.colors = colors; } bb_unload_chunk(map, map->palettechunk); map->palette = pal; } if (res) *res = map->palette; return bb_err_None; } bb_err_t bb_load_resource_pict(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_pict_t **auxdata) { bb_err_t err; if (auxdata) *auxdata = NULL; err = bb_load_resource(map, method, res, bb_ID_Pict, resnum); if (err) return err; if (auxdata) { bb_chunkdesc_t *chu = &(map->chunks[res->chunknum]); if (chu->auxdatnum >= 0 && map->auxpict) { *auxdata = &(map->auxpict[chu->auxdatnum]); } } return bb_err_None; } bb_err_t bb_load_resource_snd(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_sound_t **auxdata) { bb_err_t err; if (auxdata) *auxdata = NULL; /* err = bb_load_resource(map, method, res, bb_ID_Pict, resnum); */ err = bb_load_resource(map, method, res, bb_ID_Snd, resnum); if (err) return err; if (auxdata) { bb_chunkdesc_t *chu = &(map->chunks[res->chunknum]); if (chu->auxdatnum >= 0 && map->auxsound) { *auxdata = &(map->auxsound[chu->auxdatnum]); } } return bb_err_None; } frotz-2.44/src/dos/bcscreen.c0000644000175000017500000001137112527045477013054 00000000000000/* * file "BCscreen.c" * * Borland C front end, screen manipulation * */ #include #include #include "frotz.h" #include "BCfrotz.h" /* * get_scrnptr * * Return a pointer to the given line in video RAM. * */ byte far *get_scrnptr (int y) { if (display == _CGA_) return MK_FP ((y & 1) ? 0xba00 : 0xb800, 40 * (y & ~1)); else if (display == _MCGA_) return MK_FP (0xa000, 320 * y); else return MK_FP (0xa000, 80 * y); }/* get_scrnptr */ /* * clear_byte * * Helper function for clear_line. * */ static void clear_byte (byte far *scrn, word mask) { if (display == _CGA_) if (scrn_attr == 0) *scrn &= ~mask; else *scrn |= mask; else { outport (0x03ce, 0x0205); outportb (0x03ce, 0x08); outportb (0x03cf, mask); asm les bx,scrn asm mov al,es:[bx] asm mov al,scrn_attr asm mov es:[bx],al } }/* clear_byte */ /* * clear_line * * Helper function for os_erase_area. * */ static void clear_line (int y, int left, int right) { byte far *scrn = get_scrnptr (y); if (display == _MCGA_) _fmemset (scrn + left, scrn_attr, right - left + 1); else { word mask1 = 0x00ff >> (left & 7); word mask2 = 0xff80 >> (right & 7); int x = right / 8 - left / 8; scrn += left / 8; if (x == 0) { mask1 &= mask2; mask2 = 0; } /* Clear first byte */ clear_byte (scrn++, mask1); /* Clear middle bytes */ if (display >= _EGA_) outport (0x03ce, 0xff08); while (--x > 0) *scrn++ = scrn_attr; /* Clear last byte */ clear_byte (scrn, mask2); } }/* clear_line */ /* * os_erase_area * * Fill a rectangular area of the screen with the current background * colour. Top left coordinates are (1,1). The cursor does not move. * * The final argument gives the window being changed, -1 if only a * portion of a window is being erased, or -2 if the whole screen is * being erased. This is not relevant for the DOS interface, and so * this function ignores that argument. * */ void os_erase_area (int top, int left, int bottom, int right, int win) { int y; top--; left--; bottom--; right--; if (display <= _TEXT_) { asm mov ax,0x0600 asm mov ch,byte ptr top asm mov cl,byte ptr left asm mov dh,byte ptr bottom asm mov dl,byte ptr right asm mov bh,scrn_attr asm int 0x10 } else for (y = top; y <= bottom; y++) clear_line (y, left, right); }/* os_erase_area */ /* * copy_byte * * Helper function for copy_line. * */ static void copy_byte (byte far *scrn1, byte far *scrn2, byte mask) { int i; if (display == _CGA_) *scrn1 = (*scrn1 & ~mask) | (*scrn2 & mask); else { outport (0x03ce, 0x0005); outportb (0x03ce, 0x08); outportb (0x03cf, mask); outportb (0x03ce, 0x04); outportb (0x03c4, 0x02); for (i = 0; i < 4; i++) { outportb (0x03cf, i); outportb (0x03c5, 1 << i); asm les bx,scrn2 asm mov ah,es:[bx] asm les bx,scrn1 asm mov al,es:[bx] asm mov es:[bx],ah } outportb (0x03c5, 0x0f); } }/* copy_byte */ /* * copy_line * * Helper function for os_scroll_area. * */ static void copy_line (int y1, int y2, int left, int right) { byte far *scrn1 = get_scrnptr (y1); byte far *scrn2 = get_scrnptr (y2); if (display == _MCGA_) _fmemcpy (scrn1 + left, scrn2 + left, right - left + 1); else { word mask1 = 0x00ff >> (left & 7); word mask2 = 0xff80 >> (right & 7); int x = right / 8 - left / 8; scrn1 += left / 8; scrn2 += left / 8; if (x == 0) { mask1 &= mask2; mask2 = 0; } /* Copy first byte */ copy_byte (scrn1++, scrn2++, mask1); /* Copy middle bytes */ if (display >= _EGA_) outport (0x03ce, 0x0105); while (--x > 0) *scrn1++ = *scrn2++; /* Copy last byte */ copy_byte (scrn1, scrn2, mask2); } }/* copy_line */ /* * os_scroll_area * * Scroll a rectangular area of the screen up (units > 0) or down * (units < 0) and fill the empty space with the current background * colour. Top left coordinates are (1,1). The cursor stays put. * */ void os_scroll_area (int top, int left, int bottom, int right, int units) { int y; top--; left--; bottom--; right--; if (display <= _TEXT_) { asm mov ah,6 asm mov bx,units asm cmp bx,0 asm jg scroll asm mov ah,7 asm neg bx scroll: asm mov al,bl asm mov ch,byte ptr top asm mov cl,byte ptr left asm mov dh,byte ptr bottom asm mov dl,byte ptr right asm mov bh,scrn_attr asm int 0x10 } else if (units > 0) for (y = top; y <= bottom; y++) if (y <= bottom - units) copy_line (y, y + units, left, right); else clear_line (y, left, right); else for (y = bottom; y >= top; y--) if (y >= top - units) copy_line (y, y + units, left, right); else clear_line (y, left, right); }/* os_scroll_area */ frotz-2.44/src/dos/bcpic.c0000644000175000017500000003750112527045477012353 00000000000000/* * "BCpic.c" * * Borland C front end, picture functions * */ #include #include #include #include #include "frotz.h" #include "BCfrotz.h" #define PIC_NUMBER 0 #define PIC_WIDTH 2 #define PIC_HEIGHT 4 #define PIC_FLAGS 6 #define PIC_DATA 8 #define PIC_COLOUR 11 #define READ_BYTE(v,p,o) v = *(byte far *)(p+o) #define READ_WORD(v,p,o) v = *(word far *)(p+o) /* This may be troublesome later */ /* extern byte far *get_scrnptr (int); */ extern unsigned long get_scrnptr (int); extern FILE *os_path_open (const char *, const char *); static struct { byte fileno; byte flags; word unused1; word images; word link; byte entry_size; byte padding; word checksum; word unused2; word version; } gheader; int scaler = 1; static word pic_width = 0; static word pic_height = 0; static word pic_flags = 0; static long pic_data = 0; static long pic_colour = 0; static byte far *table_val = NULL; static word far *table_ref = NULL; static FILE *file = NULL; static byte far *info = NULL; /* * open_graphics_file * * Open a graphics file. EGA pictures may be stored in two separate * graphics files. * */ static bool open_graphics_file (int number) { char fname[MAX_FILE_NAME + 1]; char extension[4 + 1]; /* Build graphics file name */ extension[0] = '.'; extension[1] = "cmem"[display - 2]; extension[2] = 'g'; extension[3] = '0' + number; extension[4] = 0; /* Why does DOS not like "graphics\\" anymore? */ strcpy (fname, "graphics/"); strcat (fname, f_setup.story_name); strcat (fname, extension); /* Open file, load header, allocate memory, load picture directory */ if ((file = fopen (fname, "rb")) == NULL) goto failure1; if (fread (&gheader, sizeof (gheader), 1, file) != 1) goto failure2; if ((info = farmalloc (gheader.images * gheader.entry_size)) == NULL) goto failure2; if (fread (info, gheader.entry_size, gheader.images, file) != gheader.images) goto failure3; return TRUE; failure3: farfree (info); info = NULL; failure2: fclose (file); file = NULL; failure1: return FALSE; }/* open_graphics_file */ /* * close_graphics_file * * Free resources allocated for pictures. * */ static void close_graphics_file (void) { if (file != NULL) { fclose (file); file = NULL; } if (info != NULL) { farfree (info); info = NULL; } }/* close_graphics_file */ /* * init_pictures * * Prepare to draw pictures. Return true if pictures are available. * */ bool init_pictures (void) { /* Allocate memory for decompression */ table_val = (byte far *) farmalloc (3 * 3840); table_ref = (word far *) (table_val + 3840); if (table_val == NULL) return FALSE; /* Open the [first of two] graphics file[s] */ return open_graphics_file (1); }/* init_pictures */ /* * reset_pictures * * Free resources allocated for decompression of pictures. * */ void reset_pictures (void) { if (table_val != NULL) { farfree (table_val); table_val = NULL; } if (file != NULL) { fclose (file); file = NULL; } if (info != NULL) { farfree (info); info = NULL; } }/* reset_pictures */ /* * load_picture_info * * Helper function for os_picture_data. Load all information about * the given picture from the graphics file and store it in global * variables. * */ static bool load_picture_info (int picture) { byte far *ptr; byte fileno; fileno = gheader.fileno; do { int i; /* Abort if there is a problem with the graphics file */ if (file == NULL) return FALSE; /* Scan the directory of the current graphics file */ ptr = info; for (i = 0; i < gheader.images; i++) { if (picture == * (int far *) ptr) { READ_WORD (pic_width, ptr, PIC_WIDTH); READ_WORD (pic_height, ptr, PIC_HEIGHT); READ_WORD (pic_flags, ptr, PIC_FLAGS); pic_height *= scaler; pic_width *= scaler; READ_BYTE (byte0 (pic_data), ptr, PIC_DATA + 2); READ_BYTE (byte1 (pic_data), ptr, PIC_DATA + 1); READ_BYTE (byte2 (pic_data), ptr, PIC_DATA); if (gheader.entry_size > PIC_COLOUR + 2) { READ_BYTE (byte0 (pic_colour), ptr, PIC_COLOUR + 2); READ_BYTE (byte1 (pic_colour), ptr, PIC_COLOUR + 1); READ_BYTE (byte2 (pic_colour), ptr, PIC_COLOUR); } else pic_colour = 0; return TRUE; } ptr += gheader.entry_size; } /* Close current graphics file */ close_graphics_file (); /* Open next graphics file */ open_graphics_file ((gheader.link != 0) ? gheader.fileno + 1 : 1); } while (fileno != gheader.fileno); return FALSE; }/* load_picture_info */ /* * load_colour_map * * Helper function for os_draw_picture. Load a colour map from the * graphics file then copy it to the palette registers. * */ static void load_colour_map (int first_colour) { byte rgb[42]; int n, i; fseek (file, pic_colour, SEEK_SET); /* Some pictures from Arthur mistakenly claim to have 16 colours */ if ((n = fgetc (file)) == 16) n = 14; /* Each colour is stored in three bytes R-G-B */ fread (rgb, 3, n, file); /* MCGA boards can only handle R-G-B values from 0 to 63 */ for (i = 0; i < 42; i++) rgb[i] = (rgb[i] * 63 + 128) / 255; /* Synchronise with vertical retrace */ while ((inportb (0x03da) & 8) == 0); while ((inportb (0x03da) & 8) == 8); /* Copy colours to palette registers */ asm mov ax,0x1012 asm mov bx,first_colour asm mov cx,n asm lea dx,rgb asm push ss asm pop es asm int 0x10 }/* load_colour_map */ /* * draw_picture * * Helper function for os_draw_picture. The real work of drawing a * picture happens here. * */ #pragma warn -def static void pascal draw_picture (int y, int x) { static int raise_bits[4] = { 0x0100, 0x0300, 0x0700, 0x0000 }; byte buf[512]; unsigned long screen; byte transparent; byte colour_shift; int first_colour; int code, prev_code; int next_entry; int bits_per_code; int bits_shift; int bits; int current_y; int current_x; int bufpos; int pixels; int i; bufpos = 0; /* When the given picture provides a colour map then activate it. This is only used for MCGA pictures; the colour map affects every picture on the screen. The first colour to be defined is colour 2. Every map defines up to 14 colours (colour 2 to 15). These colours are not related to the standard Z-machine colour scheme which remains unchanged. (This is based on the Amiga interpreter which had to work with 16 colours. Colours 0 and 1 were used for text; changing the text colours actually changed palette entries 0 and 1. This interface uses the same trick in Amiga mode.) */ if (display == _CGA_) colour_shift = -2; if (display == _EGA_) colour_shift = 0; if (display == _MCGA_) { colour_shift = 32; first_colour = 34; } if (display == _AMIGA_) { colour_shift = -1; first_colour = 65; } if (pic_colour != 0) load_colour_map (first_colour); fseek (file, pic_data, SEEK_SET); /* Bit 0 of "flags" indicates that the picture uses a transparent colour, the top four bits tell us which colour it is. For CGA and MCGA pictures this is always 0; for EGA pictures it can be any colour between 0 and 15. */ transparent = 0xff; if (pic_flags & 1) transparent = pic_flags >> 12; /* Prepare EGA hardware for setting pixels */ if (display >= _EGA_) { outport (0x03ce, 0x0205); outport (0x03ce, 0xff08); } /* The uncompressed picture is a long sequence of bytes. Every byte holds the colour of a pixel, starting at the top left, stopping at the bottom right. We keep track of our position in the current line. (There is a special case: CGA pictures with no transparent colour are stored as bit patterns, i.e. every byte holds the pattern for eight pixels. A pixel must be white if the corresponding bit is set, otherwise it must be black.) */ current_x = x + pic_width; current_y = y - 1; /* The compressed picture is a stream of bits. We read the file byte-wise, storing the current byte in the variable "bits". Several bits make one code; the variable "bits_shift" helps us to build the next code. */ bits_shift = 0; bits = 0; reset_table: /* Clear the table. We use a table of 3840 entries. Each entry consists of both a value and a reference to another table entry. Following these references we get a sequence of values. At the start of decompression all table entries are undefined. Later we see how entries are set and used. */ next_entry = 1; /* At the start of decompression 9 bits make one code; during the process this can rise to 12 bits per code. 9 bits are sufficient to address both 256 literal values and 256 table entries; 12 bits are sufficient to address both 256 literal values and all 3840 table entries. The number of bits per code rises with the number of table entries. When the table is cleared, the number of bits per code drops back to 9. */ bits_per_code = 9; next_code: /* Read the next code from the graphics file. This requires some confusing bit operations. Note that low bits always come first. Usually there are a few bits left over from the previous code; these bits must be used before further bits are read from the graphics file. */ code = bits >> (8 - bits_shift); do { bits = fgetc (file); code |= bits << bits_shift; bits_shift += 8; } while (bits_shift < bits_per_code); bits_shift -= bits_per_code; code &= 0xfff >> (12 - bits_per_code); /* There are two codes with a special meaning. The first one is 256 which clears the table and sets the number of bits per code to 9. (This is necessary when the table is full.) The second one is 257 which marks the end of the picture. For the sake of efficiency, we drecement the code by 256. */ code -= 256; if (code == 0) goto reset_table; if (code == 1) return; /* Codes from 0 to 255 are literals, i.e. they represent a plain byte value. Codes from 258 onwards are references to table entries, i.e. they represent a sequence of byte values (see the remarks on the table above). This means that for each code one or several byte values are added to the decompressed picture. But there is yet more work to do: Every time we read a code one table entry is set. As we said above, a table entry consist of both a value and a reference to another table entry. If the current code is a literal, then the value has to be set to this literal; but if the code refers to a sequence of byte values, then the value has to be set to the last byte of this sequence. In any case, the reference is set to the previous code. Finally, one should be aware that a code may legally refer to the table entry which is currently being set. This requires some extra care. */ table_ref[next_entry] = prev_code; prev_code = code; while (code >= 0) { buf[bufpos++] = table_val[code]; code = (short) table_ref[code]; } if (next_entry == prev_code) buf[0] = code; table_val[next_entry] = code; /* The number of bits per code is incremented when the current number of bits no longer suffices to address all defined table entries; but in any case the number of bits may never be greater than 12. */ next_entry++; if (next_entry == raise_bits[bits_per_code - 9]) bits_per_code++; reverse_buffer: /* Append the sequence of byte values (pixels) to the picture. The order of the sequence must be reversed. (This is why we have stored the sequence in a buffer; experiments show that a buffer of 512 bytes suffices.) The sequence of values may spread over several lines of the picture, so we must take care to start a new line when we reach the right border of the picture. */ if (current_x == x + pic_width) { screen = get_scrnptr (current_y); current_x -= pic_width; current_y += scaler; } /* Either add a single pixel or a pattern of eight bits (b/w CGA pictures without a transparent colour) to the current line. Increment our position by 1 or 8 respectively. The pixel may have to be painted several times if the scaling factor is greater than one. */ if (display == _CGA_ && transparent == 0xff) { pixels = x + pic_width - current_x; if (pixels > 8) pixels = 8; asm les bx,screen asm mov dx,current_x asm dec dx asm push dx asm mov cl,3 asm shr dx,cl asm add bx,dx asm mov ax,es:[bx] asm mov dx,0xffff asm mov cl,byte ptr pixels asm shr dl,cl asm pop cx asm and cl,7 asm ror dx,cl asm and ax,dx asm mov dx,code asm inc dh asm ror dx,cl asm or ax,dx asm mov es:[bx],ax current_x += pixels; } else for (i = 0; i < scaler; i++) { _AH = code; if (_AH != transparent) { asm add ah,colour_shift asm les bx,screen asm mov dx,current_x asm dec dx if (display != _MCGA_) { asm push dx asm mov cl,3 asm shr dx,cl asm pop cx asm and cl,7 asm add bx,dx asm mov al,es:[bx] if (display == _CGA_) { asm mov dl,0x7f asm ror dl,cl asm and al,dl asm xor ah,1 asm ror ah,1 asm shr ah,cl asm or ah,al } else { asm mov al,0x80 asm shr al,cl asm mov dx,0x03cf asm out dx,al } } else asm add bx,dx asm mov es:[bx],ah if (display == _AMIGA_) { asm add bx,80 asm mov al,es:[bx] asm mov es:[bx],ah } } current_x++; } /* If there are no more values in the buffer then read the next code from the file. Otherwise fetch the next byte value from the buffer and continue painting the picture. */ if (bufpos == 0) goto next_code; byte0 (code) = buf[--bufpos]; goto reverse_buffer; }/* draw_picture */ #pragma warn +def /* * os_draw_picture * * Display a picture at the given coordinates. Top left is (1,1). * */ void os_draw_picture (int picture, int y, int x) { if (load_picture_info (picture)) draw_picture (y, x); }/* os_draw_picture */ /* * os_peek_colour * * Return the colour of the pixel below the cursor. This is used * by V6 games to print text on top of pictures. The coulor need * not be in the standard set of Z-machine colours. To handle * this situation, Frotz extends the colour scheme: Values above * 15 (and below 256) may be used by the interface to refer to * non-standard colours. Of course, os_set_colour must be able to * deal with these colours. Interfaces which refer to characters * instead of pixels might return the current background colour * instead. * */ int os_peek_colour (void) { if (display >= _CGA_) { asm mov ah,13 asm mov bh,0 asm mov cx,cursor_x asm mov dx,cursor_y asm int 0x10 asm mov ah,0 return _AX + 16; } else return current_bg; }/* os_peek_colour */ /* * os_picture_data * * Return true if the given picture is available. If so, write the * width and height of the picture into the appropriate variables. * Only when picture 0 is asked for, write the number of available * pictures and the release number instead. * */ bool os_picture_data (int picture, int *height, int *width) { bool avail; if (picture == 0) { avail = FALSE; /* This is the special case mentioned above. In practice, only the release number is used; and even this is only used by the DOS version of "Zork Zero". Infocom's Amiga interpreter could not handle this feature, and the Amiga version of the story file does not use it. */ pic_height = gheader.images; pic_width = gheader.version; } else avail = load_picture_info (picture); *height = pic_height; *width = pic_width; return avail; }/* os_picture_data */ frotz-2.44/src/dos/bcinput.c0000644000175000017500000004476312527045477012747 00000000000000/* * file "BCinput.c" * * Borland C front end, input functions * */ #include #include #include #include "frotz.h" #include "bcfrotz.h" #ifndef HISTORY_BUFSIZE #define HISTORY_BUFSIZE 500 #endif extern bool is_terminator (zchar); extern bool read_yes_or_no (const char *); extern void read_string (int, zchar *); extern int completion (const zchar *, zchar *); static long limit = 0; static struct { zchar buffer[HISTORY_BUFSIZE]; int latest; int current; int prefix_len; } history; static struct { zchar *buffer; int pos; int length; int max_length; int width; int max_width; } input; static bool overwrite = FALSE; int end_of_sound_flag; /* * swap_colours * * This is a small helper function for switch_cursor. It swaps the * current background and foreground colours. * */ static void swap_colours (void) { byte temp; temp = text_fg; text_fg = text_bg; text_bg = temp; }/* swap_colours */ /* * switch_cursor * * Turn cursor on/off. If there is mouse support then turn the mouse * pointer on/off as well. The cursor should not be moved and the * contents of the screen should not be changed while the cursor is * visible (because of the primitive cursor emulation we use here). * */ static void switch_cursor (bool cursor) { if (display <= _TEXT_) { /* Use hardware cursor in text mode */ if (display == _MONO_) _CX = overwrite ? 0x080f : 0x0a0b; else _CX = overwrite ? 0x0408 : 0x0506; if (!cursor) _CX = 0xffff; asm mov ah,2 asm mov bh,0 asm mov dh,byte ptr cursor_y asm mov dl,byte ptr cursor_x asm int 0x10 asm mov ah,1 asm int 0x10 } else { int saved_x = cursor_x; if (cursor) swap_colours (); if (input.pos < input.length) os_display_char (input.buffer[input.pos]); else os_display_char (' '); if (cursor) swap_colours (); cursor_x = saved_x; } }/* switch_cursor */ /* * get_current_time * * Return the current system time in 1/10 seconds. * */ static long get_current_time (void) { long time; /* Get the current time of day measured in 65536 / 1,193,180 = 0.054925493 seconds. Multiply this value with 959 / 1746 = 0.54925544 to get the current time in 0.1 seconds. */ asm mov ah,0 asm int 0x1a asm mov word ptr time,dx asm mov word ptr time + 2,cx return time * 959 / 1746; }/* get_current_time */ /* * set_timer * * Set a time limit of timeout/10 seconds if timeout is not zero; * otherwise clear the time limit. * */ static void set_timer (int timeout) { limit = (timeout != 0) ? get_current_time () + timeout : 0; }/* set_timer */ /* * time_limit_hit * * Return true if a previously set time limit has been exceeded. * */ static bool out_of_time (void) { if (limit != 0) { long now = get_current_time (); if (now < 1L * 3600 * 10 && limit > 23L * 3600 * 10) now += 24L * 3600 * 10; return now >= limit; } else return FALSE; }/* out_of_time */ /* * get_key * * Read a keypress or a mouse click. Returns... * * ZC_TIME_OUT = time limit exceeded, * ZC_BACKSPACE = the backspace key, * ZC_RETURN = the return key, * ZC_HKEY_MIN...ZC_HKEY_MAX = a hot key, * ZC_ESCAPE = the escape key, * ZC_ASCII_MIN...ZC_ASCII_MAX = ASCII character, * ZC_ARROW_MIN...ZC_ARROW_MAX = an arrow key, * ZC_FKEY_MIN...ZC_FKEY_MAX = a function key, * ZC_NUMPAD_MIN...ZC_NUMPAD_MAX = a number pad key, * ZC_SINGLE_CLICK = single mouse click, * ZC_DOUBLE_CLICK = double mouse click, * ZC_LATIN1_MIN+1...ZC_LATIN1_MAX = ISO Latin-1 character, * SPECIAL_KEY_MIN...SPECIAL_KEY_MAX = a special editing key. * */ static int get_key (bool cursor) { static byte arrow_key_map[] = { 0x48, 0x50, 0x4b, 0x4d }; static byte special_key_map[] = { 0x47, 0x4f, 0x73, 0x74, 0x53, 0x52, 0x49, 0x51, 0x0f }; static byte hot_key_map[] = { 0x13, 0x19, 0x1f, 0x16, 0x31, 0x2d, 0x20, 0x23 }; int key; /* Loop until a key was pressed */ if (cursor) switch_cursor (TRUE); if (h_flags & MOUSE_FLAG) { asm mov ax,1 asm int 0x33 } do { #ifdef SOUND_SUPPORT if (end_of_sound_flag) end_of_sound (); #endif if (_bios_keybrd (_KEYBRD_READY)) { word code = _bios_keybrd (_KEYBRD_READ); byte code0 = code; byte code1 = code >> 8; if (code0 != 0 && code0 != 9) { key = code0 - '0' + ZC_NUMPAD_MIN; if (key >= ZC_NUMPAD_MIN && key <= ZC_NUMPAD_MAX && code1 >= 0x10) goto exit_loop; for (key = ZC_LATIN1_MIN + 1; key <= ZC_LATIN1_MAX; key++) if (code0 == latin1_to_ibm[key - ZC_LATIN1_MIN]) goto exit_loop; key = code0; if (key == ZC_BACKSPACE) goto exit_loop; if (key == ZC_RETURN) goto exit_loop; if (key == ZC_ESCAPE) goto exit_loop; if (key >= ZC_ASCII_MIN && key <= ZC_ASCII_MAX) goto exit_loop; } else { for (key = ZC_ARROW_MIN; key <= ZC_ARROW_MAX; key++) if (code1 == arrow_key_map[key - ZC_ARROW_MIN]) goto exit_loop; key = code1 - 0x3b + ZC_FKEY_MIN; if (key >= ZC_FKEY_MIN && key <= ZC_FKEY_MAX - 2) goto exit_loop; for (key = ZC_HKEY_MIN; key <= ZC_HKEY_MAX; key++) if (code1 == hot_key_map[key - ZC_HKEY_MIN]) goto exit_loop; for (key = SPECIAL_KEY_MIN; key <= SPECIAL_KEY_MAX; key++) if (code1 == special_key_map[key - SPECIAL_KEY_MIN]) goto exit_loop; } } else { int clicks = read_mouse (); if (clicks == 1) { key = ZC_SINGLE_CLICK; goto exit_loop; } if (clicks == 2) { key = ZC_DOUBLE_CLICK; goto exit_loop; } } key = ZC_TIME_OUT; } while (!out_of_time ()); exit_loop: if (h_flags & MOUSE_FLAG) { asm mov ax,2 asm int 0x33 } if (cursor) switch_cursor (FALSE); return key; }/* get_key */ /* * cursor_left * * Move the cursor one character to the left. * */ static void cursor_left (void) { if (input.pos > 0) cursor_x -= os_char_width (input.buffer[--input.pos]); }/* cursor_left */ /* * cursor_right * * Move the cursor one character to the right. * */ static void cursor_right (void) { if (input.pos < input.length) cursor_x += os_char_width (input.buffer[input.pos++]); }/* cursor_right */ /* * first_char * * Move the cursor to the beginning of the input line. * */ static void first_char (void) { while (input.pos > 0) cursor_left (); }/* first_char */ /* * last_char * * Move the cursor to the end of the input line. * */ static void last_char (void) { while (input.pos < input.length) cursor_right (); }/* last_char */ /* * prev_word * * Move the cursor to the start of the previous word. * */ static void prev_word (void) { do { cursor_left (); if (input.pos == 0) return; } while (input.buffer[input.pos] == ' ' || input.buffer[input.pos - 1] != ' '); }/* prev_word */ /* * next_word * * Move the cursor to the start of the next word. * */ static void next_word (void) { do { cursor_right (); if (input.pos == input.length) return; } while (input.buffer[input.pos] == ' ' || input.buffer[input.pos - 1] != ' '); }/* next_word */ /* * input_move * * Helper function to move parts of the input buffer: * * newc != 0, oldc == 0: INSERT * newc != 0, oldc != 0: OVERWRITE * newc == 0, oldc != 0: DELETE * newc == 0, oldc == 0: NO OPERATION * */ #define H(x) (x ? 1 : 0) static void input_move (zchar newc, zchar oldc) { int newwidth = (newc != 0) ? os_char_width (newc) : 0; int oldwidth = (oldc != 0) ? os_char_width (oldc) : 0; zchar *p = input.buffer + input.pos; int saved_x = cursor_x; int updated_width = input.width + newwidth - oldwidth; int updated_length = input.length + H (newc) - H (oldc); if (updated_width > input.max_width) return; if (updated_length > input.max_length) return; input.width = updated_width; input.length = updated_length; if (oldc != 0 && newc == 0) memmove (p, p + 1, updated_length - input.pos + 1); if (newc != 0 && oldc == 0) memmove (p + 1, p, updated_length - input.pos); if (newc != 0) *p = newc; os_display_string (p); switch_scrn_attr (TRUE); if (oldwidth > newwidth) os_erase_area ( cursor_y + 1, cursor_x + 1, cursor_y + h_font_height, cursor_x + oldwidth - newwidth, -1); switch_scrn_attr (FALSE); cursor_x = saved_x; if (newc != 0) cursor_right (); }/* input_move */ #undef H(x) /* * delete_char * * Delete the character below the cursor. * */ static void delete_char (void) { input_move (0, input.buffer[input.pos]); }/* delete_char */ /* * delete_left * * Delete the character to the left of the cursor. * */ static void delete_left (void) { if (input.pos > 0) { cursor_left (); delete_char (); } }/* delete_left */ /* * truncate_line * * Truncate the input line to n characters. * */ static void truncate_line (int n) { last_char (); while (input.length > n) delete_left (); }/* truncate_line */ /* * insert_char * * Insert a character into the input buffer. * */ static void insert_char (zchar newc) { zchar oldc = 0; if (overwrite) oldc = input.buffer[input.pos]; input_move (newc, oldc); }/* insert_char */ /* * insert_string * * Add a string of characters to the input line. * */ static void insert_string (const zchar *s) { while (*s != 0) { if (input.length + 1 > input.max_length) break; if (input.width + os_char_width (*s) > input.max_width) break; insert_char (*s++); } }/* insert_string */ /* * tabulator_key * * Complete the word at the end of the input line, if possible. * */ static void tabulator_key (void) { int status; if (input.pos == input.length) { zchar extension[10]; status = completion (input.buffer, extension); insert_string (extension); } else status = 2; /* Beep if the completion was impossible or ambiguous */ if (status != 0) os_beep (status); }/* tabulator_key */ /* * store_input * * Copy the current input line to the history buffer. * */ static void store_input (void) { if (input.length >= HISTORY_MIN_ENTRY) { const zchar *ptr = input.buffer; do { if (history.latest++ == HISTORY_BUFSIZE - 1) history.latest = 0; history.buffer[history.latest] = *ptr; } while (*ptr++ != 0); } }/* store_input */ /* * fetch_entry * * Copy the current history entry to the input buffer and check if it * matches the prefix in the input buffer. * */ static bool fetch_entry (zchar *buf, int entry) { int i = 0; zchar c; do { if (entry++ == HISTORY_BUFSIZE - 1) entry = 0; c = history.buffer[entry]; if (i < history.prefix_len && input.buffer[i] != c) return FALSE; buf[i++] = c; } while (c != 0); return (i > history.prefix_len) && (i > 1); }/* fetch_entry */ /* * get_prev_entry * * Copy the previous history entry to the input buffer. * */ static void get_prev_entry (void) { zchar buf[INPUT_BUFFER_SIZE]; int i = history.current; do { do { if (i-- == 0) i = HISTORY_BUFSIZE - 1; if (i == history.latest) return; } while (history.buffer[i] != 0); } while (!fetch_entry (buf, i)); truncate_line (history.prefix_len); insert_string (buf + history.prefix_len); history.current = i; }/* get_prev_entry */ /* * get_next_entry * * Copy the next history entry to the input buffer. * */ static void get_next_entry (void) { zchar buf[INPUT_BUFFER_SIZE]; int i = history.current; truncate_line (history.prefix_len); do { do { if (i == history.latest) return; if (i++ == HISTORY_BUFSIZE - 1) i = 0; } while (history.buffer[i] != 0); if (i == history.latest) goto no_further; } while (!fetch_entry (buf, i)); insert_string (buf + history.prefix_len); no_further: history.current = i; }/* get_next_entry */ /* * os_read_line * * Read a line of input from the keyboard into a buffer. The buffer * may already be primed with some text. In this case, the "initial" * text is already displayed on the screen. After the input action * is complete, the function returns with the terminating key value. * The length of the input should not exceed "max" characters plus * an extra 0 terminator. * * Terminating keys are the return key (13) and all function keys * (see the Specification of the Z-machine) which are accepted by * the is_terminator function. Mouse clicks behave like function * keys except that the mouse position is stored in global variables * "mouse_x" and "mouse_y" (top left coordinates are (1,1)). * * Furthermore, Frotz introduces some special terminating keys: * * ZC_HKEY_PLAYBACK (Alt-P) * ZC_HKEY_RECORD (Alt-R) * ZC_HKEY_SEED (Alt-S) * ZC_HKEY_UNDO (Alt-U) * ZC_HKEY_RESTART (Alt-N, "new game") * ZC_HKEY_QUIT (Alt-X, "exit game") * ZC_HKEY_DEBUGGING (Alt-D) * ZC_HKEY_HELP (Alt-H) * * If the timeout argument is not zero, the input gets interrupted * after timeout/10 seconds (and the return value is 0). * * The complete input line including the cursor must fit in "width" * screen units. * * The function may be called once again to continue after timeouts, * misplaced mouse clicks or hot keys. In this case the "continued" * flag will be set. This information can be useful if the interface * implements input line history. * * The screen is not scrolled after the return key was pressed. The * cursor is at the end of the input line when the function returns. * * Since Inform 2.2 the helper function "completion" can be called * to implement word completion (similar to tcsh under Unix). * */ #define new_history_search() \ { history.prefix_len = input.pos; history.current = history.latest; } zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) { int key = continued ? 9999 : 0; /* Initialise input variables */ input.buffer = buf; input.pos = strlen ((char *) buf); input.length = strlen ((char *) buf); input.max_length = max; input.width = os_string_width (buf); input.max_width = width - os_char_width (' '); /* Calculate time limit */ set_timer (timeout); /* Loop until a terminator is found */ do { if (key != 9999) new_history_search (); /* Get next key from mouse or keyboard */ key = get_key (TRUE); if (key < ZC_ASCII_MIN || key > ZC_ASCII_MAX && key < ZC_LATIN1_MIN || key > ZC_LATIN1_MAX) { /* Ignore time-outs if the cursor is not at end of the line */ if (key == ZC_TIME_OUT && input.pos < input.length) key = 9999; /* Backspace, return and escape keys */ if (key == ZC_BACKSPACE) delete_left (); if (key == ZC_RETURN) store_input (); if (key == ZC_ESCAPE) truncate_line (0); /* Editing keys */ if (cwin == 0) { if (key == ZC_ARROW_UP) get_prev_entry (); if (key == ZC_ARROW_DOWN) get_next_entry (); if (key == ZC_ARROW_LEFT) cursor_left (); if (key == ZC_ARROW_RIGHT) cursor_right (); if (key >= ZC_ARROW_MIN && key <= ZC_ARROW_MAX) key = 9999; if (key == SPECIAL_KEY_HOME) first_char (); if (key == SPECIAL_KEY_END) last_char (); if (key == SPECIAL_KEY_WORD_LEFT) prev_word (); if (key == SPECIAL_KEY_WORD_RIGHT) next_word (); if (key == SPECIAL_KEY_DELETE) delete_char (); if (key == SPECIAL_KEY_INSERT) overwrite = !overwrite; if (key == SPECIAL_KEY_TAB) tabulator_key (); } if (key == SPECIAL_KEY_PAGE_UP) key = ZC_ARROW_UP; if (key == SPECIAL_KEY_PAGE_DOWN) key = ZC_ARROW_DOWN; } else insert_char (key); } while (key > 0xff || !is_terminator (key)); last_char (); overwrite = FALSE; /* Return terminating key */ return key; }/* os_read_line */ #undef new_history_search() /* * os_read_key * * Read a single character from the keyboard (or a mouse click) and * return it. Input aborts after timeout/10 seconds. * */ zchar os_read_key (int timeout, bool cursor) { int key; set_timer (timeout); do { key = get_key (cursor); } while (key > 0xff); return key; }/* os_read_key */ /* * os_read_file_name * * Return the name of a file. Flag can be one of: * * FILE_SAVE - Save game file * FILE_RESTORE - Restore game file * FILE_SCRIPT - Transscript file * FILE_RECORD - Command file for recording * FILE_PLAYBACK - Command file for playback * FILE_SAVE_AUX - Save auxilary ("preferred settings") file * FILE_LOAD_AUX - Load auxilary ("preferred settings") file * * The length of the file name is limited by MAX_FILE_NAME. Ideally * an interpreter should open a file requester to ask for the file * name. If it is unable to do that then this function should call * print_string and read_string to ask for a file name. * */ int os_read_file_name (char *file_name, const char *default_name, int flag) { char *extension; FILE *fp; bool terminal; bool result; bool saved_replay = istream_replay; bool saved_record = ostream_record; /* Turn off playback and recording temporarily */ istream_replay = FALSE; ostream_record = FALSE; /* Select appropriate extension */ extension = ".aux"; if (flag == FILE_SAVE || flag == FILE_RESTORE) extension = ".sav"; if (flag == FILE_SCRIPT) extension = ".scr"; if (flag == FILE_RECORD || flag == FILE_PLAYBACK) extension = ".rec"; /* Input file name (reserve four bytes for a file name extension) */ print_string ("Enter file name (\""); print_string (extension); print_string ("\" will be added).\nDefault is \""); print_string (default_name); print_string ("\": "); read_string (MAX_FILE_NAME - 4, (zchar *) file_name); /* Use the default name if nothing was typed */ if (file_name[0] == 0) strcpy (file_name, default_name); if (strchr (file_name, '.') == NULL) strcat (file_name, extension); /* Make sure it is safe to use this file name */ result = TRUE; /* OK if the file is opened for reading */ if (flag != FILE_SAVE && flag != FILE_SAVE_AUX && flag != FILE_RECORD) goto finished; /* OK if the file does not exist */ if ((fp = fopen (file_name, "rb")) == NULL) goto finished; /* OK if this is a pseudo-file (like PRN, CON, NUL) */ terminal = fp->flags & _F_TERM; fclose (fp); if (terminal) goto finished; /* OK if user wants to overwrite */ result = read_yes_or_no ("Overwrite existing file"); finished: /* Restore state of playback and recording */ istream_replay = saved_replay; ostream_record = saved_record; return result; }/* os_read_file_name */ frotz-2.44/src/dos/bcinit.c0000644000175000017500000005611512527045477012545 00000000000000/* * file "BCinit.c" * * Borland C front end, initialisation * */ #include #include #include #include #include #include "frotz.h" #include "bcfrotz.h" #include "bcblorb.h" f_setup_t f_setup; static char information[] = "An interpreter for all Infocom and other Z-Machine games.\n" "Complies with standard 1.0 of Graham Nelson's specification.\n" "\n" "Syntax: frotz [options] story-file\n" " -a watch attribute setting \t -o watch object movement\n" " -A watch attribute testing \t -O watch object locating\n" " -b # background colour \t -p alter piracy opcode\n" " -B # reverse background colour\t -r # right margin\n" " -c # context lines \t -s # random number seed value\n" " -d # display mode (see below) \t -S # transscript width\n" " -e # emphasis colour [mode 1] \t -t set Tandy bit\n" " -f # foreground colour \t -T bold typing [modes 2+4+5]\n" " -F # reverse foreground colour\t -u # slots for multiple undo\n" " -g # font [mode 5] (see below)\t -w # screen width\n" " -h # screen height \t -x expand abbreviations g/x/z\n" " -i ignore runtime errors \t -Z # error checking (see below)\n" " -l # left margin" "\n" "Fonts are 0 (fixed), 1 (sans serif), 2 (comic), 3 (times), 4 (serif).\n" "Display modes are 0 (mono), 1 (text), 2 (CGA), 3 (MCGA), 4 (EGA), 5 (Amiga)." "\n\n" "Error checking is 0 (none), 1 (report first error (default)),\n" " 2 (report all errors), 3 (exit after any error)."; /* in bcinit.c only. What is its significance? */ extern unsigned cdecl _heaplen = 0x800 + 4 * BUFSIZ; extern unsigned cdecl _stklen = 0x800; extern const char *optarg; extern int optind; int cdecl getopt (int, char *[], const char *); static const char *progname = NULL; extern char script_name[]; extern char command_name[]; extern char save_name[]; extern char auxilary_name[]; int display = -1; int user_background = -1; int user_foreground = -1; int user_emphasis = -1; int user_bold_typing = -1; int user_reverse_bg = -1; int user_reverse_fg = -1; int user_screen_height = -1; int user_screen_width = -1; int user_tandy_bit = -1; int user_random_seed = -1; int user_font = 1; /* Blorb-related things */ char *blorb_name; char *blorb_file; bool use_blorb; bool exec_in_blorb; static byte old_video_mode = 0; static void interrupt (*oldvect) () = NULL; /* * os_init_setup * * Set or reset various configuration variables. * */ void os_init_setup(void) { f_setup.attribute_assignment = 0; f_setup.attribute_testing = 0; f_setup.context_lines = 0; f_setup.object_locating = 0; f_setup.object_movement = 0; f_setup.left_margin = 0; f_setup.right_margin = 0; f_setup.ignore_errors = 0; f_setup.piracy = 0; f_setup.undo_slots = MAX_UNDO_SLOTS; f_setup.expand_abbreviations = 0; f_setup.script_cols = 80; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; }/* os_init_setup */ /* * dectoi * * Convert a string containing a decimal number to integer. The string may * be NULL, but it must not be empty. * */ int dectoi (const char *s) { int n = 0; if (s != NULL) do { n = 10 * n + (*s & 15); } while (*++s > ' '); return n; }/* dectoi */ /* * hextoi * * Convert a string containing a hex number to integer. The string may be * NULL, but it must not be empty. * */ int hextoi (const char *s) { int n = 0; if (s != NULL) do { n = 16 * n + (*s & 15); if (*s > '9') n += 9; } while (*++s > ' '); return n; }/* hextoi */ /* * basename * * A generic and spartan bit of code to extract the filename from a path. * This one is so trivial that it's okay to steal. */ char *basename(const char *path) { const char *s; const char *p; p = s = path; while (*s) { if (*s++ == '\\') { p = s; } } return (char *) p; } /* basename */ /* * cleanup * * Shut down the IO interface: free memory, close files, restore * interrupt pointers and return to the previous video mode. * */ static void cleanup (void) { #ifdef SOUND_SUPPORT dos_reset_sound (); #endif reset_pictures (); asm mov ah,0 asm mov al,old_video_mode asm int 0x10 setvect (0x1b, oldvect); }/* cleanup */ /* * fast_exit * * Handler routine to be called when the crtl-break key is pressed. * */ static void interrupt fast_exit () { cleanup (); exit (EXIT_FAILURE); }/* fast_exit */ /* * os_fatal * * Display error message and exit program. * */ void os_fatal (const char *s, ...) { if (h_interpreter_number) os_reset_screen (); /* Display error message */ fputs ("\nFatal error: ", stderr); fputs (s, stderr); fputs ("\n", stderr); /* Abort program */ exit (EXIT_FAILURE); }/* os_fatal */ /* * parse_options * * Parse program options and set global flags accordingly. * */ static void parse_options (int argc, char **argv) { int c; do { int num = 0; c = getopt (argc, argv, "aAb:B:c:d:e:f:F:g:h:il:oOpQr:s:S:tTu:w:xZ:"); if (optarg != NULL) num = dectoi (optarg); if (c == 'a') f_setup.attribute_assignment = 1; if (c == 'A') f_setup.attribute_testing = 1; if (c == 'b') user_background = num; if (c == 'B') user_reverse_bg = num; if (c == 'c') f_setup.context_lines = num; if (c == 'd') { display = optarg[0] | 32; if ((display < '0' || display > '5') && (display < 'a' || display > 'e')) { display = -1; } } if (c == 'e') user_emphasis = num; if (c == 'T') user_bold_typing = 1; if (c == 'f') user_foreground = num; if (c == 'F') user_reverse_fg = num; if (c == 'g') { if (num >= 0 && num <= 4) user_font = num; } if (c == 'h') user_screen_height = num; if (c == 'i') f_setup.ignore_errors = 1; if (c == 'l') f_setup.left_margin = num; if (c == 'o') f_setup.object_movement = 1; if (c == 'O') f_setup.object_locating = 1; if (c == 'p') f_setup.piracy = 1; if (c == 'r') f_setup.right_margin = num; if (c == 's') user_random_seed = num; if (c == 'S') f_setup.script_cols = num; if (c == 't') user_tandy_bit = 1; if (c == 'u') f_setup.undo_slots = num; if (c == 'w') user_screen_width = num; if (c == 'x') f_setup.expand_abbreviations = 1; if (c == 'Z') { if (num >= ERR_REPORT_NEVER && num <= ERR_REPORT_FATAL) f_setup.err_report_mode = num; } if (c == '?') optind = argc; } while (c != EOF && c != '?'); }/* parse_options */ /* * os_process_arguments * * Handle command line switches. Some variables may be set to activate * special features of Frotz: * * option_attribute_assignment * option_attribute_testing * option_context_lines * option_object_locating * option_object_movement * option_left_margin * option_right_margin * option_ignore_errors * option_piracy * option_undo_slots * option_expand_abbreviations * option_script_cols * * The global pointer "story_name" is set to the story file name. * */ void os_process_arguments (int argc, char *argv[]) { const char *p; int i; char stripped_story_name[10]; /* Parse command line options */ parse_options (argc, argv); if (optind != argc - 1) { printf ("FROTZ V%s\tMSDOS / PCDOS Edition\n", VERSION); puts (information); exit (EXIT_FAILURE); } /* Set the story file name */ f_setup.story_file = strdup(argv[optind]); /* Strip path and extension off the story file name */ p = strdup(f_setup.story_file); for (i = 0; f_setup.story_file[i] != 0; i++) if (f_setup.story_file[i] == '\\' || f_setup.story_file[i] == '/' || f_setup.story_file[i] == ':') p = f_setup.story_file + i + 1; for (i = 0; p[i] != 0 && p[i] != '.'; i++) stripped_story_name[i] = p[i]; stripped_story_name[i] = 0; f_setup.story_name = strdup(stripped_story_name); /* Create nice default file names */ f_setup.script_name = strdup(f_setup.story_name); f_setup.command_name = strdup(f_setup.story_name); f_setup.save_name = strdup(f_setup.story_name); f_setup.aux_name = strdup(f_setup.story_name); strcat (f_setup.script_name, ".scr"); strcat (f_setup.command_name, ".rec"); strcat (f_setup.save_name, ".sav"); strcat (f_setup.aux_name, ".aux"); /* Save the executable file name */ progname = argv[0]; blorb_file = strdup(f_setup.story_name); strcat(blorb_file, ".blb"); switch (dos_init_blorb()) { case bb_err_Format: printf("Blorb file loaded, but unable to build map.\n\n"); break; default: break; /* No problem. Don't say anything. */ /* printf("Blorb error code %i\n\n"); */ } }/* os_process_arguments */ /* * standard_palette * * Set palette registers to EGA default values and call VGA BIOS to * use DAC registers #0 to #63. * */ static void standard_palette (void) { static byte palette[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x14, 0x07, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x00 /* last one is the overscan register */ }; if (display == _AMIGA_) { asm mov ax,0x1002 asm lea dx,palette asm push ds asm pop es asm int 0x10 asm mov ax,0x1013 asm mov bx,0x0001 asm int 0x10 } }/* standard_palette */ /* * special_palette * * Set palette register #i to value i and call VGA BIOS to use DAC * registers #64 to #127. * */ static void special_palette (void) { static byte palette[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00 /* last one is the overscan register */ }; if (display == _AMIGA_) { asm mov ax,0x1002 asm mov dx,offset palette asm push ds asm pop es asm int 0x10 asm mov ax,0x1013 asm mov bx,0x0101 asm int 0x10 } }/* special_palette */ /* * os_init_screen * * Initialise the IO interface. Prepare the screen and other devices * (mouse, sound board). Set various OS depending story file header * entries: * * h_config (aka flags 1) * h_flags (aka flags 2) * h_screen_cols (aka screen width in characters) * h_screen_rows (aka screen height in lines) * h_screen_width * h_screen_height * h_font_height (defaults to 1) * h_font_width (defaults to 1) * h_default_foreground * h_default_background * h_interpreter_number * h_interpreter_version * h_user_name (optional; not used by any game) * * Finally, set reserve_mem to the amount of memory (in bytes) that * should not be used for multiple undo and reserved for later use. * */ void os_init_screen (void) { static byte zcolour[] = { BLACK_COLOUR, BLUE_COLOUR, GREEN_COLOUR, CYAN_COLOUR, RED_COLOUR, MAGENTA_COLOUR, BROWN + 16, LIGHTGRAY + 16, GREY_COLOUR, LIGHTBLUE + 16, LIGHTGREEN + 16, LIGHTCYAN + 16, LIGHTRED + 16, LIGHTMAGENTA + 16, YELLOW_COLOUR, WHITE_COLOUR }; static struct { /* information on modes 0 to 5 */ byte vmode; word width; word height; byte font_width; byte font_height; byte fg; byte bg; } info[] = { { 0x07, 80, 25, 1, 1, LIGHTGRAY + 16, BLACK_COLOUR }, /* MONO */ { 0x03, 80, 25, 1, 1, LIGHTGRAY + 16, BLUE_COLOUR }, /* TEXT */ { 0x06, 640, 200, 8, 8, WHITE_COLOUR, BLACK_COLOUR }, /* CGA */ { 0x13, 320, 200, 5, 8, WHITE_COLOUR, GREY_COLOUR }, /* MCGA */ { 0x0e, 640, 200, 8, 8, WHITE_COLOUR, BLUE_COLOUR }, /* EGA */ { 0x12, 640, 400, 8, 16, WHITE_COLOUR, BLACK_COLOUR } /* AMIGA */ }; static struct { /* information on modes A to E */ word vesamode; word width; word height; } subinfo[] = { { 0x001, 40, 25 }, { 0x109, 132, 25 }, { 0x10b, 132, 50 }, { 0x108, 80, 60 }, { 0x10c, 132, 60 } }; int subdisplay; /* Get the current video mode. This video mode will be selected when the program terminates. It's also useful to auto-detect monochrome boards. */ asm mov ah,15 asm int 0x10 asm mov old_video_mode,al /* If the display mode has not already been set by the user then see if this is a monochrome board. If so, set the display mode to 0. Otherwise check the graphics flag of the story. Select a graphic mode if it is set or if this is a V6 game. Select text mode if it is not. */ if (display == -1) if (old_video_mode == 7) display = '0'; else if (h_version == V6 || (h_flags & GRAPHICS_FLAG)) display = '5'; else display = '1'; /* Activate the desired display mode. All VESA text modes are very similar to the standard text mode; in fact, only here we need to know which VESA mode is used. */ if (display >= '0' && display <= '5') { subdisplay = -1; display -= '0'; _AL = info[display].vmode; _AH = 0; } else if (display == 'a') { subdisplay = 0; display = 1; _AL = 0x01; _AH = 0; } else if (display >= 'b' && display <= 'e') { subdisplay = display - 'a'; display = 1; _BX = subinfo[subdisplay].vesamode; _AX = 0x4f02; } geninterrupt (0x10); /* Make various preparations */ if (display <= _TEXT_) { /* Enable bright background colours */ asm mov ax,0x1003 asm mov bl,0 asm int 0x10 /* Turn off hardware cursor */ asm mov ah,1 asm mov cx,0xffff asm int 0x10 } else { load_fonts (); if (display == _AMIGA_) { scaler = 2; /* Use resolution 640 x 400 instead of 640 x 480. BIOS doesn't help us here since this is not a standard resolution. */ outportb (0x03c2, 0x63); outport (0x03d4, 0x0e11); outport (0x03d4, 0xbf06); outport (0x03d4, 0x1f07); outport (0x03d4, 0x9c10); outport (0x03d4, 0x8f12); outport (0x03d4, 0x9615); outport (0x03d4, 0xb916); } } #if !defined(__SMALL__) && !defined (__TINY__) && !defined (__MEDIUM__) /* Set the amount of memory to reserve for later use. It takes some memory to open command, script and game files. If Frotz is compiled in a small memory model then memory for opening files is allocated on the "near heap" while other allocations are made on the "far heap", i.e. we need not reserve memory in this case. */ reserve_mem = 4 * BUFSIZ; #endif /* Amiga emulation under V6 needs special preparation. */ if (display == _AMIGA_ && h_version == V6) { user_reverse_fg = -1; user_reverse_bg = -1; zcolour[LIGHTGRAY] = LIGHTGREY_COLOUR; zcolour[DARKGRAY] = DARKGREY_COLOUR; special_palette (); } /* Set various bits in the configuration byte. These bits tell the game which features are supported by the interpreter. */ if (h_version == V3 && user_tandy_bit != -1) h_config |= CONFIG_TANDY; if (h_version == V3) h_config |= CONFIG_SPLITSCREEN; if (h_version == V3 && (display == _MCGA_ || (display == _AMIGA_ && user_font != 0))) h_config |= CONFIG_PROPORTIONAL; if (h_version >= V4 && display != _MCGA_ && (user_bold_typing != -1 || display <= _TEXT_)) h_config |= CONFIG_BOLDFACE; if (h_version >= V4) h_config |= CONFIG_EMPHASIS | CONFIG_FIXED | CONFIG_TIMEDINPUT; if (h_version >= V5 && display != _MONO_ && display != _CGA_) h_config |= CONFIG_COLOUR; if (h_version >= V5 && display >= _CGA_ && init_pictures ()) h_config |= CONFIG_PICTURES; /* Handle various game flags. These flags are set if the game wants to use certain features. The flags must be cleared if the feature is not available. */ if (h_flags & GRAPHICS_FLAG) if (display <= _TEXT_) h_flags &= ~GRAPHICS_FLAG; if (h_version == V3 && (h_flags & OLD_SOUND_FLAG)) #ifdef SOUND_SUPPORT if (!dos_init_sound ()) #endif h_flags &= ~OLD_SOUND_FLAG; if (h_flags & SOUND_FLAG) #ifdef SOUND_SUPPORT if (!dos_init_sound ()) #endif h_flags &= ~SOUND_FLAG; if (h_version >= V5 && (h_flags & UNDO_FLAG)) if (!f_setup.undo_slots) h_flags &= ~UNDO_FLAG; if (h_flags & MOUSE_FLAG) if (subdisplay != -1 || !detect_mouse ()) h_flags &= ~MOUSE_FLAG; if (h_flags & COLOUR_FLAG) if (display == _MONO_ || display == _CGA_) h_flags &= ~COLOUR_FLAG; h_flags &= ~MENU_FLAG; /* Set the screen dimensions, font size and default colour */ h_screen_width = info[display].width; h_screen_height = info[display].height; h_font_height = info[display].font_height; h_font_width = info[display].font_width; h_default_foreground = info[display].fg; h_default_background = info[display].bg; if (subdisplay != -1) { h_screen_width = subinfo[subdisplay].width; h_screen_height = subinfo[subdisplay].height; } if (user_screen_width != -1) h_screen_width = user_screen_width; if (user_screen_height != -1) h_screen_height = user_screen_height; h_screen_cols = h_screen_width / h_font_width; h_screen_rows = h_screen_height / h_font_height; if (user_foreground != -1) h_default_foreground = zcolour[user_foreground]; if (user_background != -1) h_default_background = zcolour[user_background]; /* Set the interpreter number (a constant telling the game which operating system it runs on) and the interpreter version. The interpreter number has effect on V6 games and "Beyond Zork". */ h_interpreter_number = INTERP_MSDOS; h_interpreter_version = 'F'; if (display == _AMIGA_) h_interpreter_number = INTERP_AMIGA; /* Install the fast_exit routine to handle the ctrl-break key */ oldvect = getvect (0x1b); setvect (0x1b, fast_exit); }/* os_init_screen */ /* * os_reset_screen * * Reset the screen before the program stops. * */ void os_reset_screen (void) { os_set_font (TEXT_FONT); os_set_text_style (0); os_display_string ((zchar *) "[Hit any key to exit.]"); os_read_key (0, TRUE); cleanup (); }/* os_reset_screen */ /* * os_restart_game * * This routine allows the interface to interfere with the process of * restarting a game at various stages: * * RESTART_BEGIN - restart has just begun * RESTART_WPROP_SET - window properties have been initialised * RESTART_END - restart is complete * */ void os_restart_game (int stage) { int x, y; if (story_id == BEYOND_ZORK) if (stage == RESTART_BEGIN) if ((display == _MCGA_ || display == _AMIGA_) && os_picture_data (1, &x, &y)) { special_palette (); asm mov ax,0x1010 asm mov bx,64 asm mov dh,0 asm mov ch,0 asm mov cl,0 asm int 0x10 asm mov ax,0x1010 asm mov bx,79 asm mov dh,0xff asm mov ch,0xff asm mov cl,0xff asm int 0x10 os_draw_picture (1, 1, 1); os_read_key (0, FALSE); standard_palette (); } }/* os_restart_game */ /* * os_random_seed * * Return an appropriate random seed value in the range from 0 to * 32767, possibly by using the current system time. * */ int os_random_seed (void) { if (user_random_seed == -1) { /* Use the time of day as seed value */ asm mov ah,0 asm int 0x1a return _DX & 0x7fff; } else return user_random_seed; }/* os_random_seed */ /* * os_path_open * * Open a file in the current directory. If this fails then * search the directories in the ZCODE_PATH environment variable, * if it is defined, otherwise search INFOCOM_PATH. * */ FILE *os_path_open (const char *name, const char *mode) { FILE *fp; char buf[MAX_FILE_NAME + 1]; char *p, *bp, lastch; if ((fp = fopen (name, mode)) != NULL) return fp; if ((p = getenv ("ZCODE_PATH")) == NULL) p = getenv ("INFOCOM_PATH"); if (p != NULL) { while (*p) { bp = buf; while (*p && *p != OS_PATHSEP) lastch = *bp++ = *p++; if (lastch != '\\' && lastch != '/') *bp++ = '\\'; strcpy (bp, name); if ((fp = fopen (buf, mode)) != NULL) return fp; if (*p) p++; } } return NULL; }/* os_path_open */ /* * os_load_story * * This is different from os_path_open() because we need to see if the * story file is actually a chunk inside a blorb file. Right now we're * looking only at the exact path we're given on the command line. * * Open a file in the current directory. If this fails, then search the * directories in the ZCODE_PATH environmental variable. If that's not * defined, search INFOCOM_PATH. * */ FILE *os_load_story(void) { FILE *fp; /* Did we build a valid blorb map? */ if (exec_in_blorb) { fp = fopen(blorb_file, "rb"); fseek(fp, blorb_res.data.startpos, SEEK_SET); } else { fp = fopen(f_setup.story_file, "rb"); } return fp; } int dos_init_blorb(void) { FILE *blorbfile; /* If the filename given on the command line is the same as our * computed blorb filename, then we will assume the executable * is contained in the blorb file. */ if (strncmp((char *)basename(f_setup.story_file), (char *)basename(blorb_file), 55) == 0) { if ((blorbfile = fopen(blorb_file, "rb")) == NULL) return bb_err_Read; /* Under DOS, bb_create_map() returns bb_err_Format */ blorb_err = bb_create_map(blorbfile, &blorb_map); if (blorb_err != bb_err_None) { return blorb_err; } /* Now we need to locate the EXEC chunk within the blorb file * and present it to the rest of the program as a file stream. */ blorb_err = bb_load_chunk_by_type(blorb_map, bb_method_FilePos, &blorb_res, bb_ID_ZCOD, 0); if (blorb_err == bb_err_None) { exec_in_blorb = 1; use_blorb = 1; } } return 0; } /* * Seek into a storyfile, either a standalone file or the * ZCODE chunk of a blorb file */ int os_storyfile_seek(FILE * fp, long offset, int whence) { /* Is this a Blorb file containing Zcode? */ if (exec_in_blorb) { switch (whence) { case SEEK_END: return fseek(fp, blorb_res.data.startpos + blorb_res.length + offset, SEEK_SET); break; case SEEK_CUR: return fseek(fp, offset, SEEK_CUR); break; case SEEK_SET: default: return fseek(fp, blorb_res.data.startpos + offset, SEEK_SET); break; } } else { return fseek(fp, offset, whence); } } /* * Tell the position in a storyfile, either a standalone file * or the ZCODE chunk of a blorb file */ int os_storyfile_tell(FILE * fp) { /* Is this a Blorb file containing Zcode? */ if (exec_in_blorb) { return ftell(fp) - blorb_res.data.startpos; } else { return ftell(fp); } } frotz-2.44/src/blorb/0000755000175000017500000000000012527045477011514 500000000000000frotz-2.44/src/blorb/blorb.h0000644000175000017500000001157212527045477012713 00000000000000#ifndef BLORB_H #define BLORB_H /* blorb.h: Header file for Blorb library, version 1.0.2. Designed by Andrew Plotkin http://www.eblong.com/zarf/blorb/index.html This is the header that a Z-machine interpreter should include. It defines everything that the interpreter has to know. */ /* Things you (the porter) have to edit: */ /* As you might expect, uint32 must be a 32-bit unsigned numeric type, and uint16 a 16-bit unsigned numeric type. You should also uncomment exactly one of the two ENDIAN definitions. */ /* #define BLORB_BIG_ENDIAN */ #define BLORB_LITTLE_ENDIAN #include #if UINT_MAX == (1UL<<32)-1UL typedef unsigned int uint32; #else typedef unsigned long uint32; #endif typedef unsigned short uint16; /* End of things you have to edit. */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* Error type and error codes */ typedef int bb_err_t; #define bb_err_None (0) #define bb_err_CompileTime (1) #define bb_err_Alloc (2) #define bb_err_Read (3) #define bb_err_NotAMap (4) #define bb_err_Format (5) #define bb_err_NotFound (6) /* Methods for loading a chunk */ #define bb_method_DontLoad (0) #define bb_method_Memory (1) #define bb_method_FilePos (2) /* Four-byte constants */ #define bb_make_id(c1, c2, c3, c4) \ (((c1) << 24) | ((c2) << 16) | ((c3) << 8) | (c4)) #define bb_ID_Snd (bb_make_id('S', 'n', 'd', ' ')) #define bb_ID_Exec (bb_make_id('E', 'x', 'e', 'c')) #define bb_ID_Pict (bb_make_id('P', 'i', 'c', 't')) #define bb_ID_Copyright (bb_make_id('(', 'c', ')', ' ')) #define bb_ID_AUTH (bb_make_id('A', 'U', 'T', 'H')) #define bb_ID_ANNO (bb_make_id('A', 'N', 'N', 'O')) /* bb_result_t: Result when you try to load a chunk. */ typedef struct bb_result_struct { int chunknum; /* The chunk number (for use in bb_unload_chunk(), etc.) */ union { void *ptr; /* A pointer to the data (if you used bb_method_Memory) */ uint32 startpos; /* The position in the file (if you used bb_method_FilePos) */ } data; uint32 length; /* The length of the data */ } bb_result_t; /* bb_aux_sound_t: Extra data which may be associated with a sound. */ typedef struct bb_aux_sound_struct { char repeats; } bb_aux_sound_t; /* bb_aux_pict_t: Extra data which may be associated with an image. */ typedef struct bb_aux_pict_struct { uint32 ratnum, ratden; uint32 minnum, minden; uint32 maxnum, maxden; } bb_aux_pict_t; /* bb_resolution_t: The global resolution data. */ typedef struct bb_resolution_struct { uint32 px, py; uint32 minx, miny; uint32 maxx, maxy; } bb_resolution_t; /* bb_color_t: Guess what. */ typedef struct bb_color_struct { unsigned char red, green, blue; } bb_color_t; /* bb_palette_t: The palette data. */ typedef struct bb_palette_struct { int isdirect; union { int depth; /* The depth (if isdirect is TRUE). Either 16 or 32. */ struct { int numcolors; bb_color_t *colors; } table; /* The list of colors (if isdirect is FALSE). */ } data; } bb_palette_t; /* bb_zheader_t: Information to identify a Z-code file. */ typedef struct bb_zheader_struct { uint16 releasenum; /* Bytes $2-3 of header. */ char serialnum[6]; /* Bytes $12-17 of header. */ uint16 checksum; /* Bytes $1C-1D of header. */ /* The initpc field is not used by Blorb. */ } bb_zheader_t; /* bb_map_t: Holds the complete description of an open Blorb file. This type is opaque for normal interpreter use. */ typedef struct bb_map_struct bb_map_t; /* Function declarations. These functions are of fairly general use; they would apply to any Blorb file. */ extern bb_err_t bb_create_map(FILE *file, bb_map_t **newmap); extern bb_err_t bb_destroy_map(bb_map_t *map); extern char *bb_err_to_string(bb_err_t err); extern bb_err_t bb_load_chunk_by_type(bb_map_t *map, int method, bb_result_t *res, uint32 chunktype, int count); extern bb_err_t bb_load_chunk_by_number(bb_map_t *map, int method, bb_result_t *res, int chunknum); extern bb_err_t bb_unload_chunk(bb_map_t *map, int chunknum); extern bb_err_t bb_load_resource(bb_map_t *map, int method, bb_result_t *res, uint32 usage, int resnum); extern bb_err_t bb_count_resources(bb_map_t *map, uint32 usage, int *num, int *min, int *max); /* More function declarations. These functions are more or less specific to the Z-machine's use of Blorb. */ extern uint16 bb_get_release_num(bb_map_t *map); extern bb_zheader_t *bb_get_zheader(bb_map_t *map); extern bb_resolution_t *bb_get_resolution(bb_map_t *map); extern bb_err_t bb_get_palette(bb_map_t *map, bb_palette_t **res); extern bb_err_t bb_load_resource_pict(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_pict_t **auxdata); extern bb_err_t bb_load_resource_snd(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_sound_t **auxdata); #endif /* BLORB_H */ frotz-2.44/src/blorb/blorblow.h0000644000175000017500000000502012527045477013424 00000000000000#ifndef BLORBLOW_H #define BLORBLOW_H /* blorblow.h: Low-level header file for Blorb library, version 1.0.2. Designed by Andrew Plotkin http://www.eblong.com/zarf/blorb/index.html This header is generally of interest only to the Blorb library code itself (blorblib.c); it defines things internal to the library. An interpreter shouldn't have to include this file. The only time you might need to include this is if you're writing a Blorb file analysis tool (such as blorbscan), or a transformation tool, or some such thing. */ /* More four-byte constants. */ #define bb_ID_FORM (bb_make_id('F', 'O', 'R', 'M')) #define bb_ID_IFRS (bb_make_id('I', 'F', 'R', 'S')) #define bb_ID_RIdx (bb_make_id('R', 'I', 'd', 'x')) #define bb_ID_IFhd (bb_make_id('I', 'F', 'h', 'd')) #define bb_ID_Reso (bb_make_id('R', 'e', 's', 'o')) #define bb_ID_Loop (bb_make_id('L', 'o', 'o', 'p')) #define bb_ID_RelN (bb_make_id('R', 'e', 'l', 'N')) #define bb_ID_Plte (bb_make_id('P', 'l', 't', 'e')) /* bb_chunkdesc_t: Describes one chunk of the Blorb file. */ typedef struct bb_chunkdesc_struct { uint32 type; uint32 len; uint32 startpos; /* start of chunk header */ uint32 datpos; /* start of data (either startpos or startpos+8) */ void *ptr; /* pointer to malloc'd data, if loaded */ int auxdatnum; /* entry in the auxsound/auxpict array; -1 if none. This only applies to chunks that represent resources; */ } bb_chunkdesc_t; /* bb_resdesc_t: Describes one resource in the Blorb file. */ typedef struct bb_resdesc_struct { uint32 usage; int resnum; int chunknum; } bb_resdesc_t; /* bb_map_t: Holds the complete description of an open Blorb file. */ struct bb_map_struct { uint32 inited; /* holds bb_Inited_Magic if the map structure is valid */ FILE *file; int numchunks; bb_chunkdesc_t *chunks; /* list of chunk descriptors */ int numresources; bb_resdesc_t *resources; /* list of resource descriptors */ bb_resdesc_t **ressorted; /* list of pointers to descriptors in map->resources -- sorted by usage and resource number. */ bb_zheader_t *zheader; int releasenum; bb_resolution_t *resolution; int palettechunk; /* chunk number of palette, or -1 if there is none. */ bb_palette_t *palette; bb_aux_sound_t *auxsound; /* extra information about sounds */ bb_aux_pict_t *auxpict; /* extra information about pictures */ }; #define bb_Inited_Magic (0xB7012BED) extern char *bb_id_to_string(uint32 id); #endif /* BLORBLOW_H */ frotz-2.44/src/blorb/blorblib.c0000644000175000017500000005400412527045477013372 00000000000000/* blorblib.c: Blorb file reader library, version 1.0.2. Designed by Andrew Plotkin http://www.eblong.com/zarf/blorb/index.html This is portable code to read a Blorb file. Add it to your interpreter, #include "blorb.h", and you're ready to go. */ #include #include #include "blorb.h" #include "blorblow.h" /* This endian stuff needs to be fixed with something from * http://www.ibm.com/developerworks/aix/library/au-endianc/index.html */ #ifdef BLORB_BIG_ENDIAN static char contentsticker[] = "\nBlorb Library 1.0 (big-endian)\n"; #define bb_native2(v) (v) #define bb_native4(v) (v) #endif #ifdef BLORB_LITTLE_ENDIAN static char contentsticker[] = "\nBlorb Library 1.0 (little-endian)\n"; #define bb_native2(v) \ ( (((uint16)(v) >> 8) & 0x00ff) \ | (((uint16)(v) << 8) & 0xff00)) #define bb_native4(v) \ ( (((uint32)(v) >> 24) & 0x000000ff) \ | (((uint32)(v) >> 8) & 0x0000ff00) \ | (((uint32)(v) << 8) & 0x00ff0000) \ | (((uint32)(v) << 24) & 0xff000000)) #endif #ifdef CRAPOLA #ifndef bb_native4 #error "You must define either BLORB_BIG_ENDIAN" #error "or BLORB_LITTLE_ENDIAN in blorb.h in order" #error "to compile this library." #endif #endif /* CRAPOLA */ static int lib_inited = FALSE; static bb_err_t bb_initialize_map(bb_map_t *map); static bb_err_t bb_initialize(void); static int sortsplot(const void *p1, const void *p2); /* Do some one-time startup tests. */ static bb_err_t bb_initialize() { union { uint32 val; unsigned char ch[4]; } test; uint32 val; if (sizeof(uint32) != 4 || sizeof(uint16) != 2) return bb_err_CompileTime; /* Basic types are the wrong size. */ test.ch[0] = 0x13; test.ch[1] = 0x57; test.ch[2] = 0x9a; test.ch[3] = 0xce; val = test.val; if (bb_native4(val) != 0x13579ace) return bb_err_CompileTime; /* Wrong endianness. */ return bb_err_None; } bb_err_t bb_create_map(FILE *file, bb_map_t **newmap) { bb_err_t err; bb_map_t *map; size_t readlen; uint32 nextpos, totallength; bb_chunkdesc_t *chunks; int chunks_size, numchunks; uint32 buffer[4]; *newmap = NULL; if (!lib_inited) { err = bb_initialize(); if (err) return err; lib_inited = TRUE; } /* First, chew through the file and index the chunks. */ err = fseek(file, 0, 0); if (err) return bb_err_Read; readlen = fread(buffer, sizeof(uint32), 3, file); if (readlen != 3) return bb_err_Read; if (bb_native4(buffer[0]) != bb_ID_FORM) return bb_err_Format; if (bb_native4(buffer[2]) != bb_ID_IFRS) return bb_err_Format; totallength = bb_native4(buffer[1]) + 8; nextpos = 12; chunks_size = 8; numchunks = 0; chunks = (bb_chunkdesc_t *)malloc(sizeof(bb_chunkdesc_t) * chunks_size); while (nextpos < totallength) { uint32 type, len; int chunum; bb_chunkdesc_t *chu; err = fseek(file, nextpos, 0); if (err) return bb_err_Read; readlen = fread(buffer, sizeof(uint32), 2, file); if (readlen != 2) return bb_err_Read; type = bb_native4(buffer[0]); len = bb_native4(buffer[1]); if (numchunks >= chunks_size) { chunks_size *= 2; chunks = (bb_chunkdesc_t *)realloc(chunks, sizeof(bb_chunkdesc_t) * chunks_size); } chunum = numchunks; chu = &(chunks[chunum]); numchunks++; chu->type = type; chu->startpos = nextpos; if (type == bb_ID_FORM) { chu->datpos = nextpos; chu->len = len+8; } else { chu->datpos = nextpos+8; chu->len = len; } chu->ptr = NULL; chu->auxdatnum = -1; nextpos = nextpos + len + 8; if (nextpos & 1) nextpos++; if (nextpos > totallength) return bb_err_Format; } /* The basic IFF structure seems to be ok, and we have a list of chunks. Now we allocate the map structure itself. */ map = (bb_map_t *)malloc(sizeof(bb_map_t)); if (!map) { free(chunks); return bb_err_Alloc; } map->inited = bb_Inited_Magic; map->file = file; map->chunks = chunks; map->numchunks = numchunks; map->resources = NULL; map->ressorted = NULL; map->numresources = 0; map->releasenum = 0; map->zheader = NULL; map->resolution = NULL; map->palettechunk = -1; map->palette = NULL; map->auxsound = NULL; map->auxpict = NULL; /* Now we do everything else involved in loading the Blorb file, such as building resource lists. */ err = bb_initialize_map(map); if (err) { bb_destroy_map(map); return err; } *newmap = map; return bb_err_None; } static bb_err_t bb_initialize_map(bb_map_t *map) { /* It is important that the map structure be kept valid during this function. If this returns an error, bb_destroy_map() will be called. */ int ix, jx; bb_result_t chunkres; bb_err_t err; uint32 *ptr; uint32 len; uint32 val; int numres; int gotindex = FALSE; for (ix=0; ixnumchunks; ix++) { bb_chunkdesc_t *chu = &map->chunks[ix]; switch (chu->type) { case bb_ID_RIdx: /* Resource index chunk: build the resource list and sort it. */ if (gotindex) return bb_err_Format; /* duplicate index chunk */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; ptr = chunkres.data.ptr; len = chunkres.length; val = ptr[0]; numres = bb_native4(val); if (numres) { int ix2; bb_resdesc_t *resources; bb_resdesc_t **ressorted; if (len != numres*12+4) return bb_err_Format; /* bad length field */ resources = (bb_resdesc_t *)malloc(numres * sizeof(bb_resdesc_t)); ressorted = (bb_resdesc_t **)malloc(numres * sizeof(bb_resdesc_t *)); if (!ressorted || !resources) return bb_err_Alloc; ix2 = 0; for (jx=0; jxusage = bb_native4(val); val = ptr[2+jx*3]; res->resnum = bb_native4(val); val = ptr[3+jx*3]; respos = bb_native4(val); while (ix2 < map->numchunks && map->chunks[ix2].startpos < respos) ix2++; if (ix2 >= map->numchunks || map->chunks[ix2].startpos != respos) return bb_err_Format; /* start pos does not match a real chunk */ res->chunknum = ix2; ressorted[jx] = res; } /* Sort a resource list (actually a list of pointers to structures in map->resources.) This makes it easy to find resources by usage and resource number. */ qsort(ressorted, numres, sizeof(bb_resdesc_t *), &sortsplot); map->numresources = numres; map->resources = resources; map->ressorted = ressorted; } bb_unload_chunk(map, ix); gotindex = TRUE; break; case bb_ID_RelN: /* Release number chunk: Get the release number. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; if (chunkres.length != 2) return bb_err_Format; { uint16 val = *((uint16 *)chunkres.data.ptr); map->releasenum = bb_native2(val); } bb_unload_chunk(map, ix); break; case bb_ID_IFhd: /* Z-header chunk: Get the header info. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; if (chunkres.length < 13) return bb_err_Format; { uint16 val; bb_zheader_t *head = (bb_zheader_t *)malloc(sizeof(bb_zheader_t)); if (!head) return bb_err_Alloc; val = ((uint16 *)(chunkres.data.ptr))[0]; head->releasenum = bb_native2(val); val = ((uint16 *)(chunkres.data.ptr))[4]; head->checksum = bb_native2(val); for (jx=0; jx<6; jx++) { head->serialnum[jx] = ((char *)(chunkres.data.ptr))[2+jx]; } map->zheader = head; } bb_unload_chunk(map, ix); break; case bb_ID_Reso: /* Resolution chunk: Get the window size data, and resolution ratios for images. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; if (chunkres.length < 24) return bb_err_Format; ptr = chunkres.data.ptr; len = chunkres.length; { bb_resolution_t *reso = (bb_resolution_t *)malloc(sizeof(bb_resolution_t)); if (!reso) return bb_err_Alloc; reso->px = bb_native4(ptr[0]); reso->py = bb_native4(ptr[1]); reso->minx = bb_native4(ptr[2]); reso->miny = bb_native4(ptr[3]); reso->maxx = bb_native4(ptr[4]); reso->maxy = bb_native4(ptr[5]); map->resolution = reso; } ptr += 6; len -= 6*4; len = len / 28; if (len) { bb_aux_pict_t *aux = (bb_aux_pict_t *)malloc(len * sizeof(bb_aux_pict_t)); for (jx=0; jxchunks[res.chunknum]); if (chu->auxdatnum != -1) return bb_err_Format; /* two image entries for this resource */ chu->auxdatnum = jx; aux[jx].ratnum = bb_native4(ptr[1]); aux[jx].ratden = bb_native4(ptr[2]); aux[jx].minnum = bb_native4(ptr[3]); aux[jx].minden = bb_native4(ptr[4]); aux[jx].maxnum = bb_native4(ptr[5]); aux[jx].maxden = bb_native4(ptr[6]); } } map->auxpict = aux; } bb_unload_chunk(map, ix); break; case bb_ID_Loop: /* Looping chunk: Get looping data for sounds. */ err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, ix); if (err) return err; ptr = chunkres.data.ptr; len = chunkres.length; len = len / 8; if (len) { bb_aux_sound_t *aux = (bb_aux_sound_t *)malloc(len * sizeof(bb_aux_sound_t)); for (jx=0; jxchunks[res.chunknum]); if (chu->auxdatnum != -1) return bb_err_Format; /* two looping entries for this resource */ chu->auxdatnum = jx; aux[jx].repeats = bb_native4(ptr[1]); } } map->auxsound = aux; } bb_unload_chunk(map, ix); break; case bb_ID_Plte: /* Palette chunk: Don't get the palette info now, since it may be large and the interpreter may not care. But remember the chunk number in case the interpreter asks later. */ map->palettechunk = ix; break; } } return bb_err_None; } bb_err_t bb_destroy_map(bb_map_t *map) { int ix; if (!map || !map->chunks || map->inited != bb_Inited_Magic) return bb_err_NotAMap; for (ix=0; ixnumchunks; ix++) { bb_chunkdesc_t *chu = &(map->chunks[ix]); if (chu->ptr) { free(chu->ptr); chu->ptr = NULL; } } if (map->chunks) { free(map->chunks); map->chunks = NULL; } map->numchunks = 0; if (map->resources) { free(map->resources); map->resources = NULL; } if (map->ressorted) { free(map->ressorted); map->ressorted = NULL; } map->numresources = 0; if (map->zheader) { free(map->zheader); map->zheader = NULL; } if (map->resolution) { free(map->resolution); map->resolution = NULL; } if (map->palette) { if (!map->palette->isdirect && map->palette->data.table.colors) { free(map->palette->data.table.colors); map->palette->data.table.colors = NULL; } free(map->palette); map->palette = NULL; } if (map->auxsound) { free(map->auxsound); map->auxsound = NULL; } if (map->auxpict) { free(map->auxpict); map->auxpict = NULL; } map->file = NULL; map->inited = 0; free(map); return bb_err_None; } /* Turn a four-byte constant into a string. This returns a static buffer, so if you call it twice, the old value gets overwritten. */ char *bb_id_to_string(uint32 id) { static char buf[5]; buf[0] = (id >> 24) & 0xff; buf[1] = (id >> 16) & 0xff; buf[2] = (id >> 8) & 0xff; buf[3] = (id) & 0xff; buf[4] = '\0'; return buf; } /* Turn an error code into a string describing the error. */ char *bb_err_to_string(bb_err_t err) { switch (err) { case bb_err_None: return "ok"; case bb_err_CompileTime: return "library compiled wrong"; case bb_err_Alloc: return "cannot allocate memory"; case bb_err_Read: return "cannot read from file"; case bb_err_NotAMap: return "map structure is bad"; case bb_err_Format: return "bad format in Blorb file"; case bb_err_NotFound: return "data not found"; default: return "unknown error"; } } /* This is used for binary searching and quicksorting the resource pointer list. */ static int sortsplot(const void *p1, const void *p2) { bb_resdesc_t *v1 = *(bb_resdesc_t **)p1; bb_resdesc_t *v2 = *(bb_resdesc_t **)p2; if (v1->usage < v2->usage) return -1; if (v1->usage > v2->usage) return 1; return v1->resnum - v2->resnum; } bb_err_t bb_load_chunk_by_type(bb_map_t *map, int method, bb_result_t *res, uint32 type, int count) { int ix; for (ix=0; ix < map->numchunks; ix++) { if (map->chunks[ix].type == type) { if (count == 0) break; count--; } } if (ix >= map->numchunks) { return bb_err_NotFound; } return bb_load_chunk_by_number(map, method, res, ix); } bb_err_t bb_load_chunk_by_number(bb_map_t *map, int method, bb_result_t *res, int chunknum) { bb_chunkdesc_t *chu; if (chunknum < 0 || chunknum >= map->numchunks) return bb_err_NotFound; chu = &(map->chunks[chunknum]); switch (method) { case bb_method_DontLoad: /* do nothing */ break; case bb_method_FilePos: res->data.startpos = chu->datpos; break; case bb_method_Memory: if (!chu->ptr) { bb_err_t err; size_t readlen; void *dat = malloc(chu->len); if (!dat) return bb_err_Alloc; err = fseek(map->file, chu->datpos, 0); if (err) return bb_err_Read; readlen = fread(dat, 1, chu->len, map->file); if (readlen != chu->len) return bb_err_Read; chu->ptr = dat; } res->data.ptr = chu->ptr; break; } res->chunknum = chunknum; res->length = chu->len; return bb_err_None; } bb_err_t bb_load_resource(bb_map_t *map, int method, bb_result_t *res, uint32 usage, int resnum) { bb_resdesc_t sample; bb_resdesc_t *ptr; bb_resdesc_t **found; sample.usage = usage; sample.resnum = resnum; ptr = &sample; found = bsearch(&ptr, map->ressorted, map->numresources, sizeof(bb_resdesc_t *), &sortsplot); if (!found) return bb_err_NotFound; return bb_load_chunk_by_number(map, method, res, (*found)->chunknum); } bb_err_t bb_unload_chunk(bb_map_t *map, int chunknum) { bb_chunkdesc_t *chu; if (chunknum < 0 || chunknum >= map->numchunks) return bb_err_NotFound; chu = &(map->chunks[chunknum]); if (chu->ptr) { free(chu->ptr); chu->ptr = NULL; } return bb_err_None; } bb_err_t bb_count_resources(bb_map_t *map, uint32 usage, int *num, int *min, int *max) { int ix; int count, minval, maxval, val; count = 0; minval = 0; maxval = 0; for (ix=0; ixnumresources; ix++) { if (map->resources[ix].usage == usage) { val = map->resources[ix].resnum; if (count == 0) { count++; minval = val; maxval = val; } else { count++; if (val < minval) minval = val; if (val > maxval) maxval = val; } } } if (num) *num = count; if (min) *min = minval; if (max) *max = maxval; return bb_err_None; } uint16 bb_get_release_num(bb_map_t *map) { return map->releasenum; } bb_zheader_t *bb_get_zheader(bb_map_t *map) { return map->zheader; } bb_resolution_t *bb_get_resolution(bb_map_t *map) { return map->resolution; } bb_err_t bb_get_palette(bb_map_t *map, bb_palette_t **res) { int ix; bb_err_t err; if (res) *res = NULL; if (map->palettechunk < 0) { return bb_err_None; } if (!map->palette) { bb_result_t chunkres; bb_palette_t *pal; unsigned char *ptr; pal = (bb_palette_t *)malloc(sizeof(bb_palette_t)); if (!pal) return bb_err_Alloc; err = bb_load_chunk_by_number(map, bb_method_Memory, &chunkres, map->palettechunk); if (err) return err; ptr = chunkres.data.ptr; if (chunkres.length == 1) { int val = ptr[0]; if (val != 16 && val != 32) return bb_err_Format; pal->isdirect = TRUE; pal->data.depth = val; } else { int size = chunkres.length / 3; bb_color_t *colors = (bb_color_t *)malloc(size * sizeof(bb_color_t)); if (!colors) return bb_err_Alloc; if (size < 1 || size > 256) return bb_err_Format; for (ix=0; ixisdirect = FALSE; pal->data.table.numcolors = size; pal->data.table.colors = colors; } bb_unload_chunk(map, map->palettechunk); map->palette = pal; } if (res) *res = map->palette; return bb_err_None; } bb_err_t bb_load_resource_pict(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_pict_t **auxdata) { bb_err_t err; if (auxdata) *auxdata = NULL; err = bb_load_resource(map, method, res, bb_ID_Pict, resnum); if (err) return err; if (auxdata) { bb_chunkdesc_t *chu = &(map->chunks[res->chunknum]); if (chu->auxdatnum >= 0 && map->auxpict) { *auxdata = &(map->auxpict[chu->auxdatnum]); } } return bb_err_None; } bb_err_t bb_load_resource_snd(bb_map_t *map, int method, bb_result_t *res, int resnum, bb_aux_sound_t **auxdata) { bb_err_t err; if (auxdata) *auxdata = NULL; /* err = bb_load_resource(map, method, res, bb_ID_Pict, resnum); */ err = bb_load_resource(map, method, res, bb_ID_Snd, resnum); if (err) return err; if (auxdata) { bb_chunkdesc_t *chu = &(map->chunks[res->chunknum]); if (chu->auxdatnum >= 0 && map->auxsound) { *auxdata = &(map->auxsound[chu->auxdatnum]); } } return bb_err_None; } frotz-2.44/src/curses/0000755000175000017500000000000012527045477011720 500000000000000frotz-2.44/src/curses/ux_audio_none.c0000644000175000017500000000454412527045477014647 00000000000000/* * ux_audio_none.c - Unix interface, sound support * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #ifdef USE_NCURSES_H #include #else #include #endif #include "ux_frotz.h" #ifdef NO_SOUND /* don't compile this unless we're using no audio */ /* * os_beep * * Play a beep sound. Ideally, the sound should be high- (number == 1) * or low-pitched (number == 2). * */ void os_beep (int number) { beep(); }/* os_beep */ /* * os_prepare_sample * * Load the sample from the disk. * */ void os_prepare_sample (int number) { /* Not implemented */ }/* os_prepare_sample */ /* * os_start_sample * * Play the given sample at the given volume (ranging from 1 to 8 and * 255 meaning a default volume). The sound is played once or several * times in the background (255 meaning forever). In Z-code 3 the * repeats value is always 0 and the number of repeats is taken from * the sound file itself. The end_of_sound function is called as soon * as the sound finishes. * */ void os_start_sample (int number, int volume, int repeats, zword eos) { /* Not implemented */ }/* os_start_sample */ /* * os_stop_sample * * Turn off the current sample. * */ void os_stop_sample (int number) { /* Not implemented */ }/* os_stop_sample */ /* * os_finish_with_sample * * Remove the current sample from memory (if any). * */ void os_finish_with_sample (number) { /* Not implemented */ }/* os_finish_with_sample */ /* * os_wait_sample * * Stop repeating the current sample and wait until it finishes. * */ void os_wait_sample (void) { /* Not implemented */ }/* os_wait_sample */ #endif /* NO_SOUND */ frotz-2.44/src/curses/ux_file.c0000644000175000017500000000553212527045477013444 00000000000000/* * ux_file.c - File handling functions. * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * The objective here is to have ALL attempts to open, close, read, and * write files go through wrapper functions in this file. Hopefully this * will make it easier to do things like support zlib. * */ #define __UNIX_PORT_FILE #include #include #include #include #include #include /* * os_path_open * * Open a file in the current directory. If this fails, then search the * directories in the ZCODE_PATH environmental variable. If that's not * defined, search INFOCOM_PATH. * */ FILE *os_path_open(const char *name, const char *mode) { FILE *fp; char buf[FILENAME_MAX + 1]; char *p; /* Let's see if the file is in the currect directory */ /* or if the user gave us a full path. */ if ((fp = fopen(name, mode))) { return fp; } /* If zcodepath is defined in a config file, check that path. */ /* If we find the file a match in that path, great. */ /* Otherwise, check some environmental variables. */ if (f_setup.zcode_path != NULL) { if ((fp = pathopen(name, f_setup.zcode_path, mode, buf)) != NULL) { strncpy(f_setup.story_name, buf, FILENAME_MAX); return fp; } } if ( (p = getenv(PATH1) ) == NULL) p = getenv(PATH2); if (p != NULL) { fp = pathopen(name, p, mode, buf); strncpy(f_setup.story_name, buf, FILENAME_MAX); return fp; } return NULL; /* give up */ } /* os_path_open() */ /* * pathopen * * Given a standard Unix-style path and a filename, search the path for * that file. If found, return a pointer to that file and put the full * path where the file was found in fullname. * */ FILE *pathopen(const char *name, const char *p, const char *mode, char *fullname) { FILE *fp; char buf[FILENAME_MAX + 1]; char *bp, lastch; lastch = 'a'; /* makes compiler shut up */ while (*p) { bp = buf; while (*p && *p != PATHSEP) lastch = *bp++ = *p++; if (lastch != DIRSEP) *bp++ = DIRSEP; strcpy(bp, name); if ((fp = fopen(buf, mode)) != NULL) { strncpy(fullname, buf, FILENAME_MAX); return fp; } if (*p) p++; } return NULL; } /* FILE *pathopen() */ frotz-2.44/src/curses/ux_blorb.h0000644000175000017500000000132312527045477013624 00000000000000 #include "../blorb/blorb.h" #include "../blorb/blorblow.h" typedef struct sampledata_struct { unsigned short channels; unsigned long samples; unsigned short bits; double rate; } sampledata_t; /* typedef struct blorb_data_struct { bb_map_t map; bb_result_t result; } blorb_data_t; */ bb_err_t blorb_err; bb_map_t *blorb_map; bb_result_t blorb_res; /* uint32 *findchunk(uint32 *data, char *chunkID, int length); */ char *findchunk(char *pstart, char *fourcc, int n); unsigned short ReadShort(const unsigned char *bytes); unsigned long ReadLong(const unsigned char *bytes); double ReadExtended(const unsigned char *bytes); #define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) frotz-2.44/src/curses/ux_text.c0000644000175000017500000002025412527045477013507 00000000000000/* * ux_text.c - Unix interface, text functions * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #include #include #include #ifdef USE_NCURSES_H #include #else #include #endif #include "ux_frotz.h" /* When color_enabled is FALSE, we still minimally keep track of colors by * setting current_color to A_REVERSE if the game reads the default * foreground and background colors and swaps them. If we don't do this, * Strange Results can happen when playing certain V6 games when * color_enabled is FALSE. */ bool color_enabled = FALSE; /* int current_color = 0; */ static char latin1_to_ascii[] = " ! c L >o> 1/41/23/4? " "A A A A Ae A AE C E E E E I I I I " "Th N O O O O Oe * O U U U Ue Y Th ss " "a a a a ae a ae c e e e e i i i i " "th n o o o o oe : o u u u ue y th y "; /* * os_font_data * * Return true if the given font is available. The font can be * * TEXT_FONT * PICTURE_FONT * GRAPHICS_FONT * FIXED_WIDTH_FONT * * The font size should be stored in "height" and "width". If * the given font is unavailable then these values must _not_ * be changed. * */ int os_font_data (int font, int *height, int *width) { if (font == TEXT_FONT) { *height = 1; *width = 1; return 1; /* Truth in advertising */ } return 0; }/* os_font_data */ #ifdef COLOR_SUPPORT /* * unix_convert * * Converts frotz's (and Infocom's) color values to ncurses color values. * */ static int unix_convert(int color) { switch(color) { case BLACK_COLOUR: return COLOR_BLACK; case RED_COLOUR: return COLOR_RED; case GREEN_COLOUR: return COLOR_GREEN; case YELLOW_COLOUR: return COLOR_YELLOW; case BLUE_COLOUR: return COLOR_BLUE; case MAGENTA_COLOUR: return COLOR_MAGENTA; case CYAN_COLOUR: return COLOR_CYAN; case WHITE_COLOUR: return COLOR_WHITE; } return 0; } #endif /* * os_set_colour * * Set the foreground and background colours which can be: * * DEFAULT_COLOUR * BLACK_COLOUR * RED_COLOUR * GREEN_COLOUR * YELLOW_COLOUR * BLUE_COLOUR * MAGENTA_COLOUR * CYAN_COLOUR * WHITE_COLOUR * * MS-DOS 320 columns MCGA mode only: * * GREY_COLOUR * * Amiga only: * * LIGHTGREY_COLOUR * MEDIUMGREY_COLOUR * DARKGREY_COLOUR * * There may be more colours in the range from 16 to 255; see the * remarks on os_peek_colour. * */ void os_set_colour (int new_foreground, int new_background) { if (new_foreground == 1) new_foreground = h_default_foreground; if (new_background == 1) new_background = h_default_background; if (u_setup.color_enabled) { #ifdef COLOR_SUPPORT static int colorspace[10][10]; static int n_colors = 0; if (!colorspace[new_foreground][new_background]) { init_pair(++n_colors, unix_convert(new_foreground), unix_convert(new_background)); colorspace[new_foreground][new_background] = n_colors; } u_setup.current_color = COLOR_PAIR(colorspace[new_foreground][new_background]); #endif } else u_setup.current_color = (((new_foreground == h_default_background) && (new_background == h_default_foreground)) ? A_REVERSE : 0); os_set_text_style(u_setup.current_text_style); }/* os_set_colour */ /* * os_set_text_style * * Set the current text style. Following flags can be set: * * REVERSE_STYLE * BOLDFACE_STYLE * EMPHASIS_STYLE (aka underline aka italics) * FIXED_WIDTH_STYLE * */ void os_set_text_style (int new_style) { int temp = 0; u_setup.current_text_style = new_style; if (new_style & REVERSE_STYLE) temp |= A_REVERSE; if (new_style & BOLDFACE_STYLE) temp |= A_BOLD; if (new_style & EMPHASIS_STYLE) temp |= A_UNDERLINE; attrset(temp ^ u_setup.current_color); }/* os_set_text_style */ /* * os_set_font * * Set the font for text output. The interpreter takes care not to * choose fonts which aren't supported by the interface. * */ void os_set_font (int new_font) { /* Not implemented */ }/* os_set_font */ /* * os_display_char * * Display a character of the current font using the current colours and * text style. The cursor moves to the next position. Printable codes are * all ASCII values from 32 to 126, ISO Latin-1 characters from 160 to * 255, ZC_GAP (gap between two sentences) and ZC_INDENT (paragraph * indentation). The screen should not be scrolled after printing to the * bottom right corner. * */ void os_display_char (zchar c) { if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX) { if (u_setup.plain_ascii) { char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); char c1 = *ptr++; char c2 = *ptr++; char c3 = *ptr++; addch(c1); if (c2 != ' ') addch(c2); if (c3 != ' ') addch(c3); } else addch(c); return; } if (c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX) { addch(c); return; } if (c == ZC_INDENT) { addch(' '); addch(' '); addch(' '); return; } if (c == ZC_GAP) { addch(' '); addch(' '); return; } }/* os_display_char */ /* * os_display_string * * Pass a string of characters to os_display_char. * */ void os_display_string (const zchar *s) { zchar c; while ((c = (unsigned char) *s++) != 0) { if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) { int arg = (unsigned char) *s++; if (c == ZC_NEW_FONT) os_set_font (arg); if (c == ZC_NEW_STYLE) os_set_text_style (arg); } else os_display_char (c); } }/* os_display_string */ /* * os_char_width * * Return the width of the character in screen units. * */ int os_char_width (zchar c) { if (c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX && u_setup.plain_ascii) { int width = 0; const char *ptr = latin1_to_ascii + 3 * (c - ZC_LATIN1_MIN); char c1 = *ptr++; char c2 = *ptr++; char c3 = *ptr++; /* Why, oh, why did you declare variables that way??? */ if (c1 == c1) /* let's avoid confusing the compiler (and me) */ width++; if (c2 != ' ') width++; if (c3 != ' ') width++; return width; } return 1; }/* os_char_width*/ /* * os_string_width * * Calculate the length of a word in screen units. Apart from letters, * the word may contain special codes: * * NEW_STYLE - next character is a new text style * NEW_FONT - next character is a new font * */ int os_string_width (const zchar *s) { int width = 0; zchar c; while ((c = *s++) != 0) { if (c == ZC_NEW_STYLE || c == ZC_NEW_FONT) { s++; /* No effect */ } else width += os_char_width(c); } return width; }/* os_string_width */ /* * os_set_cursor * * Place the text cursor at the given coordinates. Top left is (1,1). * */ void os_set_cursor (int y, int x) { /* Curses thinks the top left is (0,0) */ move(--y, --x); }/* os_set_cursor */ /* * os_more_prompt * * Display a MORE prompt, wait for a keypress and remove the MORE * prompt from the screen. * */ void os_more_prompt (void) { int saved_style, saved_x, saved_y; /* Save some useful information */ saved_style = u_setup.current_text_style; getyx(stdscr, saved_y, saved_x); os_set_text_style(0); addstr("[MORE]"); os_read_key(0, TRUE); move(saved_y, saved_x); addstr(" "); move(saved_y, saved_x); os_set_text_style(saved_style); }/* os_more_prompt */ frotz-2.44/src/curses/ux_screen.c0000644000175000017500000000730012527045477013777 00000000000000/* * ux_screen.c - Unix interface, screen manipulation * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #include #include #include #ifdef USE_NCURSES_H #include #else #include #endif #include "ux_frotz.h" /* * os_erase_area * * Fill a rectangular area of the screen with the current background * colour. Top left coordinates are (1,1). The cursor does not move. * * The final argument gives the window being changed, -1 if only a * portion of a window is being erased, or -2 if the whole screen is * being erased. This is not relevant for the curses interface. * */ void os_erase_area (int top, int left, int bottom, int right, int win) { int y, x, i, j; /* Catch the most common situation and do things the easy way */ if ((top == 1) && (bottom == h_screen_rows) && (left == 1) && (right == h_screen_cols)) { #ifdef COLOR_SUPPORT /* Only set the curses background when doing an erase, so it won't * interfere with the copying we do in os_scroll_area. */ bkgdset(u_setup.current_color | ' '); erase(); bkgdset(0); #else erase(); #endif } else { /* Sigh... */ int saved_style = u_setup.current_text_style; os_set_text_style(u_setup.current_color); getyx(stdscr, y, x); top--; left--; bottom--; right--; for (i = top; i <= bottom; i++) { move(i, left); for (j = left; j <= right; j++) addch(' '); } move(y, x); os_set_text_style(saved_style); } }/* os_erase_area */ /* * os_scroll_area * * Scroll a rectangular area of the screen up (units > 0) or down * (units < 0) and fill the empty space with the current background * colour. Top left coordinates are (1,1). The cursor stays put. * */ void os_scroll_area (int top, int left, int bottom, int right, int units) { top--; left--; bottom--; right--; if ((left == 0) && (right == h_screen_cols - 1)) { static int old_scroll_top = 0; static int old_scroll_bottom = 0; if (!((old_scroll_top == top) && (old_scroll_bottom == bottom))) { old_scroll_top = top; old_scroll_bottom = bottom; setscrreg(top, bottom); } scrollok(stdscr, TRUE); scrl(units); scrollok(stdscr, FALSE); } else { int row, col, x, y; chtype ch; getyx(stdscr, y, x); /* Must turn off attributes during copying. */ attrset(0); if (units > 0) { for (row = top; row <= bottom - units; row++) for (col = left; col <= right; col++) { ch = mvinch(row + units, col); mvaddch(row, col, ch); } } else if (units < 0) { for (row = bottom; row >= top - units; row--) for (col = left; col <= right; col++) { ch = mvinch(row + units, col); mvaddch(row, col, ch); } } /* Restore attributes. */ os_set_text_style(u_setup.current_text_style); move(y, x); } if (units > 0) os_erase_area(bottom - units + 2, left + 1, bottom + 1, right + 1, 0); else if (units < 0) os_erase_area(top + 1, left + 1, top - units, right + 1, 0); }/* os_scroll_area */ frotz-2.44/src/curses/ux_init.c0000644000175000017500000007100212527045477013463 00000000000000/* * ux_init.c - Unix interface, initialisation * Galen Hazelwood * David Griffith * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #include #include #include #include #include #include #include #include /* We will use our own private getopt functions. */ #include "getopt.h" #ifdef USE_NCURSES_H #include #else #include #endif #ifndef WIN32 #include #endif #include "ux_frotz.h" #include "ux_blorb.h" f_setup_t f_setup; u_setup_t u_setup; #define INFORMATION "\ An interpreter for all Infocom and other Z-Machine games.\n\ Complies with standard 1.0 of Graham Nelson's specification.\n\ \n\ Syntax: frotz [options] story-file\n\ -a watch attribute setting \t -O watch object locating\n\ -A watch attribute testing \t -p plain ASCII output only\n\ -b # background color \t -P alter piracy opcode\n\ -c # context lines \t -q quiet (disable sound effects)\n\ -d disable color \t -r # right margin\n\ -e enable sound \t -s # random number seed value\n\ -f # foreground color \t -S # transcript width\n\ -F Force color mode \t -t set Tandy bit\n\ -h # screen height \t -u # slots for multiple undo\n\ -i ignore fatal errors \t -w # screen width\n\ -l # left margin \t -x expand abbreviations g/x/z\n\ -o watch object movement" /* char stripped_story_name[FILENAME_MAX+1]; char semi_stripped_story_name[FILENAME_MAX+1]; */ /* * os_fatal * * Display error message and exit program. * */ void os_fatal (const char *s, ...) { if (u_setup.curses_active) { /* Solaris 2.6's cc complains if the below cast is missing */ os_display_string((zchar *)"\n\n"); os_beep(BEEP_HIGH); os_set_text_style(BOLDFACE_STYLE); os_display_string((zchar *)"Fatal error: "); os_set_text_style(0); os_display_string((zchar *)s); os_display_string((zchar *)"\n"); new_line(); os_reset_screen(); exit(1); } fputs ("\nFatal error: ", stderr); fputs (s, stderr); fputs ("\n\n", stderr); exit (1); }/* os_fatal */ /* extern char script_name[]; */ /* extern char command_name[]; */ /* extern char save_name[];*/ /*extern char auxilary_name[];*/ /* * os_process_arguments * * Handle command line switches. Some variables may be set to activate * special features of Frotz: * * option_attribute_assignment * option_attribute_testing * option_context_lines * option_object_locating * option_object_movement * option_left_margin * option_right_margin * option_ignore_errors * option_piracy * option_undo_slots * option_expand_abbreviations * option_script_cols * * The global pointer "story_name" is set to the story file name. * * */ void os_process_arguments (int argc, char *argv[]) { int c, i; char *p = NULL; char *blorb_ext = NULL; char *home; char configfile[FILENAME_MAX + 1]; #ifndef WIN32 if ((getuid() == 0) || (geteuid() == 0)) { printf("I won't run as root!\n"); exit(1); } #endif #ifdef WIN32 #define HOMEDIR "USERPROFILE" #else #define HOMEDIR "HOME" #endif if ((home = getenv(HOMEDIR)) == NULL) { printf("Hard drive on fire!\n"); exit(1); } /* * It doesn't look like Frotz can reliably be resized given its current * screen-handling code. While playing with Nitfol, I noticed that it * resized itself fairly reliably, even though the terminal looked rather * ugly to begin with. Since Nitfol uses the Glk library for screen I/O, * I think there might be something in Glk that can make resizing easier. * Something to think about for later. * */ /* if (signal(SIGWINCH, SIG_IGN) != SIG_IGN) signal(SIGWINCH, sigwinch_handler); */ if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, sigint_handler); /* First check for a "$HOME/.frotzrc". */ /* If not found, look for CONFIG_DIR/frotz.conf */ /* $HOME/.frotzrc overrides CONFIG_DIR/frotz.conf */ strncpy(configfile, home, FILENAME_MAX); strncat(configfile, "/", 1); strncat(configfile, USER_CONFIG, strlen(USER_CONFIG)); if (!getconfig(configfile)) { strncpy(configfile, CONFIG_DIR, FILENAME_MAX); strncat(configfile, "/", 1); /* added by DJP */ strncat(configfile, MASTER_CONFIG, FILENAME_MAX-10); getconfig(configfile); /* we're not concerned if this fails */ } /* Parse the options */ do { c = getopt(argc, argv, "aAb:c:def:Fh:il:oOpPqr:s:S:tu:w:xZ:"); switch(c) { case 'a': f_setup.attribute_assignment = 1; break; case 'A': f_setup.attribute_testing = 1; break; case 'b': u_setup.background_color = getcolor(optarg); u_setup.force_color = 1; u_setup.disable_color = 0; if ((u_setup.background_color < 2) || (u_setup.background_color > 9)) u_setup.background_color = -1; break; case 'c': f_setup.context_lines = atoi(optarg); break; case 'd': u_setup.disable_color = 1; break; case 'e': f_setup.sound = 1; break; case 'f': u_setup.foreground_color = getcolor(optarg); u_setup.force_color = 1; u_setup.disable_color = 0; if ((u_setup.foreground_color < 2) || (u_setup.foreground_color > 9)) u_setup.foreground_color = -1; break; case 'F': u_setup.force_color = 1; u_setup.disable_color = 0; break; case 'h': u_setup.screen_height = atoi(optarg); break; case 'i': f_setup.ignore_errors = 1; break; case 'l': f_setup.left_margin = atoi(optarg); break; case 'o': f_setup.object_movement = 1; break; case 'O': f_setup.object_locating = 1; break; case 'p': u_setup.plain_ascii = 1; break; case 'P': f_setup.piracy = 1; break; case 'q': f_setup.sound = 0; break; case 'r': f_setup.right_margin = atoi(optarg); break; case 's': u_setup.random_seed = atoi(optarg); break; case 'S': f_setup.script_cols = atoi(optarg); break; case 't': u_setup.tandy_bit = 1; break; case 'u': f_setup.undo_slots = atoi(optarg); break; case 'w': u_setup.screen_width = atoi(optarg); break; case 'x': f_setup.expand_abbreviations = 1; break; case 'Z': f_setup.err_report_mode = atoi(optarg); if ((f_setup.err_report_mode < ERR_REPORT_NEVER) || (f_setup.err_report_mode > ERR_REPORT_FATAL)) f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; break; } } while (c != EOF); if (optind != argc - 1) { printf("FROTZ V%s\t", VERSION); #ifdef OSS_SOUND printf("oss sound driver, "); #endif #ifdef USE_NCURSES printf("ncurses interface."); #else printf("curses interface."); #endif putchar('\n'); puts (INFORMATION); printf ("\t-Z # error checking mode (default = %d)\n" "\t %d = don't report errors %d = report first error\n" "\t %d = report all errors %d = exit after any error\n\n", ERR_DEFAULT_REPORT_MODE, ERR_REPORT_NEVER, ERR_REPORT_ONCE, ERR_REPORT_ALWAYS, ERR_REPORT_FATAL); exit (1); } /* This section is exceedingly messy and really can't be fixed without major changes all over the place. */ /* Save the story file name */ f_setup.story_file = strdup(argv[optind]); f_setup.story_name = strdup(basename(argv[optind])); /* Now strip off the extension. */ p = rindex(f_setup.story_name, '.'); if ((p != NULL) && ((strcmp(p,EXT_BLORB2) == 0) || (strcmp(p,EXT_BLORB3) == 0) || (strcmp(p,EXT_BLORB4) == 0) ) ) { blorb_ext = strdup(p); } else { blorb_ext = strdup(EXT_BLORB); } /* Get rid of extensions with 1 to 6 character extensions. */ /* This will take care of an extension like ".zblorb". */ /* More than that, there might be something weird going on */ /* which is not our concern. */ if (p != NULL) { if (strlen(p) >= 2 && strlen(p) <= 7) { *p = '\0'; /* extension removed */ } } f_setup.story_path = strdup(dirname(argv[optind])); /* Create nice default file names */ u_setup.blorb_name = malloc(FILENAME_MAX * sizeof(char)); strncpy(u_setup.blorb_name, f_setup.story_name, strlen(f_setup.story_name) +1); strncat(u_setup.blorb_name, blorb_ext, strlen(blorb_ext)); u_setup.blorb_file = malloc(strlen(f_setup.story_path) * sizeof(char) + strlen(u_setup.blorb_name) * sizeof(char) + 4); strncpy(u_setup.blorb_file, f_setup.story_path, strlen(f_setup.story_path)); strncat(u_setup.blorb_file, "/", 1); strncat(u_setup.blorb_file, u_setup.blorb_name, strlen(u_setup.blorb_name) + 1); f_setup.script_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); strncpy(f_setup.script_name, f_setup.story_name, strlen(f_setup.story_name)); strncat(f_setup.script_name, EXT_SCRIPT, strlen(EXT_SCRIPT)); f_setup.command_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); strncpy(f_setup.command_name, f_setup.story_name, strlen(f_setup.story_name)); strncat(f_setup.command_name, EXT_COMMAND, strlen(EXT_COMMAND)); f_setup.save_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); strncpy(f_setup.save_name, f_setup.story_name, strlen(f_setup.story_name)); strncat(f_setup.save_name, EXT_SAVE, strlen(EXT_SAVE)); f_setup.aux_name = malloc(strlen(f_setup.story_name) * sizeof(char) + 5); strncpy(f_setup.aux_name, f_setup.story_name, strlen(f_setup.story_name)); strncat(f_setup.aux_name, EXT_AUX, strlen(EXT_AUX)); switch (ux_init_blorb()) { case bb_err_Format: printf("Blorb file loaded, but unable to build map.\n\n"); break; case bb_err_NotFound: printf("Blorb file loaded, but lacks executable chunk.\n\n"); break; } printf("u_setup.blorb_file %s\n", u_setup.blorb_file); printf("u_setup.blorb_name %s\n", u_setup.blorb_name); }/* os_process_arguments */ /* * os_init_screen * * Initialise the IO interface. Prepare the screen and other devices * (mouse, sound board). Set various OS depending story file header * entries: * * h_config (aka flags 1) * h_flags (aka flags 2) * h_screen_cols (aka screen width in characters) * h_screen_rows (aka screen height in lines) * h_screen_width * h_screen_height * h_font_height (defaults to 1) * h_font_width (defaults to 1) * h_default_foreground * h_default_background * h_interpreter_number * h_interpreter_version * h_user_name (optional; not used by any game) * * Finally, set reserve_mem to the amount of memory (in bytes) that * should not be used for multiple undo and reserved for later use. * * (Unix has a non brain-damaged memory model which dosen't require such * ugly hacks, neener neener neener. --GH :) * */ void os_init_screen (void) { /*trace(TRACE_CALLS);*/ if (initscr() == NULL) { /* Set up curses */ os_fatal("Unable to initialize curses. Maybe your $TERM setting is bad."); exit(1); } u_setup.curses_active = 1; /* Let os_fatal know curses is running */ cbreak(); /* Raw input mode, no line processing */ noecho(); /* No input echo */ nonl(); /* No newline translation */ intrflush(stdscr, TRUE); /* Flush output on interrupt */ keypad(stdscr, TRUE); /* Enable the keypad and function keys */ scrollok(stdscr, FALSE); /* No scrolling unless explicitly asked for */ if (h_version == V3 && u_setup.tandy_bit != 0) h_config |= CONFIG_TANDY; if (h_version == V3) h_config |= CONFIG_SPLITSCREEN; if (h_version >= V4) h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS | CONFIG_FIXED | CONFIG_TIMEDINPUT; if (h_version >= V5) h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG); #ifdef NO_SOUND if (h_version >= V5) h_flags &= ~SOUND_FLAG; if (h_version == V3) h_flags &= ~OLD_SOUND_FLAG; #else if ((h_version >= 5) && (h_flags & SOUND_FLAG)) h_flags |= SOUND_FLAG; if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG)) h_flags |= OLD_SOUND_FLAG; if ((h_version == 6) && (f_setup.sound != 0)) h_config |= CONFIG_SOUND; #endif if (h_version >= V5 && (h_flags & UNDO_FLAG)) if (f_setup.undo_slots == 0) h_flags &= ~UNDO_FLAG; getmaxyx(stdscr, h_screen_rows, h_screen_cols); if (u_setup.screen_height != -1) h_screen_rows = u_setup.screen_height; if (u_setup.screen_width != -1) h_screen_cols = u_setup.screen_width; h_screen_width = h_screen_cols; h_screen_height = h_screen_rows; h_font_width = 1; h_font_height = 1; /* Must be after screen dimensions are computed. */ if (h_version == V6) { if (unix_init_pictures()) h_config |= CONFIG_PICTURES; else h_flags &= ~GRAPHICS_FLAG; } /* Use the ms-dos interpreter number for v6, because that's the * kind of graphics files we understand. Otherwise, use DEC. */ h_interpreter_number = h_version == 6 ? INTERP_MSDOS : INTERP_DEC_20; h_interpreter_version = 'F'; #ifdef COLOR_SUPPORT /* Enable colors if the terminal supports them, the user did not * disable colors, and the game or user requested colors. User * requests them by specifying a foreground or background. */ u_setup.color_enabled = (has_colors() && !u_setup.disable_color && (((h_version >= 5) && (h_flags & COLOUR_FLAG)) || (u_setup.foreground_color != -1) || (u_setup.background_color != -1))); /* Maybe we don't want to muck about with changing $TERM to * xterm-color which some supposedly current Unicies still don't * understand. */ if (u_setup.force_color) u_setup.color_enabled = TRUE; if (u_setup.color_enabled) { h_config |= CONFIG_COLOUR; h_flags |= COLOUR_FLAG; /* FIXME: beyond zork handling? */ start_color(); h_default_foreground = (u_setup.foreground_color == -1) ? FOREGROUND_DEF : u_setup.foreground_color; h_default_background = (u_setup.background_color ==-1) ? BACKGROUND_DEF : u_setup.background_color; } else #endif /* COLOR_SUPPORT */ { /* Set these per spec 8.3.2. */ h_default_foreground = WHITE_COLOUR; h_default_background = BLACK_COLOUR; if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG; } os_set_colour(h_default_foreground, h_default_background); os_erase_area(1, 1, h_screen_rows, h_screen_cols, 0); }/* os_init_screen */ /* * os_reset_screen * * Reset the screen before the program stops. * */ void os_reset_screen (void) { os_stop_sample(0); os_set_text_style(0); print_string("[Hit any key to exit.]\n"); os_read_key(0, FALSE); scrollok(stdscr, TRUE); scroll(stdscr); refresh(); endwin(); }/* os_reset_screen */ /* * os_restart_game * * This routine allows the interface to interfere with the process of * restarting a game at various stages: * * RESTART_BEGIN - restart has just begun * RESTART_WPROP_SET - window properties have been initialised * RESTART_END - restart is complete * */ void os_restart_game (int stage) { } /* * os_random_seed * * Return an appropriate random seed value in the range from 0 to * 32767, possibly by using the current system time. * */ int os_random_seed (void) { if (u_setup.random_seed == -1) /* Use the epoch as seed value */ return (time(0) & 0x7fff); else return u_setup.random_seed; }/* os_random_seed */ /* * os_path_open * * Open a file in the current directory. If this fails, then search the * directories in the ZCODE_PATH environmental variable. If that's not * defined, search INFOCOM_PATH. * */ FILE *os_path_open(const char *name, const char *mode) { FILE *fp; char buf[FILENAME_MAX + 1]; char *p; /* Let's see if the file is in the currect directory */ /* or if the user gave us a full path. */ if ((fp = fopen(name, mode))) { return fp; } /* If zcodepath is defined in a config file, check that path. */ /* If we find the file a match in that path, great. */ /* Otherwise, check some environmental variables. */ if (f_setup.zcode_path != NULL) { if ((fp = pathopen(name, f_setup.zcode_path, mode, buf)) != NULL) { strncpy(f_setup.story_name, buf, FILENAME_MAX); return fp; } } if ( (p = getenv(PATH1) ) == NULL) p = getenv(PATH2); if (p != NULL) { fp = pathopen(name, p, mode, buf); strncpy(f_setup.story_name, buf, FILENAME_MAX); return fp; } return NULL; /* give up */ } /* os_path_open() */ /* * os_load_story * * This is different from os_path_open() because we need to see if the * story file is actually a chunk inside a blorb file. Right now we're * looking only at the exact path we're given on the command line. * * Open a file in the current directory. If this fails, then search the * directories in the ZCODE_PATH environmental variable. If that's not * defined, search INFOCOM_PATH. * */ FILE *os_load_story(void) { FILE *fp; /* Did we build a valid blorb map? */ if (u_setup.exec_in_blorb) { fp = fopen(u_setup.blorb_file, "rb"); fseek(fp, blorb_res.data.startpos, SEEK_SET); } else { fp = fopen(f_setup.story_file, "rb"); } return fp; } /* * Seek into a storyfile, either a standalone file or the * ZCODE chunk of a blorb file */ int os_storyfile_seek(FILE * fp, long offset, int whence) { /* Is this a Blorb file containing Zcode? */ if (u_setup.exec_in_blorb) { switch (whence) { case SEEK_END: return fseek(fp, blorb_res.data.startpos + blorb_res.length + offset, SEEK_SET); break; case SEEK_CUR: return fseek(fp, offset, SEEK_CUR); break; case SEEK_SET: default: return fseek(fp, blorb_res.data.startpos + offset, SEEK_SET); break; } } else { return fseek(fp, offset, whence); } } /* * Tell the position in a storyfile, either a standalone file * or the ZCODE chunk of a blorb file */ int os_storyfile_tell(FILE * fp) { /* Is this a Blorb file containing Zcode? */ if (u_setup.exec_in_blorb) { return ftell(fp) - blorb_res.data.startpos; } else { return ftell(fp); } } /* * pathopen * * Given a standard Unix-style path and a filename, search the path for * that file. If found, return a pointer to that file and put the full * path where the file was found in fullname. * */ FILE *pathopen(const char *name, const char *p, const char *mode, char *fullname) { FILE *fp; char buf[FILENAME_MAX + 1]; char *bp, lastch; lastch = 'a'; /* makes compiler shut up */ while (*p) { bp = buf; while (*p && *p != PATHSEP) lastch = *bp++ = *p++; if (lastch != DIRSEP) *bp++ = DIRSEP; strcpy(bp, name); if ((fp = fopen(buf, mode)) != NULL) { strncpy(fullname, buf, FILENAME_MAX); return fp; } if (*p) p++; } return NULL; } /* FILE *pathopen() */ /* * getconfig * * Parse a config file. * The til-end-of-line comment character is the COMMENT define. I use '#' * here. This code originally appeared in my q2-wrapper program. Find it * at metalab.cs.unc.edu or assorted Quake2 websites. * * This section must be modified whenever new options are added to * the config file. Ordinarily I would use yacc and lex, but the grammar * is too simple to involve those resources, and I can't really expect all * compile targets to have those two tools installed. * */ int getconfig(char *configfile) { FILE *fp; int num, num2; char varname[LINELEN + 1]; char value[LINELEN + 1]; /* * We shouldn't care if the config file is unreadable or not * present. Just use the defaults. * */ if ((fp = fopen(configfile, "r")) == NULL) return FALSE; while (fgets(varname, LINELEN, fp) != NULL) { /* If we don't get a whole line, dump the rest of the line */ if (varname[strlen(varname)-1] != '\n') while (fgetc(fp) != '\n') ; /* Remove trailing whitespace and newline */ for (num = strlen(varname) - 1; isspace(varname[num]); num--) ; varname[num+1] = 0; /* Drop everything past the comment character */ for (num = 0; num <= strlen(varname)+1; num++) { if (varname[num] == COMMENT) varname[num] = 0; } /* Find end of variable name */ for (num = 0; !isspace(varname[num]) && num < LINELEN; num++); for (num2 = num; isspace(varname[num2]) && num2 < LINELEN; num2++); /* Find the beginning of the value */ strncpy(value, &varname[num2], LINELEN); varname[num] = 0; /* chop off value from the var name */ /* varname now contains the variable name */ /* First, boolean config stuff */ if (strcmp(varname, "attrib_set") == 0) { f_setup.attribute_assignment = getbool(value); } else if (strcmp(varname, "attrib_test") == 0) { f_setup.attribute_testing = getbool(value); } else if (strcmp(varname, "ignore_fatal") == 0) { f_setup.ignore_errors = getbool(value); } else if (strcmp(varname, "color") == 0) { u_setup.disable_color = !getbool(value); } else if (strcmp(varname, "colour") == 0) { u_setup.disable_color = !getbool(value); } else if (strcmp(varname, "force_color") == 0) { u_setup.force_color = getbool(value); } else if (strcmp(varname, "obj_move") == 0) { f_setup.object_movement = getbool(value); } else if (strcmp(varname, "obj_loc") == 0) { f_setup.object_locating = getbool(value); } else if (strcmp(varname, "piracy") == 0) { f_setup.piracy = getbool(value); } else if (strcmp(varname, "ascii") == 0) { u_setup.plain_ascii = getbool(value); } else if (strcmp(varname, "sound") == 0) { f_setup.sound = getbool(value); } else if (strcmp(varname, "tandy") == 0) { u_setup.tandy_bit = getbool(value); } else if (strcmp(varname, "expand_abb") == 0) { f_setup.expand_abbreviations = getbool(value); } /* now for stringtype yet still numeric variables */ else if (strcmp(varname, "background") == 0) { u_setup.background_color = getcolor(value); } else if (strcmp(varname, "foreground") == 0) { u_setup.foreground_color = getcolor(value); } else if (strcmp(varname, "context_lines") == 0) { f_setup.context_lines = atoi(value); } else if (strcmp(varname, "screen_height") == 0) { u_setup.screen_height = atoi(value); } else if (strcmp(varname, "left_margin") == 0) { f_setup.left_margin = atoi(value); } else if (strcmp(varname, "right_margin") == 0) { f_setup.right_margin = atoi(value); } else if (strcmp(varname, "randseed") == 0) { u_setup.random_seed = atoi(value); } else if (strcmp(varname, "script_width") == 0) { f_setup.script_cols = atoi(value); } else if (strcmp(varname, "undo_slots") == 0) { f_setup.undo_slots = atoi(value); } else if (strcmp(varname, "screen_width") == 0) { u_setup.screen_width = atoi(value); } /* default is set in main() by call to init_err() */ else if (strcmp(varname, "errormode") == 0) { f_setup.err_report_mode = geterrmode(value); } /* now for really stringtype variable */ else if (strcmp(varname, "zcode_path") == 0) { f_setup.zcode_path = malloc(strlen(value) * sizeof(char) + 1); strncpy(f_setup.zcode_path, value, strlen(value) * sizeof(char)); } /* The big nasty if-else thingy is finished */ } /* while */ return TRUE; } /* getconfig() */ /* * getbool * * Check a string for something that means "yes" and return TRUE. * Otherwise return FALSE. * */ int getbool(char *value) { int num; /* Be case-insensitive */ for (num = 0; value[num] !=0; num++) value[num] = tolower(value[num]); if (strncmp(value, "y", 1) == 0) return TRUE; if (strcmp(value, "true") == 0) return TRUE; if (strcmp(value, "on") == 0) return TRUE; if (strcmp(value, "1") == 0) return TRUE; return FALSE; } /* getbool() */ /* * getcolor * * Figure out what color this string might indicate and returns an integer * corresponding to the color macros defined in frotz.h. * */ int getcolor(char *value) { int num; /* Be case-insensitive */ for (num = 0; value[num] !=0; num++) value[num] = tolower(value[num]); if (strcmp(value, "black") == 0) return BLACK_COLOUR; if (strcmp(value, "red") == 0) return RED_COLOUR; if (strcmp(value, "green") == 0) return GREEN_COLOUR; if (strcmp(value, "blue") == 0) return BLUE_COLOUR; if (strcmp(value, "magenta") == 0) return MAGENTA_COLOUR; if (strcmp(value, "cyan") == 0) return CYAN_COLOUR; if (strcmp(value, "white") == 0) return WHITE_COLOUR; if (strcmp(value, "purple") == 0) return MAGENTA_COLOUR; if (strcmp(value, "violet") == 0) return MAGENTA_COLOUR; if (strcmp(value, "aqua") == 0) return CYAN_COLOUR; /* If we can't tell what that string means, * we tell caller to use the default. */ return -1; } /* getcolor() */ /* * geterrmode * * Parse for "never", "once", "always", or "fatal" and return a macro * defined in ux_frotz.h related to the error reporting mode. * */ int geterrmode(char *value) { int num; /* Be case-insensitive */ for (num = 0; value[num] !=0; num++) value[num] = tolower(value[num]); if (strcmp(value, "never") == 0) return ERR_REPORT_NEVER; if (strcmp(value, "once") == 0) return ERR_REPORT_ONCE; if (strcmp(value, "always") == 0) return ERR_REPORT_ALWAYS; if (strcmp(value, "fatal") == 0) return ERR_REPORT_FATAL; return ERR_DEFAULT_REPORT_MODE; } /* geterrmode() */ /* * sigwinch_handler * * Called whenever Frotz recieves a SIGWINCH signal to make curses * cleanly resize the window. * */ void sigwinch_handler(int sig) { /* There are some significant problems involved in getting resizes to work properly with at least this implementation of the Z Machine and probably the Z-Machine standard itself. See the file BUGS for a detailed explaination for this. Because of this trouble, this function currently does nothing. */ } /* * sigint_handler * Sometimes the screen will be left in a weird state if the following * is not done. * */ void sigint_handler(int dummy) { signal(SIGINT, sigint_handler); scrollok(stdscr, TRUE); scroll(stdscr); refresh(); endwin(); exit(1); } void redraw(void) { /* not implemented */ } void os_init_setup(void) { f_setup.attribute_assignment = 0; f_setup.attribute_testing = 0; f_setup.context_lines = 0; f_setup.object_locating = 0; f_setup.object_movement = 0; f_setup.left_margin = 0; f_setup.right_margin = 0; f_setup.ignore_errors = 0; f_setup.piracy = 0; /* enable the piracy opcode */ f_setup.undo_slots = MAX_UNDO_SLOTS; f_setup.expand_abbreviations = 0; f_setup.script_cols = 80; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; u_setup.use_blorb = 0; u_setup.exec_in_blorb = 0; u_setup.disable_color = 0; u_setup.force_color = 0; u_setup.foreground_color = -1; u_setup.background_color = -1; u_setup.screen_width = -1; u_setup.screen_height = -1; u_setup.random_seed = -1; u_setup.random_seed = -1; u_setup.tandy_bit = 0; u_setup.current_text_style = 0; /* Since I can't use attr_get, which would make things easier, I need to use the same hack the MS-DOS port does...keep the current style in a global variable. */ u_setup.plain_ascii = 0; /* true if user wants to disable Latin-1 */ u_setup.curses_active = 0; /* true if os_init_screen has run */ /* u_setup.interpreter = INTERP_DEFAULT; */ u_setup.current_color = 0; u_setup.color_enabled = FALSE; } int ux_init_blorb(void) { FILE *blorbfile; /* If the filename given on the command line is the same as our * computed blorb filename, then we will assume the executable * is contained in the blorb file. */ if (strncmp(basename(f_setup.story_file), basename(u_setup.blorb_file), 55) == 0) { if ((blorbfile = fopen(u_setup.blorb_file, "rb")) == NULL) return bb_err_Read; blorb_err = bb_create_map(blorbfile, &blorb_map); if (blorb_err != bb_err_None) return bb_err_Format; /* Now we need to locate the EXEC chunk within the blorb file * and present it to the rest of the program as a file stream. */ blorb_err = bb_load_chunk_by_type(blorb_map, bb_method_FilePos, &blorb_res, bb_make_id('Z','C','O','D'), 0); if (blorb_err == bb_err_None) { u_setup.exec_in_blorb = 1; u_setup.use_blorb = 1; } return blorb_err; } } frotz-2.44/src/curses/getopt.h0000644000175000017500000001061112527042452013300 00000000000000/* Declarations for getopt. Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc. This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this program; if not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __UNISTD_LOADED #ifndef _GETOPT_H #define _GETOPT_H 1 #ifdef __cplusplus extern "C" { #endif /* For communication from `getopt' to the caller. When `getopt' finds an option that takes an argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is returned here. */ extern char *optarg; /* Index in ARGV of the next element to be scanned. This is used for communication to and from the caller and for communication between successive calls to `getopt'. On entry to `getopt', zero means this is the first call; initialize. When `getopt' returns EOF, this is the index of the first of the non-option elements that the caller should itself scan. Otherwise, `optind' communicates from one call to the next how much of ARGV has been scanned so far. */ extern int optind; /* Callers store zero here to inhibit the error message `getopt' prints for unrecognized options. */ extern int opterr; /* Set to an option character which was unrecognized. */ extern int optopt; /* Describe the long-named options requested by the application. The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector of `struct option' terminated by an element containing a name which is zero. The field `has_arg' is: no_argument (or 0) if the option does not take an argument, required_argument (or 1) if the option requires an argument, optional_argument (or 2) if the option takes an optional argument. If the field `flag' is not NULL, it points to a variable that is set to the value given in the field `val' when the option is found, but left unchanged if the option is not found. To have a long-named option do something other than set an `int' to a compiled-in constant, such as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a nonzero value (the equivalent single-letter option character, if there is one). For long options that have a zero `flag' field, `getopt' returns the contents of the `val' field. */ struct option { #if __STDC__ const char *name; #else char *name; #endif /* has_arg can't be an enum because some compilers complain about type mismatches in all the code that assumes it is an int. */ int has_arg; int *flag; int val; }; /* Names for the values of the `has_arg' field of `struct option'. */ #define no_argument 0 #define required_argument 1 #define optional_argument 2 #if __STDC__ #if defined(__GNU_LIBRARY__) /* Many other libraries have conflicting prototypes for getopt, with differences in the consts, in stdlib.h. To avoid compilation errors, only prototype getopt for the GNU C library. */ extern int getopt (int argc, char *const *argv, const char *shortopts); #else /* not __GNU_LIBRARY__ */ extern int getopt (); #endif /* not __GNU_LIBRARY__ */ extern int getopt_long (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); extern int getopt_long_only (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind); /* Internal only. Users should not call this directly. */ extern int _getopt_internal (int argc, char *const *argv, const char *shortopts, const struct option *longopts, int *longind, int long_only); #else /* not __STDC__ */ extern int getopt (); extern int getopt_long (); extern int getopt_long_only (); extern int _getopt_internal (); #endif /* not __STDC__ */ #ifdef __cplusplus } #endif #endif /* _GETOPT_H */ #endif /* __UNISTD_LOADED */ frotz-2.44/src/curses/ux_pic.c0000644000175000017500000002276012527045477013302 00000000000000/* * ux_pic.c - Unix interface, picture outline functions * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #include #include #ifdef USE_NCURSES_H #include #else #include #endif #include "ux_frotz.h" #define PIC_FILE_HEADER_FLAGS 1 #define PIC_FILE_HEADER_NUM_IMAGES 4 #define PIC_FILE_HEADER_ENTRY_SIZE 8 #define PIC_FILE_HEADER_VERSION 14 #define PIC_HEADER_NUMBER 0 #define PIC_HEADER_WIDTH 2 #define PIC_HEADER_HEIGHT 4 static void safe_mvaddch(int, int, int); static void save_scrnset(int, int, int, int); static struct { int z_num; int width; int height; int orig_width; int orig_height; } *pict_info; static int num_pictures = 0; static unsigned char lookupb(unsigned char *p, int n) { return p[n]; } static unsigned short lookupw(unsigned char *p, int n) { return (p[n + 1] << 8) | p[n]; } /* * Do a rounding division, rounding to even if fraction part is 1/2. * We assume x and y are nonnegative. * */ static int round_div(int x, int y) { int quotient = x / y; int dblremain = (x % y) << 1; if ((dblremain > y) || (dblremain == y) && (quotient & 1)) quotient++; return quotient; } bool unix_init_pictures (void) { FILE *file = NULL; int success = FALSE; unsigned char gheader[16]; unsigned char *raw_info = NULL; char *filename; const char *basename, *dotpos; int namelen; if ((filename = malloc(2 * strlen(f_setup.story_name) + 10)) == NULL) return FALSE; basename = strrchr(f_setup.story_name, '/'); if (basename) basename++; else basename = f_setup.story_name; dotpos = strrchr(basename, '.'); namelen = (dotpos ? dotpos - basename : strlen(basename)); sprintf(filename, "%.*sgraphics/%.*s.mg1", basename - f_setup.story_name, f_setup.story_name, namelen, basename); do { int i, entry_size, flags, x_scale, y_scale; if (((file = fopen (filename, "rb")) == NULL) || (fread(&gheader, sizeof (gheader), 1, file) != 1)) break; num_pictures = lookupw(gheader, PIC_FILE_HEADER_NUM_IMAGES); entry_size = lookupb(gheader, PIC_FILE_HEADER_ENTRY_SIZE); flags = lookupb(gheader, PIC_FILE_HEADER_FLAGS); raw_info = malloc(num_pictures * entry_size); if (fread(raw_info, num_pictures * entry_size, 1, file) != 1) break; pict_info = malloc((num_pictures + 1) * sizeof(*pict_info)); pict_info[0].z_num = 0; pict_info[0].height = num_pictures; pict_info[0].width = lookupw(gheader, PIC_FILE_HEADER_VERSION); y_scale = 200; x_scale = (flags & 0x08) ? 640 : 320; /* Copy and scale. */ for (i = 1; i <= num_pictures; i++) { unsigned char *p = raw_info + entry_size * (i - 1); int height, width; pict_info[i].z_num = lookupw(p, PIC_HEADER_NUMBER); pict_info[i].orig_height = lookupw(p, PIC_HEADER_HEIGHT); pict_info[i].orig_width = lookupw(p, PIC_HEADER_WIDTH); pict_info[i].height = round_div(pict_info[i].orig_height * h_screen_rows, y_scale); pict_info[i].width = round_div(pict_info[i].orig_width * h_screen_cols, x_scale); /* Don't let dimensions get rounded to nothing. */ if (pict_info[i].orig_height && !pict_info[i].height) pict_info[1].height = 1; if (pict_info[i].orig_width && !pict_info[i].width) pict_info[i].width = 1; } success = TRUE; } while (0); if (file) fclose(file); if (raw_info) free(raw_info); return success; } /* Convert a Z picture number to an index into pict_info. */ static int z_num_to_index(int n) { int i; for (i = 0; i <= num_pictures; i++) if (pict_info[i].z_num == n) return i; return -1; } /* * os_picture_data * * Return true if the given picture is available. If so, write the * width and height of the picture into the appropriate variables. * Only when picture 0 is asked for, write the number of available * pictures and the release number instead. * */ int os_picture_data(int num, int *height, int *width) { int index; *height = 0; *width = 0; if (!pict_info) return FALSE; if ((index = z_num_to_index(num)) == -1) return FALSE; *height = pict_info[index].height; *width = pict_info[index].width; return TRUE; } /* * Do a mvaddch if the coordinates aren't too large. * */ static void safe_mvaddch(int y, int x, int ch) { if ((y < h_screen_rows) && (x < h_screen_cols)) mvaddch(y, x, ch); } /* * Set n chars starting at (x, y), doing bounds checking. * */ static void safe_scrnset(int y, int x, int ch, int n) { if ((y < h_screen_rows) && (x < h_screen_cols)) { move(y, x); if (x + n > h_screen_cols) n = h_screen_cols - x; while (n--) addch(ch); } } /* * os_draw_picture * * Display a picture at the given coordinates. Top left is (1,1). * */ /* TODO: handle truncation correctly. Spec 8.8.3 says all graphics should * be clipped to the current window. To do that, we should probably * modify z_draw_picture in the frotz core to pass some extra parameters. */ void os_draw_picture (int num, int row, int col) { int width, height, r, c; int saved_x, saved_y; static int plus, ltee, rtee, ttee, btee, hline, vline, ckboard; static int urcorner, ulcorner, llcorner, lrcorner; static bool acs_initialized = FALSE; if (!acs_initialized) { plus = u_setup.plain_ascii ? '+' : ACS_PLUS; ltee = u_setup.plain_ascii ? '<' : ACS_LTEE; rtee = u_setup.plain_ascii ? '>' : ACS_RTEE; ttee = u_setup.plain_ascii ? '^' : ACS_TTEE; btee = u_setup.plain_ascii ? 'v' : ACS_BTEE; hline = u_setup.plain_ascii ? '-' : ACS_HLINE; vline = u_setup.plain_ascii ? '|' : ACS_VLINE; ckboard = u_setup.plain_ascii ? ':' : ACS_CKBOARD; urcorner = u_setup.plain_ascii ? '\\' : ACS_URCORNER; ulcorner = u_setup.plain_ascii ? '/' : ACS_ULCORNER; llcorner = u_setup.plain_ascii ? '\\' : ACS_LLCORNER; lrcorner = u_setup.plain_ascii ? '/' : ACS_LRCORNER; acs_initialized = TRUE; } if (!os_picture_data(num, &height, &width) || !width || !height) return; col--, row--; getyx(stdscr, saved_y, saved_x); /* General case looks like: * /----\ * |::::| * |::42| * \----/ * * Special cases are: 1 x n: n x 1: 1 x 1: * * ^ * | * <-----> | + * | * v */ if ((height == 1) && (width == 1)) safe_mvaddch(row, col, plus); else if (height == 1) { safe_mvaddch(row, col, ltee); safe_scrnset(row, col + 1, hline, width - 2); safe_mvaddch(row, col + width - 1, rtee); } else if (width == 1) { safe_mvaddch(row, col, ttee); for (r = row + 1; r < row + height - 1; r++) safe_mvaddch(r, col, vline); safe_mvaddch(row + height - 1, col, btee); } else { safe_mvaddch(row, col, ulcorner); safe_scrnset(row, col + 1, hline, width - 2); safe_mvaddch(row, col + width - 1, urcorner); for (r = row + 1; r < row + height - 1; r++) { safe_mvaddch(r, col, vline); safe_scrnset(r, col + 1, ckboard, width - 2); safe_mvaddch(r, col + width - 1, vline); } safe_mvaddch(row + height - 1, col, llcorner); safe_scrnset(row + height - 1, col + 1, hline, width - 2); safe_mvaddch(row + height - 1, col + width - 1, lrcorner); } /* Picture number. */ if (height > 2) { for (c = col + width - 2; c > col && num > 0; c--, (num /= 10)) safe_mvaddch(row + height - 2, c, '0' + num % 10); } move(saved_y, saved_x); } /* * os_peek_colour * * Return the colour of the pixel below the cursor. This is used * by V6 games to print text on top of pictures. The coulor need * not be in the standard set of Z-machine colours. To handle * this situation, Frotz extends the colour scheme: Values above * 15 (and below 256) may be used by the interface to refer to * non-standard colours. Of course, os_set_colour must be able to * deal with these colours. Interfaces which refer to characters * instead of pixels might return the current background colour * instead. * */ int os_peek_colour (void) { if (u_setup.color_enabled) { #ifdef COLOR_SUPPORT short fg, bg; pair_content(PAIR_NUMBER(inch() & A_COLOR), &fg, &bg); switch(bg) { case COLOR_BLACK: return BLACK_COLOUR; case COLOR_RED: return RED_COLOUR; case COLOR_GREEN: return GREEN_COLOUR; case COLOR_YELLOW: return YELLOW_COLOUR; case COLOR_BLUE: return BLUE_COLOUR; case COLOR_MAGENTA: return MAGENTA_COLOUR; case COLOR_CYAN: return CYAN_COLOUR; case COLOR_WHITE: return WHITE_COLOUR; } return 0; #endif /* COLOR_SUPPORT */ } else { return (inch() & A_REVERSE) ? h_default_foreground : h_default_background; } } frotz-2.44/src/curses/ux_frotz.h0000644000175000017500000000501712527045477013674 00000000000000/* * ux_frotz.h * * Unix interface, declarations, definitions, and defaults * */ #include "../common/frotz.h" #include "../blorb/blorb.h" #include "ux_setup.h" #define MASTER_CONFIG "frotz.conf" #define USER_CONFIG ".frotzrc" #define ASCII_DEF 1 #define ATTRIB_ASSIG_DEF 0 #define ATTRIB_TEST_DEF 0 #define COLOR_DEF 1 #define ERROR_HALT_DEF 0 #define EXPAND_DEF 0 #define PIRACY_DEF 0 #define TANDY_DEF 0 #define OBJ_MOVE_DEF 0 #define OBJ_LOC_DEF 0 #define BACKGROUND_DEF BLACK_COLOUR #define FOREGROUND_DEF WHITE_COLOUR #define HEIGHT_DEF -1 /* let curses figure it out */ #define CONTEXTLINES_DEF 0 #define WIDTH_DEF 80 #define TWIDTH_DEF 80 #define SEED_DEF -1 #define SLOTS_DEF MAX_UNDO_SLOTS #define LMARGIN_DEF 0 #define RMARGIN_DEF 0 #define ERR_REPORT_DEF ERR_REPORT_ONCE #define QUETZAL_DEF 1 #define SAVEDIR_DEF "if-saves" #define ZCODEPATH_DEF "/usr/games/zcode:/usr/local/games/zcode" #define LINELEN 256 /* for getconfig() */ #define COMMENT '#' /* for config files */ #define PATHSEP ':' /* for pathopen() */ #define DIRSEP '/' /* for pathopen() */ #define EDITMODE_EMACS 0 #define EDITMODE_VI 1 #define PIC_NUMBER 0 #define PIC_WIDTH 2 #define PIC_HEIGHT 4 #define PIC_FLAGS 6 #define PIC_DATA 8 #define PIC_COLOUR 11 /* Paths where z-files may be found */ #define PATH1 "ZCODE_PATH" #define PATH2 "INFOCOM_PATH" #define NO_SOUND #ifdef OSS_SOUND # undef NO_SOUND #endif /* Some regular curses (not ncurses) libraries don't do this correctly. */ #ifndef getmaxyx #define getmaxyx(w, y, x) (y) = getmaxy(w), (x) = getmaxx(w) #endif extern bool color_enabled; /* ux_text */ extern char stripped_story_name[FILENAME_MAX+1]; extern char semi_stripped_story_name[FILENAME_MAX+1]; extern char *progname; extern char *gamepath; /* use to find sound files */ extern f_setup_t f_setup; extern u_setup_t u_setup; /*** Blorb related stuff ***/ bb_err_t blorb_err; bb_map_t *blorb_map; bb_result_t blorb_res; /*** Functions specific to the Unix port of Frotz ***/ bool unix_init_pictures(void); /* ux_pic.c */ bool unix_init_pictures(void); /* ux_pic.c */ void unix_init_scrollback(void); /* ux_screen.c */ void unix_save_screen(int); /* ux_screen.c */ void unix_do_scrollback(void); /* ux_screen.c */ int getconfig(char *); int geterrmode(char *); int getcolor(char *); int getbool(char *); FILE *pathopen(const char *, const char *, const char *, char *); void sigwinch_handler(int); void sigint_handler(int); void redraw(void); #ifdef NO_MEMMOVE void *memmove(void *, void *); #endif frotz-2.44/src/curses/ux_setup.h0000644000175000017500000000107412527045477013667 00000000000000typedef struct unix_setup_struct { int disable_color; int force_color; int foreground_color; int background_color; int screen_width; int screen_height; int random_seed; int tandy_bit; int current_text_style; /* also in ux_text.c and ux_screen.c */ int curses_active; int plain_ascii; int current_color; /* ux_text.c ux_screen.c */ bool color_enabled; /* ux_init.c ux_pic.c ux_text.c */ char *blorb_name; char *blorb_file; bool use_blorb; bool exec_in_blorb; int interpreter; /* see frotz.h */ } u_setup_t; extern u_setup_t u_setup; frotz-2.44/src/curses/ux_blorb.c0000644000175000017500000000272512527045477013626 00000000000000#include #include #include #include #include #include #include "../common/frotz.h" #include "../blorb/blorb.h" #include "../blorb/blorblow.h" #include "ux_blorb.h" char *findchunk(char *data, char *string, int length) { char *mydata = data+12; while (TRUE) { if (strncmp((char*)mydata, string, 4) == 0) return mydata+8; mydata += ReadLong(mydata+4)+8; if ((mydata - data) >= length) break; } return NULL; } unsigned short ReadShort(const unsigned char *bytes) { return (unsigned short)( ((unsigned short)(bytes[0] & 0xFF) << 8) | ((unsigned short)(bytes[1] & 0xFF))); } unsigned long ReadLong(const unsigned char *bytes) { return (unsigned long)( ((unsigned long)(bytes[0] & 0xFF) << 24) | ((unsigned long)(bytes[1] & 0xFF) << 16) | ((unsigned long)(bytes[2] & 0xFF) << 8) | ((unsigned long)(bytes[3] & 0xFF))); } double ReadExtended(const unsigned char *bytes) { double f; int expon; unsigned long hiMant, loMant; expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); hiMant = ReadLong(bytes+2); loMant = ReadLong(bytes+6); if (expon == 0 && hiMant == 0 && loMant == 0) f = 0; else { if (expon == 0x7FFF) /* Infinity or NaN */ f = -1; else { expon -= 16383; /* must #include or these won't work */ f = ldexp(UnsignedToFloat(hiMant),expon -= 31); f += ldexp(UnsignedToFloat(loMant),expon -= 32); } } if (bytes[0] & 0x80) return -f; return f; } frotz-2.44/src/curses/ux_audio_oss.c0000644000175000017500000002153212527045477014510 00000000000000/* * ux_audio_oss.c - Sound support using the OSS drivers * * This code is mostly verbatim from the file x_sample.c in Daniel * Schepler's xfrotz-2.32.1. * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #include /* #include */ #ifdef USE_NCURSES_H #include #else #include #endif #include "ux_frotz.h" #ifdef OSS_SOUND /* don't compile this if not using OSS */ #include #include #include #include #include #include #include #include #include #include "../soundcard.h" extern void end_of_sound(void); /* Buffer used to store sample data */ static char *sound_buffer = NULL; static int sound_length; static int sample_rate; static int current_num; /* Implementation of the separate process which plays the sounds. The signals used to communicate with the process are: SIGINT - complete current repeat of the sound, then quit SIGTERM - stop sound immediately */ static pid_t child_pid; /* Number of repeats left */ static int num_repeats; /* File handles for mixer and PCM devices */ static int mixer_fd, dsp_fd; static int old_volume; static void sigterm_handler(int signal) { ioctl(dsp_fd, SNDCTL_DSP_RESET, 0); if (mixer_fd >= 0) ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &old_volume); _exit(0); } static void sigint_handler(int signal) { num_repeats = 1; } static void play_sound(int volume, int repeats) { struct sigaction sa; int format = AFMT_U8; int channels = 1; dsp_fd = open(SOUND_DEV, O_WRONLY); if (dsp_fd < 0) { perror(SOUND_DEV); _exit(1); } /* This section of code fixes the nasty problem of samples * being played with pops and scratches when used with a * non-Linux system implementing OSS sound. */ if (ioctl(dsp_fd, SNDCTL_DSP_SETFMT, &format) == -1) { perror(SOUND_DEV); exit(1); } if (format != AFMT_U8) { fprintf(stderr, "bad sound format\n"); exit(1); } if (ioctl(dsp_fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { perror(SOUND_DEV); exit(1); } if (channels != 1) { fprintf(stderr, "bad channels\n"); exit(1); } /* End sound fix from Torbjorn Andersson (no dot thingies) */ ioctl(dsp_fd, SNDCTL_DSP_SPEED, &sample_rate); if (volume != 255) { mixer_fd = open("/dev/mixer", O_RDWR); if (mixer_fd < 0) perror("/dev/mixer"); else { int new_vol; ioctl(mixer_fd, SOUND_MIXER_READ_VOLUME, &old_volume); new_vol = volume * 100 / 8; new_vol = (new_vol << 8) | new_vol; ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &new_vol); } } else mixer_fd = -1; sa.sa_handler = sigterm_handler; sigemptyset(&sa.sa_mask); sigaddset(&sa.sa_mask, SIGINT); sigaddset(&sa.sa_mask, SIGTERM); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); sa.sa_handler = sigint_handler; sigaction(SIGINT, &sa, NULL); for (num_repeats = repeats; num_repeats > 0; num_repeats < 255 ? num_repeats-- : 0) { char *curr_pos = sound_buffer; int len_left = sound_length; int write_result; while (len_left > 0) { write_result = write(dsp_fd, curr_pos, len_left); if (write_result <= 0) { perror(SOUND_DEV); goto finish; } curr_pos += write_result; len_left -= write_result; } } finish: ioctl(dsp_fd, SNDCTL_DSP_SYNC, 0); if (mixer_fd >= 0) ioctl(mixer_fd, SOUND_MIXER_WRITE_VOLUME, &old_volume); _exit(0); } /* * os_beep * * Play a beep sound. Ideally, the sound should be high- (number == 1) * or low-pitched (number == 2). * */ void os_beep (int number) { /* This should later be expanded to support high and low beeps. */ beep(); }/* os_beep */ /* * os_prepare_sample * * Load the sample from the disk. * */ void os_prepare_sample (int number) { FILE *samples; char *filename; const char *basename, *dotpos; int namelen; int read_length; if (sound_buffer != NULL && current_num == number) return; free(sound_buffer); sound_buffer = NULL; filename = malloc(strlen(f_setup.story_name) + 10); if (! filename) return; basename = strrchr(f_setup.story_name, '/'); if (basename) basename++; else basename = f_setup.story_name; dotpos = strrchr(basename, '.'); namelen = (dotpos ? dotpos - basename : strlen(basename)); if (namelen > 6) namelen = 6; sprintf(filename, "%.*ssound/%.*s%02d.snd", basename - f_setup.story_name, f_setup.story_name, namelen, basename, number); samples = fopen(filename, "r"); if (samples == NULL) { perror(filename); return; } fgetc(samples); fgetc(samples); fgetc(samples); fgetc(samples); sample_rate = fgetc(samples) << 8; sample_rate |= fgetc(samples); fgetc(samples); fgetc(samples); sound_length = fgetc(samples) << 8; sound_length |= fgetc(samples); sound_buffer = NULL; if (sound_length > 0) { sound_buffer = malloc(sound_length); if (!sound_buffer) { perror("malloc"); return; } read_length = fread(sound_buffer, 1, sound_length, samples); if (read_length < sound_length) { if (feof(samples)) { /* * One of the Sherlock samples trigger this for me, so let's make it * a non-fatal error. */ sound_buffer = realloc(sound_buffer, read_length); if (! sound_buffer) { perror("realloc"); return; } sound_length = read_length; } else { errno = ferror(samples); perror(filename); free(sound_buffer); sound_buffer = NULL; } } } }/* os_prepare_sample */ static void sigchld_handler(int signal) { int status; struct sigaction sa; waitpid(child_pid, &status, WNOHANG); child_pid = 0; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGCHLD, &sa, NULL); end_of_sound(); } /* * os_start_sample * * Play the given sample at the given volume (ranging from 1 to 8 and * 255 meaning a default volume). The sound is played once or several * times in the background (255 meaning forever). In Z-code 3 the * repeats value is always 0 and the number of repeats is taken from * the sound file itself. The end_of_sound function is called as soon * as the sound finishes. * */ void os_start_sample (int number, int volume, int repeats, zword eos) { /* INCOMPLETE */ sigset_t sigchld_mask; struct sigaction sa; os_prepare_sample(number); if (! sound_buffer) return; os_stop_sample(0); sigemptyset(&sigchld_mask); sigaddset(&sigchld_mask, SIGCHLD); sigprocmask(SIG_BLOCK, &sigchld_mask, NULL); child_pid = fork(); if (child_pid < 0) { /* error in fork */ perror("fork"); return; } else if (child_pid == 0) { /* child */ sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); play_sound(volume, repeats); } else { /* parent */ sa.sa_handler = sigchld_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGCHLD, &sa, NULL); sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); } }/* os_start_sample */ /* Send the specified signal to the player program, then wait for it to exit. */ static void stop_player(int signal) { sigset_t sigchld_mask; struct sigaction sa; int status; sigemptyset(&sigchld_mask); sigaddset(&sigchld_mask, SIGCHLD); sigprocmask(SIG_BLOCK, &sigchld_mask, NULL); if (child_pid == 0) { sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); return; } kill(child_pid, signal); waitpid(child_pid, &status, 0); child_pid = 0; sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGCHLD, &sa, NULL); sigprocmask(SIG_UNBLOCK, &sigchld_mask, NULL); } /* * os_stop_sample * * Turn off the current sample. * */ void os_stop_sample (int number) { /* INCOMPLETE */ stop_player(SIGTERM); }/* os_stop_sample */ /* * os_finish_with_sample * * Remove the current sample from memory (if any). * */ void os_finish_with_sample (int number) { /* INCOMPLETE */ free(sound_buffer); sound_buffer = NULL; }/* os_finish_with_sample */ /* * os_wait_sample * * Stop repeating the current sample and wait until it finishes. * */ void os_wait_sample (void) { stop_player(SIGINT); }/* os_wait_sample */ #endif /* OSS_SOUND */ frotz-2.44/src/curses/ux_input.c0000644000175000017500000004731312527045477013667 00000000000000/* * ux_input.c - Unix interface, input functions * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #define __UNIX_PORT_FILE #include #include #include #include #include #ifdef USE_NCURSES_H #include #else #include #endif #include "ux_frotz.h" static struct timeval global_timeout; /* Some special characters. */ #define MOD_CTRL 0x40 #define MOD_META 0x80 #define CHR_DEL (MOD_CTRL ^'?') /* These are useful for circular buffers. */ #define RING_DEC( ptr, beg, end) (ptr > (beg) ? --ptr : (ptr = (end))) #define RING_INC( ptr, beg, end) (ptr < (end) ? ++ptr : (ptr = (beg))) #define MAX_HISTORY 20 static char *history_buffer[MAX_HISTORY]; static char **history_next = history_buffer; /* Next available slot. */ static char **history_view = history_buffer; /* What the user is looking at. */ #define history_end (history_buffer + MAX_HISTORY - 1) extern bool is_terminator (zchar); extern void read_string (int, zchar *); extern int completion (const zchar *, zchar *); /* * unix_set_global_timeout * * This sets up a time structure to determine when unix_read_char should * return zero (representing input timeout). When current system time * equals global_timeout, boom. * */ static void unix_set_global_timeout(int timeout) { if (!timeout) global_timeout.tv_sec = 0; else { gettimeofday(&global_timeout, NULL); global_timeout.tv_sec += (timeout/10); global_timeout.tv_usec += ((timeout%10)*100000); if (global_timeout.tv_usec > 999999) { global_timeout.tv_sec++; global_timeout.tv_usec -= 1000000; } } } /* This returns the number of milliseconds until the input timeout * elapses or zero if it has already elapsed. -1 is returned if no * timeout is in effect, otherwise the return value is non-negative. */ static int timeout_to_ms() { struct timeval now, diff; if (global_timeout.tv_sec == 0) return -1; gettimeofday( &now, NULL); diff.tv_usec = global_timeout.tv_usec - now.tv_usec; if (diff.tv_usec < 0) { /* Carry */ now.tv_sec++; diff.tv_usec += 1000000; } diff.tv_sec = global_timeout.tv_sec - now.tv_sec; if (diff.tv_sec < 0) return 0; if (diff.tv_sec >= INT_MAX / 1000 - 1) /* Paranoia... */ return INT_MAX - 1000; return diff.tv_sec * 1000 + diff.tv_usec / 1000; } /* * unix_read_char * * This uses the curses getch() routine to get the next character * typed, and returns it. It returns values which the standard * considers to be legal input, and also returns editing and frotz hot * keys. If called with extkeys set it will also return line-editing * keys like INSERT etc. * * If unix_set_global_timeout has been used to set a global timeout * this routine may also return ZC_TIME_OUT if input times out. */ static int unix_read_char(int extkeys) { int c; while(1) { timeout( timeout_to_ms()); c = getch(); /* Catch 98% of all input right here... */ if ((c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX) || (!u_setup.plain_ascii && c >= ZC_LATIN1_MIN && c <= ZC_LATIN1_MAX)) return c; /* ...and the other 2% makes up 98% of the code. :( */ /* On many terminals the backspace key returns DEL. */ if (c == erasechar()) return ZC_BACKSPACE;; if (c == killchar()) return ZC_ESCAPE; switch(c) { /* Normally ERR means timeout. I suppose we might also get ERR if a signal hits getch. */ case ERR: if (timeout_to_ms() == 0) return ZC_TIME_OUT; else continue; /* * Under ncurses, getch() will return OK (defined to 0) when Ctrl-@ or * Ctrl-Space is pressed. 0 is also the ZSCII character code for * ZC_TIME_OUT. This causes a fatal error "Call to non-routine", after * which Frotz aborts. This doesn't happen with all games nor is the * crashing consistent. Sometimes repeated tests on a single game will * yield some crashes and some non-crashes. When linked with ncurses, * we must make sure that unix_read_char() does not return a bogus * ZC_TIME_OUT. * */ #ifdef USE_NCURSES_H case 0: continue; #endif /* USE_NCURSES_H */ /* Screen decluttering. */ case MOD_CTRL ^ 'L': case MOD_CTRL ^ 'R': clearok( curscr, 1); refresh(); clearok( curscr, 0); continue; /* Lucian P. Smith reports KEY_ENTER on Irix 5.3. LF has never been reported, but I'm leaving it in just in case. */ case '\n': case '\r': case KEY_ENTER: return ZC_RETURN; /* I've seen KEY_BACKSPACE returned on some terminals. */ case KEY_BACKSPACE: case '\b': return ZC_BACKSPACE; /* On terminals with 8-bit character sets or 7-bit connections "Alt-Foo" may be returned as an escape followed by the ASCII value of the letter. We have to decide here whether to return a single escape or a frotz hot key. */ case ZC_ESCAPE: nodelay(stdscr, TRUE); c = getch(); nodelay(stdscr, FALSE); switch(c) { case ERR: return ZC_ESCAPE; case 'p': return ZC_HKEY_PLAYBACK; case 'r': return ZC_HKEY_RECORD; case 's': return ZC_HKEY_SEED; case 'u': return ZC_HKEY_UNDO; case 'n': return ZC_HKEY_RESTART; case 'x': return ZC_HKEY_QUIT; case 'd': return ZC_HKEY_DEBUG; case 'h': return ZC_HKEY_HELP; default: continue; /* Ignore unknown combinations. */ } /* The standard function key block. */ case KEY_UP: return ZC_ARROW_UP; case KEY_DOWN: return ZC_ARROW_DOWN; case KEY_LEFT: return ZC_ARROW_LEFT; case KEY_RIGHT: return ZC_ARROW_RIGHT; case KEY_F(1): return ZC_FKEY_MIN; case KEY_F(2): return ZC_FKEY_MIN + 1; case KEY_F(3): return ZC_FKEY_MIN + 2; case KEY_F(4): return ZC_FKEY_MIN + 3; case KEY_F(5): return ZC_FKEY_MIN + 4; case KEY_F(6): return ZC_FKEY_MIN + 5; case KEY_F(7): return ZC_FKEY_MIN + 6; case KEY_F(8): return ZC_FKEY_MIN + 7; case KEY_F(9): return ZC_FKEY_MIN + 8; case KEY_F(10): return ZC_FKEY_MIN + 9; case KEY_F(11): return ZC_FKEY_MIN + 10; case KEY_F(12): return ZC_FKEY_MIN + 11; /* Curses can't differentiate keypad numbers from cursor keys, which is annoying, as cursor and keypad keys have nothing to do with each other on, say, a vt200. So we can only read 1, 3, 5, 7 and 9 from the keypad. This would be so silly that we choose not to provide keypad keys at all. */ /* Catch the meta key on those plain old ASCII terminals where it sets the high bit. This only works in u_setup.plain_ascii mode: otherwise these character codes would have been interpreted according to ISO-Latin-1 earlier. */ case MOD_META | 'p': return ZC_HKEY_PLAYBACK; case MOD_META | 'r': return ZC_HKEY_RECORD; case MOD_META | 's': return ZC_HKEY_SEED; case MOD_META | 'u': return ZC_HKEY_UNDO; case MOD_META | 'n': return ZC_HKEY_RESTART; case MOD_META | 'x': return ZC_HKEY_QUIT; case MOD_META | 'd': return ZC_HKEY_DEBUG; case MOD_META | 'h': return ZC_HKEY_HELP; /* these are the emacs-editing characters */ case MOD_CTRL ^ 'B': return ZC_ARROW_LEFT; case MOD_CTRL ^ 'F': return ZC_ARROW_RIGHT; case MOD_CTRL ^ 'P': return ZC_ARROW_UP; case MOD_CTRL ^ 'N': return ZC_ARROW_DOWN; case MOD_CTRL ^ 'A': c = KEY_HOME; break; case MOD_CTRL ^ 'E': c = KEY_END; break; case MOD_CTRL ^ 'D': c = KEY_DC; break; case MOD_CTRL ^ 'K': c = KEY_EOL; break; default: break; /* Who knows? */ } /* Control-N through Control-U happen to map to the frotz hot * key codes, but not in any mnemonic manner. It annoys an * emacs user (or this one anyway) when he tries out of habit * to use one of the emacs keys that isn't implemented and he * gets a random hot key function. It's less jarring to catch * them and do nothing. [APP] */ if ((c >= ZC_HKEY_MIN) && (c <= ZC_HKEY_MAX)) continue; /* Finally, if we're in full line mode (os_read_line), we might return codes which aren't legal Z-machine keys but are used by the editor. */ if (extkeys) return c; } } /* * unix_add_to_history * * Add the given string to the next available history buffer slot. * */ static void unix_add_to_history(zchar *str) { if (*history_next != NULL) free( *history_next); *history_next = (char *)malloc(strlen((char *)str) + 1); strcpy( *history_next, (char *)str); RING_INC( history_next, history_buffer, history_end); history_view = history_next; /* Reset user frame after each line */ } /* * unix_history_back * * Copy last available string to str, if possible. Return 1 if successful. * Only lines of at most maxlen characters will be considered. In addition * the first searchlen characters of the history entry must match those of str. */ static int unix_history_back(zchar *str, int searchlen, int maxlen) { char **prev = history_view; do { RING_DEC( history_view, history_buffer, history_end); if ((history_view == history_next) || (*history_view == NULL)) { os_beep(BEEP_HIGH); history_view = prev; return 0; } } while (strlen( *history_view) > maxlen || (searchlen != 0 && strncmp( (char *)str, *history_view, searchlen))); strcpy((char *)str + searchlen, *history_view + searchlen); return 1; } /* * unix_history_forward * * Opposite of unix_history_back, and works in the same way. */ static int unix_history_forward(zchar *str, int searchlen, int maxlen) { char **prev = history_view; do { RING_INC( history_view, history_buffer, history_end); if ((history_view == history_next) || (*history_view == NULL)) { os_beep(BEEP_HIGH); history_view = prev; return 0; } } while (strlen( *history_view) > maxlen || (searchlen != 0 && strncmp( (char *)str, *history_view, searchlen))); strcpy((char *)str + searchlen, *history_view + searchlen); return 1; } /* * scrnmove * * In the row of the cursor, move n characters starting at src to dest. * */ static void scrnmove(int dest, int src, int n) { int col, x, y; getyx(stdscr, y, x); if (src > dest) { for (col = src; col < src + n; col++) { chtype ch = mvinch(y, col); mvaddch(y, col - src + dest, ch); } } else if (src < dest) { for (col = src + n - 1; col >= src; col--) { chtype ch = mvinch(y, col); mvaddch(y, col - src + dest, ch); } } move(y, x); } /* * scrnset * * In the row of the cursor, set n characters starting at start to c. * */ static void scrnset(int start, int c, int n) { int y, x; getyx(stdscr, y, x); while (n--) mvaddch(y, start + n, c); move(y, x); } /* * os_read_line * * Read a line of input from the keyboard into a buffer. The buffer * may already be primed with some text. In this case, the "initial" * text is already displayed on the screen. After the input action * is complete, the function returns with the terminating key value. * The length of the input should not exceed "max" characters plus * an extra 0 terminator. * * Terminating keys are the return key (13) and all function keys * (see the Specification of the Z-machine) which are accepted by * the is_terminator function. Mouse clicks behave like function * keys except that the mouse position is stored in global variables * "mouse_x" and "mouse_y" (top left coordinates are (1,1)). * * Furthermore, Frotz introduces some special terminating keys: * * ZC_HKEY_KEY_PLAYBACK (Alt-P) * ZC_HKEY_RECORD (Alt-R) * ZC_HKEY_SEED (Alt-S) * ZC_HKEY_UNDO (Alt-U) * ZC_HKEY_RESTART (Alt-N, "new game") * ZC_HKEY_QUIT (Alt-X, "exit game") * ZC_HKEY_DEBUGGING (Alt-D) * ZC_HKEY_HELP (Alt-H) * * If the timeout argument is not zero, the input gets interrupted * after timeout/10 seconds (and the return value is ZC_TIME_OUT). * * The complete input line including the cursor must fit in "width" * screen units. * * The function may be called once again to continue after timeouts, * misplaced mouse clicks or hot keys. In this case the "continued" * flag will be set. This information can be useful if the interface * implements input line history. * * The screen is not scrolled after the return key was pressed. The * cursor is at the end of the input line when the function returns. * * Since Inform 2.2 the helper function "completion" can be called * to implement word completion (similar to tcsh under Unix). * */ zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) { int ch, y, x, len = strlen( (char *)buf); /* These are static to allow input continuation to work smoothly. */ static int scrpos = 0, searchpos = -1, insert_flag = 1; /* Set x and y to be at the start of the input area. */ getyx(stdscr, y, x); x -= len; if (width < max) max = width; /* Better be careful here or it might segv. I wonder if we should just ignore 'continued' and check for len > 0 instead? Might work better with Beyond Zork. */ if (!(continued && scrpos <= len && searchpos <= len)) { scrpos = len; history_view = history_next; /* Reset user's history view. */ searchpos = -1; /* -1 means initialize from len. */ insert_flag = 1; /* Insert mode is now default. */ } unix_set_global_timeout(timeout); for (;;) { move(y, x + scrpos); /* Maybe there's a cleaner way to do this, but refresh() is */ /* still needed here to print spaces. --DG */ refresh(); switch (ch = unix_read_char(1)) { case ZC_BACKSPACE: /* Delete preceeding character */ if (scrpos != 0) { len--; scrpos--; searchpos = -1; scrnmove(x + scrpos, x + scrpos + 1, len - scrpos); mvaddch(y, x + len, ' '); memmove(buf + scrpos, buf + scrpos + 1, len - scrpos); } break; case CHR_DEL: case KEY_DC: /* Delete following character */ if (scrpos < len) { len--; searchpos = -1; scrnmove(x + scrpos, x + scrpos + 1, len - scrpos); mvaddch(y, x + len, ' '); memmove(buf + scrpos, buf + scrpos + 1, len - scrpos); } continue; /* Don't feed is_terminator bad zchars. */ case KEY_EOL: /* Delete from cursor to end of line. */ scrnset(x + scrpos, ' ', len - scrpos); len = scrpos; continue; case ZC_ESCAPE: /* Delete whole line */ scrnset(x, ' ', len); len = scrpos = 0; searchpos = -1; history_view = history_next; continue; /* Cursor motion */ case ZC_ARROW_LEFT: if (scrpos) scrpos--; continue; case ZC_ARROW_RIGHT: if (scrpos < len) scrpos++; continue; case KEY_HOME: scrpos = 0; continue; case KEY_END: scrpos = len; continue; case KEY_IC: /* Insert Character */ insert_flag = !insert_flag; continue; case ZC_ARROW_UP: case ZC_ARROW_DOWN: if (searchpos < 0) searchpos = len; if ((ch == ZC_ARROW_UP ? unix_history_back : unix_history_forward) (buf, searchpos, max)) { scrnset(x, ' ', len); mvaddstr(y, x, (char *) buf); scrpos = len = strlen((char *) buf); } continue; /* Passthrough as up/down arrows for Beyond Zork. */ case KEY_PPAGE: ch = ZC_ARROW_UP; break; case KEY_NPAGE: ch = ZC_ARROW_DOWN; break; case '\t': /* This really should be fixed to work also in the middle of a sentence. */ { int status; zchar extension[10], saved_char; saved_char = buf[scrpos]; buf[scrpos] = '\0'; status = completion( buf, extension); buf[scrpos] = saved_char; if (status != 2) { int ext_len = strlen((char *) extension); if (ext_len > max - len) { ext_len = max - len; status = 1; } memmove(buf + scrpos + ext_len, buf + scrpos, len - scrpos); memmove(buf + scrpos, extension, ext_len); scrnmove(x + scrpos + ext_len, x + scrpos, len - scrpos); mvaddnstr(y, x + scrpos, (char *) extension, ext_len); scrpos += ext_len; len += ext_len; searchpos = -1; } if (status) os_beep(BEEP_HIGH); } continue; /* TAB is invalid as an input character. */ default: /* ASCII or ISO-Latin-1 */ if ((ch >= ZC_ASCII_MIN && ch <= ZC_ASCII_MAX) || (!u_setup.plain_ascii && ch >= ZC_LATIN1_MIN && ch <= ZC_LATIN1_MAX)) { searchpos = -1; if ((scrpos == max) || (insert_flag && (len == max))) { os_beep(BEEP_HIGH); continue; } if (insert_flag && (scrpos < len)) { /* move what's there to the right */ scrnmove(x + scrpos + 1, x + scrpos, len - scrpos); memmove(buf + scrpos + 1, buf + scrpos, len - scrpos); } if (insert_flag || scrpos == len) len++; mvaddch(y, x + scrpos, ch); buf[scrpos++] = ch; continue; } } if (is_terminator(ch)) { buf[len] = '\0'; if (ch == ZC_RETURN) unix_add_to_history(buf); /* Games don't know about line editing and might get confused if the cursor is not at the end of the input line. */ move(y, x + len); return ch; } } }/* os_read_line */ /* * os_read_key * * Read a single character from the keyboard (or a mouse click) and * return it. Input aborts after timeout/10 seconds. * */ zchar os_read_key (int timeout, int cursor) { zchar c; refresh(); if (!cursor) curs_set(0); unix_set_global_timeout(timeout); c = (zchar) unix_read_char(0); if (!cursor) curs_set(1); return c; }/* os_read_key */ /* * os_read_file_name * * Return the name of a file. Flag can be one of: * * FILE_SAVE - Save game file * FILE_RESTORE - Restore game file * FILE_SCRIPT - Transcript file * FILE_RECORD - Command file for recording * FILE_PLAYBACK - Command file for playback * FILE_SAVE_AUX - Save auxilary ("preferred settings") file * FILE_LOAD_AUX - Load auxilary ("preferred settings") file * * The length of the file name is limited by MAX_FILE_NAME. Ideally * an interpreter should open a file requester to ask for the file * name. If it is unable to do that then this function should call * print_string and read_string to ask for a file name. * */ int os_read_file_name (char *file_name, const char *default_name, int flag) { int saved_replay = istream_replay; int saved_record = ostream_record; /* Turn off playback and recording temporarily */ istream_replay = 0; ostream_record = 0; print_string ("Enter a file name.\nDefault is \""); print_string (default_name); print_string ("\": "); read_string (FILENAME_MAX, (zchar *)file_name); /* Use the default name if nothing was typed */ if (file_name[0] == 0) strcpy (file_name, default_name); /* Restore state of playback and recording */ istream_replay = saved_replay; ostream_record = saved_record; return 1; } /* os_read_file_name */ /* * os_read_mouse * * Store the mouse position in the global variables "mouse_x" and * "mouse_y" and return the mouse buttons currently pressed. * */ zword os_read_mouse (void) { /* INCOMPLETE */ } /* os_read_mouse */ /* What's this? */ /* * Local Variables: * c-basic-offset: 4 * End: */ #ifdef NO_MEMMOVE /* * This is for operating systems based on 4.2BSD or older or SYSVR3 or * older. Since they lack the memmove(3) system call, it is provided * here. Because I don't have a machine like this to play with, this code * is untested. If you happen to have a spare SunOS 4.1.x install CD * lying around, please consider sending it my way. Dave. * */ void *memmove(void *s, void *t, size_t n) { char *p = s; char *q = t; if (p < q) { while (n--) *p++ = *q++; } else { p += n; q += n; while (n--) *--p = *--q; } } #endif /* NO_MEMMOVE */ frotz-2.44/src/common/0000755000175000017500000000000012527045477011704 500000000000000frotz-2.44/src/common/process.c0000644000175000017500000003215512527045477013454 00000000000000/* process.c - Interpreter loop and program control * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" #ifdef DJGPP #include "djfrotz.h" #endif zword zargs[8]; int zargc; static int finished = 0; static void __extended__ (void); static void __illegal__ (void); void (*op0_opcodes[0x10]) (void) = { z_rtrue, z_rfalse, z_print, z_print_ret, z_nop, z_save, z_restore, z_restart, z_ret_popped, z_catch, z_quit, z_new_line, z_show_status, z_verify, __extended__, z_piracy }; void (*op1_opcodes[0x10]) (void) = { z_jz, z_get_sibling, z_get_child, z_get_parent, z_get_prop_len, z_inc, z_dec, z_print_addr, z_call_s, z_remove_obj, z_print_obj, z_ret, z_jump, z_print_paddr, z_load, z_call_n }; void (*var_opcodes[0x40]) (void) = { __illegal__, z_je, z_jl, z_jg, z_dec_chk, z_inc_chk, z_jin, z_test, z_or, z_and, z_test_attr, z_set_attr, z_clear_attr, z_store, z_insert_obj, z_loadw, z_loadb, z_get_prop, z_get_prop_addr, z_get_next_prop, z_add, z_sub, z_mul, z_div, z_mod, z_call_s, z_call_n, z_set_colour, z_throw, __illegal__, __illegal__, __illegal__, z_call_s, z_storew, z_storeb, z_put_prop, z_read, z_print_char, z_print_num, z_random, z_push, z_pull, z_split_window, z_set_window, z_call_s, z_erase_window, z_erase_line, z_set_cursor, z_get_cursor, z_set_text_style, z_buffer_mode, z_output_stream, z_input_stream, z_sound_effect, z_read_char, z_scan_table, z_not, z_call_n, z_call_n, z_tokenise, z_encode_text, z_copy_table, z_print_table, z_check_arg_count }; void (*ext_opcodes[0x1d]) (void) = { z_save, z_restore, z_log_shift, z_art_shift, z_set_font, z_draw_picture, z_picture_data, z_erase_picture, z_set_margins, z_save_undo, z_restore_undo, z_print_unicode, z_check_unicode, __illegal__, __illegal__, __illegal__, z_move_window, z_window_size, z_window_style, z_get_wind_prop, z_scroll_window, z_pop_stack, z_read_mouse, z_mouse_window, z_push_stack, z_put_wind_prop, z_print_form, z_make_menu, z_picture_table }; /* * init_process * * Initialize process variables. * */ void init_process (void) { finished = 0; } /* init_process */ /* * load_operand * * Load an operand, either a variable or a constant. * */ static void load_operand (zbyte type) { zword value; if (type & 2) { /* variable */ zbyte variable; CODE_BYTE (variable) if (variable == 0) value = *sp++; else if (variable < 16) value = *(fp - variable); else { zword addr = h_globals + 2 * (variable - 16); LOW_WORD (addr, value) } } else if (type & 1) { /* small constant */ zbyte bvalue; CODE_BYTE (bvalue) value = bvalue; } else CODE_WORD (value) /* large constant */ zargs[zargc++] = value; }/* load_operand */ /* * load_all_operands * * Given the operand specifier byte, load all (up to four) operands * for a VAR or EXT opcode. * */ static void load_all_operands (zbyte specifier) { int i; for (i = 6; i >= 0; i -= 2) { zbyte type = (specifier >> i) & 0x03; if (type == 3) break; load_operand (type); } }/* load_all_operands */ /* * interpret * * Z-code interpreter main loop * */ void interpret (void) { do { zbyte opcode; CODE_BYTE (opcode) zargc = 0; if (opcode < 0x80) { /* 2OP opcodes */ load_operand ((zbyte) (opcode & 0x40) ? 2 : 1); load_operand ((zbyte) (opcode & 0x20) ? 2 : 1); var_opcodes[opcode & 0x1f] (); } else if (opcode < 0xb0) { /* 1OP opcodes */ load_operand ((zbyte) (opcode >> 4)); op1_opcodes[opcode & 0x0f] (); } else if (opcode < 0xc0) { /* 0OP opcodes */ op0_opcodes[opcode - 0xb0] (); } else { /* VAR opcodes */ zbyte specifier1; zbyte specifier2; if (opcode == 0xec || opcode == 0xfa) { /* opcodes 0xec */ CODE_BYTE (specifier1) /* and 0xfa are */ CODE_BYTE (specifier2) /* call opcodes */ load_all_operands (specifier1); /* with up to 8 */ load_all_operands (specifier2); /* arguments */ } else { CODE_BYTE (specifier1) load_all_operands (specifier1); } var_opcodes[opcode - 0xc0] (); } #if defined(DJGPP) && defined(SOUND_SUPPORT) if (end_of_sound_flag) end_of_sound (); #endif } while (finished == 0); finished--; }/* interpret */ /* * call * * Call a subroutine. Save PC and FP then load new PC and initialise * new stack frame. Note that the caller may legally provide less or * more arguments than the function actually has. The call type "ct" * can be 0 (z_call_s), 1 (z_call_n) or 2 (direct call). * */ void call (zword routine, int argc, zword *args, int ct) { long pc; zword value; zbyte count; int i; if (sp - stack < 4) runtime_error (ERR_STK_OVF); GET_PC (pc) *--sp = (zword) (pc >> 9); *--sp = (zword) (pc & 0x1ff); *--sp = (zword) (fp - stack - 1); *--sp = (zword) (argc | (ct << 12)); fp = sp; frame_count++; /* Calculate byte address of routine */ if (h_version <= V3) pc = (long) routine << 1; else if (h_version <= V5) pc = (long) routine << 2; else if (h_version <= V7) pc = ((long) routine << 2) + ((long) h_functions_offset << 3); else /* h_version == V8 */ pc = (long) routine << 3; if (pc >= story_size) runtime_error (ERR_ILL_CALL_ADDR); SET_PC (pc) /* Initialise local variables */ CODE_BYTE (count) if (count > 15) runtime_error (ERR_CALL_NON_RTN); if (sp - stack < count) runtime_error (ERR_STK_OVF); fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ value = 0; for (i = 0; i < count; i++) { if (h_version <= V4) /* V1 to V4 games provide default */ CODE_WORD (value) /* values for all local variables */ *--sp = (zword) ((argc-- > 0) ? args[i] : value); } /* Start main loop for direct calls */ if (ct == 2) interpret (); }/* call */ /* * ret * * Return from the current subroutine and restore the previous stack * frame. The result may be stored (0), thrown away (1) or pushed on * the stack (2). In the latter case a direct call has been finished * and we must exit the interpreter loop. * */ void ret (zword value) { long pc; int ct; if (sp > fp) runtime_error (ERR_STK_UNDF); sp = fp; ct = *sp++ >> 12; frame_count--; fp = stack + 1 + *sp++; pc = *sp++; pc = ((long) *sp++ << 9) | pc; SET_PC (pc) /* Handle resulting value */ if (ct == 0) store (value); if (ct == 2) *--sp = value; /* Stop main loop for direct calls */ if (ct == 2) finished++; }/* ret */ /* * branch * * Take a jump after an instruction based on the flag, either true or * false. The branch can be short or long; it is encoded in one or two * bytes respectively. When bit 7 of the first byte is set, the jump * takes place if the flag is true; otherwise it is taken if the flag * is false. When bit 6 of the first byte is set, the branch is short; * otherwise it is long. The offset occupies the bottom 6 bits of the * first byte plus all the bits in the second byte for long branches. * Uniquely, an offset of 0 means return false, and an offset of 1 is * return true. * */ void branch (bool flag) { long pc; zword offset; zbyte specifier; zbyte off1; zbyte off2; CODE_BYTE (specifier) off1 = specifier & 0x3f; if (!flag) specifier ^= 0x80; if (!(specifier & 0x40)) { /* it's a long branch */ if (off1 & 0x20) /* propagate sign bit */ off1 |= 0xc0; CODE_BYTE (off2) offset = (off1 << 8) | off2; } else offset = off1; /* it's a short branch */ if (specifier & 0x80) { if (offset > 1) { /* normal branch */ GET_PC (pc) pc += (short) offset - 2; SET_PC (pc) } else ret (offset); /* special case, return 0 or 1 */ } }/* branch */ /* * store * * Store an operand, either as a variable or pushed on the stack. * */ void store (zword value) { zbyte variable; CODE_BYTE (variable) if (variable == 0) *--sp = value; else if (variable < 16) *(fp - variable) = value; else { zword addr = h_globals + 2 * (variable - 16); SET_WORD (addr, value) } }/* store */ /* * direct_call * * Call the interpreter loop directly. This is necessary when * * - a sound effect has been finished * - a read instruction has timed out * - a newline countdown has hit zero * * The interpreter returns the result value on the stack. * */ int direct_call (zword addr) { zword saved_zargs[8]; int saved_zargc; int i; /* Calls to address 0 return false */ if (addr == 0) return 0; /* Save operands and operand count */ for (i = 0; i < 8; i++) saved_zargs[i] = zargs[i]; saved_zargc = zargc; /* Call routine directly */ call (addr, 0, 0, 2); /* Restore operands and operand count */ for (i = 0; i < 8; i++) zargs[i] = saved_zargs[i]; zargc = saved_zargc; /* Resulting value lies on top of the stack */ return (short) *sp++; }/* direct_call */ /* * __extended__ * * Load and execute an extended opcode. * */ static void __extended__ (void) { zbyte opcode; zbyte specifier; CODE_BYTE (opcode) CODE_BYTE (specifier) load_all_operands (specifier); if (opcode < 0x1d) /* extended opcodes from 0x1d on */ ext_opcodes[opcode] (); /* are reserved for future spec' */ }/* __extended__ */ /* * __illegal__ * * Exit game because an unknown opcode has been hit. * */ static void __illegal__ (void) { runtime_error (ERR_ILL_OPCODE); }/* __illegal__ */ /* * z_catch, store the current stack frame for later use with z_throw. * * no zargs used * */ void z_catch (void) { store (frame_count); }/* z_catch */ /* * z_throw, go back to the given stack frame and return the given value. * * zargs[0] = value to return * zargs[1] = stack frame * */ void z_throw (void) { if (zargs[1] > frame_count) runtime_error (ERR_BAD_FRAME); /* Unwind the stack a frame at a time. */ for (; frame_count > zargs[1]; --frame_count) fp = stack + 1 + fp[1]; ret (zargs[0]); }/* z_throw */ /* * z_call_n, call a subroutine and discard its result. * * zargs[0] = packed address of subroutine * zargs[1] = first argument (optional) * ... * zargs[7] = seventh argument (optional) * */ void z_call_n (void) { if (zargs[0] != 0) call (zargs[0], zargc - 1, zargs + 1, 1); }/* z_call_n */ /* * z_call_s, call a subroutine and store its result. * * zargs[0] = packed address of subroutine * zargs[1] = first argument (optional) * ... * zargs[7] = seventh argument (optional) * */ void z_call_s (void) { if (zargs[0] != 0) call (zargs[0], zargc - 1, zargs + 1, 0); else store (0); }/* z_call_s */ /* * z_check_arg_count, branch if subroutine was called with >= n arg's. * * zargs[0] = number of arguments * */ void z_check_arg_count (void) { if (fp == stack + STACK_SIZE) branch (zargs[0] == 0); else branch (zargs[0] <= (*fp & 0xff)); }/* z_check_arg_count */ /* * z_jump, jump unconditionally to the given address. * * zargs[0] = PC relative address * */ void z_jump (void) { long pc; GET_PC (pc) pc += (short) zargs[0] - 2; if (pc >= story_size) runtime_error (ERR_ILL_JUMP_ADDR); SET_PC (pc) }/* z_jump */ /* * z_nop, no operation. * * no zargs used * */ void z_nop (void) { /* Do nothing */ }/* z_nop */ /* * z_quit, stop game and exit interpreter. * * no zargs used * */ void z_quit (void) { finished = 9999; }/* z_quit */ /* * z_ret, return from a subroutine with the given value. * * zargs[0] = value to return * */ void z_ret (void) { ret (zargs[0]); }/* z_ret */ /* * z_ret_popped, return from a subroutine with a value popped off the stack. * * no zargs used * */ void z_ret_popped (void) { ret (*sp++); }/* z_ret_popped */ /* * z_rfalse, return from a subroutine with false (0). * * no zargs used * */ void z_rfalse (void) { ret (0); }/* z_rfalse */ /* * z_rtrue, return from a subroutine with true (1). * * no zargs used * */ void z_rtrue (void) { ret (1); }/* z_rtrue */ frotz-2.44/src/common/getopt.c0000644000175000017500000000212212527042452013255 00000000000000/* * getopt.c * * Replacement for a Unix style getopt function * * Quick, clean, and portable to funky systems that don't have getopt() * for whatever reason. * */ #include #include #ifndef MSDOS_16BIT #define cdecl #endif int optind = 1; int optopt = 0; const char *optarg = NULL; int cdecl getopt (int argc, char *argv[], const char *options) { static int pos = 1; const char *p; if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == 0) return EOF; optopt = argv[optind][pos++]; optarg = NULL; if (argv[optind][pos] == 0) { pos = 1; optind++; } p = strchr (options, optopt); if (optopt == ':' || p == NULL) { fputs ("illegal option -- ", stdout); goto error; } else if (p[1] == ':') { if (optind >= argc) { fputs ("option requires an argument -- ", stdout); goto error; } else { optarg = argv[optind]; if (pos != 1) optarg += pos; pos = 1; optind++; } } return optopt; error: fputc (optopt, stdout); fputc ('\n', stdout); return '?'; }/* getopt */ frotz-2.44/src/common/math.c0000644000175000017500000001022512527045477012721 00000000000000/* math.c - Arithmetic, compare and logical opcodes * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" /* * z_add, 16bit addition. * * zargs[0] = first value * zargs[1] = second value * */ void z_add (void) { store ((zword) ((short) zargs[0] + (short) zargs[1])); }/* z_add */ /* * z_and, bitwise AND operation. * * zargs[0] = first value * zargs[1] = second value * */ void z_and (void) { store ((zword) (zargs[0] & zargs[1])); }/* z_and */ /* * z_art_shift, arithmetic SHIFT operation. * * zargs[0] = value * zargs[1] = #positions to shift left (positive) or right * */ void z_art_shift (void) { if ((short) zargs[1] > 0) store ((zword) ((short) zargs[0] << (short) zargs[1])); else store ((zword) ((short) zargs[0] >> - (short) zargs[1])); }/* z_art_shift */ /* * z_div, signed 16bit division. * * zargs[0] = first value * zargs[1] = second value * */ void z_div (void) { if (zargs[1] == 0) runtime_error (ERR_DIV_ZERO); store ((zword) ((short) zargs[0] / (short) zargs[1])); }/* z_div */ /* * z_je, branch if the first value equals any of the following. * * zargs[0] = first value * zargs[1] = second value (optional) * ... * zargs[3] = fourth value (optional) * */ void z_je (void) { branch ( zargc > 1 && (zargs[0] == zargs[1] || ( zargc > 2 && (zargs[0] == zargs[2] || ( zargc > 3 && (zargs[0] == zargs[3])))))); }/* z_je */ /* * z_jg, branch if the first value is greater than the second. * * zargs[0] = first value * zargs[1] = second value * */ void z_jg (void) { branch ((short) zargs[0] > (short) zargs[1]); }/* z_jg */ /* * z_jl, branch if the first value is less than the second. * * zargs[0] = first value * zargs[1] = second value * */ void z_jl (void) { branch ((short) zargs[0] < (short) zargs[1]); }/* z_jl */ /* * z_jz, branch if value is zero. * * zargs[0] = value * */ void z_jz (void) { branch ((short) zargs[0] == 0); }/* z_jz */ /* * z_log_shift, logical SHIFT operation. * * zargs[0] = value * zargs[1] = #positions to shift left (positive) or right (negative) * */ void z_log_shift (void) { if ((short) zargs[1] > 0) store ((zword) (zargs[0] << (short) zargs[1])); else store ((zword) (zargs[0] >> - (short) zargs[1])); }/* z_log_shift */ /* * z_mod, remainder after signed 16bit division. * * zargs[0] = first value * zargs[1] = second value * */ void z_mod (void) { if (zargs[1] == 0) runtime_error (ERR_DIV_ZERO); store ((zword) ((short) zargs[0] % (short) zargs[1])); }/* z_mod */ /* * z_mul, 16bit multiplication. * * zargs[0] = first value * zargs[1] = second value * */ void z_mul (void) { store ((zword) ((short) zargs[0] * (short) zargs[1])); }/* z_mul */ /* * z_not, bitwise NOT operation. * * zargs[0] = value * */ void z_not (void) { store ((zword) ~zargs[0]); }/* z_not */ /* * z_or, bitwise OR operation. * * zargs[0] = first value * zargs[1] = second value * */ void z_or (void) { store ((zword) (zargs[0] | zargs[1])); }/* z_or */ /* * z_sub, 16bit substraction. * * zargs[0] = first value * zargs[1] = second value * */ void z_sub (void) { store ((zword) ((short) zargs[0] - (short) zargs[1])); }/* z_sub */ /* * z_test, branch if all the flags of a bit mask are set in a value. * * zargs[0] = value to be examined * zargs[1] = bit mask * */ void z_test (void) { branch ((zargs[0] & zargs[1]) == zargs[1]); }/* z_test */ frotz-2.44/src/common/frotz.h0000644000175000017500000004655312527045477013156 00000000000000/* * frotz.h * * Global declarations and definitions * */ /* Unfortunately, frotz's bool definition conflicts with that of curses. But since no os_* function uses it, it's safe to let the frotz core see this definition, but have the unix port see the curses version. */ #ifndef VERSION #define VERSION "2.44" #endif #ifndef __UNIX_PORT_FILE #include typedef int bool; #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #endif /* __UNIX_PORT_FILE */ #include /* typedef unsigned short zbyte; */ typedef unsigned char zbyte; typedef unsigned short zword; /* This business turns out to interfere with the command history * functions in the curses interface code. Unicode support for DOS * Frotz probably won't happen. I'd like to get it working for Unix * Frotz eventually. The patch I was using was rather old and bugs * might have crept in. If you know how to implement Unicode for the * Unix side, please let me know and please test it to make sure it * doesn't mess with command history. */ /* #ifdef MSDOS_16BIT typedef unsigned char zchar; #else typedef unsigned short zchar; #endif */ typedef unsigned char zchar; enum story { BEYOND_ZORK, SHERLOCK, ZORK_ZERO, SHOGUN, ARTHUR, JOURNEY, LURKING_HORROR, UNKNOWN }; /*** screen window ***/ typedef struct { zword y_pos; zword x_pos; zword y_size; zword x_size; zword y_cursor; zword x_cursor; zword left; zword right; zword nl_routine; zword nl_countdown; zword style; zword colour; zword font; zword font_size; zword attribute; zword line_count; zword true_fore; zword true_back; } Zwindow; #include "setup.h" /*** Constants that may be set at compile time ***/ #ifndef MAX_UNDO_SLOTS #define MAX_UNDO_SLOTS 500 #endif #ifndef MAX_FILE_NAME #define MAX_FILE_NAME 80 #endif #ifndef TEXT_BUFFER_SIZE #define TEXT_BUFFER_SIZE 200 #endif #ifndef INPUT_BUFFER_SIZE #define INPUT_BUFFER_SIZE 200 #endif #ifndef STACK_SIZE #define STACK_SIZE 1024 #endif /* Assorted filename extensions */ #define EXT_SAVE ".qzl" #define EXT_SCRIPT ".scr" #define EXT_BLORB ".blb" #define EXT_BLORB2 ".zblb" #define EXT_BLORB3 ".blorb" #define EXT_BLORB4 ".zblorb" #define EXT_COMMAND ".rec" #define EXT_AUX ".aux" #ifndef DEFAULT_SAVE_NAME #define DEFAULT_SAVE_NAME "story.sav" #endif #ifndef DEFAULT_SCRIPT_NAME #define DEFAULT_SCRIPT_NAME "story.scr" #endif #ifndef DEFAULT_COMMAND_NAME #define DEFAULT_COMMAND_NAME "story.rec" #endif #ifndef DEFAULT_AUXILARY_NAME #define DEFAULT_AUXILARY_NAME "story.aux" #endif #ifndef DEFAULT_SAVE_DIR /* DG */ #define DEFAULT_SAVE_DIR ".frotz-saves" #endif /*** Story file header format ***/ #define H_VERSION 0 #define H_CONFIG 1 #define H_RELEASE 2 #define H_RESIDENT_SIZE 4 #define H_START_PC 6 #define H_DICTIONARY 8 #define H_OBJECTS 10 #define H_GLOBALS 12 #define H_DYNAMIC_SIZE 14 #define H_FLAGS 16 #define H_SERIAL 18 #define H_ABBREVIATIONS 24 #define H_FILE_SIZE 26 #define H_CHECKSUM 28 #define H_INTERPRETER_NUMBER 30 #define H_INTERPRETER_VERSION 31 #define H_SCREEN_ROWS 32 #define H_SCREEN_COLS 33 #define H_SCREEN_WIDTH 34 #define H_SCREEN_HEIGHT 36 #define H_FONT_HEIGHT 38 /* this is the font width in V5 */ #define H_FONT_WIDTH 39 /* this is the font height in V5 */ #define H_FUNCTIONS_OFFSET 40 #define H_STRINGS_OFFSET 42 #define H_DEFAULT_BACKGROUND 44 #define H_DEFAULT_FOREGROUND 45 #define H_TERMINATING_KEYS 46 #define H_LINE_WIDTH 48 #define H_STANDARD_HIGH 50 #define H_STANDARD_LOW 51 #define H_ALPHABET 52 #define H_EXTENSION_TABLE 54 #define H_USER_NAME 56 #define HX_TABLE_SIZE 0 #define HX_MOUSE_X 1 #define HX_MOUSE_Y 2 #define HX_UNICODE_TABLE 3 /*** Various Z-machine constants ***/ #define V1 1 #define V2 2 #define V3 3 #define V4 4 #define V5 5 #define V6 6 #define V7 7 #define V8 8 #define CONFIG_BYTE_SWAPPED 0x01 /* Story file is byte swapped - V3 */ #define CONFIG_TIME 0x02 /* Status line displays time - V3 */ #define CONFIG_TWODISKS 0x04 /* Story file occupied two disks - V3 */ #define CONFIG_TANDY 0x08 /* Tandy licensed game - V3 */ #define CONFIG_NOSTATUSLINE 0x10 /* Interpr can't support status lines - V3 */ #define CONFIG_SPLITSCREEN 0x20 /* Interpr supports split screen mode - V3 */ #define CONFIG_PROPORTIONAL 0x40 /* Interpr uses proportional font - V3 */ #define CONFIG_COLOUR 0x01 /* Interpr supports colour - V5+ */ #define CONFIG_PICTURES 0x02 /* Interpr supports pictures - V6 */ #define CONFIG_BOLDFACE 0x04 /* Interpr supports boldface style - V4+ */ #define CONFIG_EMPHASIS 0x08 /* Interpr supports emphasis style - V4+ */ #define CONFIG_FIXED 0x10 /* Interpr supports fixed width style - V4+ */ #define CONFIG_SOUND 0x20 /* Interpr supports sound - V6 */ #define CONFIG_TIMEDINPUT 0x80 /* Interpr supports timed input - V4+ */ #define SCRIPTING_FLAG 0x0001 /* Outputting to transcription file - V1+ */ #define FIXED_FONT_FLAG 0x0002 /* Use fixed width font - V3+ */ #define REFRESH_FLAG 0x0004 /* Refresh the screen - V6 */ #define GRAPHICS_FLAG 0x0008 /* Game wants to use graphics - V5+ */ #define OLD_SOUND_FLAG 0x0010 /* Game wants to use sound effects - V3 */ #define UNDO_FLAG 0x0010 /* Game wants to use UNDO feature - V5+ */ #define MOUSE_FLAG 0x0020 /* Game wants to use a mouse - V5+ */ #define COLOUR_FLAG 0x0040 /* Game wants to use colours - V5+ */ #define SOUND_FLAG 0x0080 /* Game wants to use sound effects - V5+ */ #define MENU_FLAG 0x0100 /* Game wants to use menus - V6 */ #define TRANSPARENT_FLAG 0x0001 /* Game wants to use transparency - V6 */ #define INTERP_DEFAULT 0 #define INTERP_DEC_20 1 #define INTERP_APPLE_IIE 2 #define INTERP_MACINTOSH 3 #define INTERP_AMIGA 4 #define INTERP_ATARI_ST 5 #define INTERP_MSDOS 6 #define INTERP_CBM_128 7 #define INTERP_CBM_64 8 #define INTERP_APPLE_IIC 9 #define INTERP_APPLE_IIGS 10 #define INTERP_TANDY 11 #define DEFAULT_COLOUR 1 #define BLACK_COLOUR 2 #define RED_COLOUR 3 #define GREEN_COLOUR 4 #define YELLOW_COLOUR 5 #define BLUE_COLOUR 6 #define MAGENTA_COLOUR 7 #define CYAN_COLOUR 8 #define WHITE_COLOUR 9 #define GREY_COLOUR 10 /* INTERP_MSDOS only */ #define LIGHTGREY_COLOUR 10 /* INTERP_AMIGA only */ #define MEDIUMGREY_COLOUR 11 /* INTERP_AMIGA only */ #define DARKGREY_COLOUR 12 /* INTERP_AMIGA only */ #define REVERSE_STYLE 1 #define BOLDFACE_STYLE 2 #define EMPHASIS_STYLE 4 #define FIXED_WIDTH_STYLE 8 #define TEXT_FONT 1 #define PICTURE_FONT 2 #define GRAPHICS_FONT 3 #define FIXED_WIDTH_FONT 4 #define BEEP_HIGH 1 #define BEEP_LOW 2 /*** Constants for os_restart_game */ #define RESTART_BEGIN 0 #define RESTART_WPROP_SET 1 #define RESTART_END 2 /*** Character codes ***/ #define ZC_TIME_OUT 0x00 #define ZC_NEW_STYLE 0x01 #define ZC_NEW_FONT 0x02 #define ZC_BACKSPACE 0x08 #define ZC_INDENT 0x09 #define ZC_GAP 0x0b #define ZC_RETURN 0x0d #define ZC_HKEY_MIN 0x0e #define ZC_HKEY_RECORD 0x0e #define ZC_HKEY_PLAYBACK 0x0f #define ZC_HKEY_SEED 0x10 #define ZC_HKEY_UNDO 0x11 #define ZC_HKEY_RESTART 0x12 #define ZC_HKEY_QUIT 0x13 #define ZC_HKEY_DEBUG 0x14 #define ZC_HKEY_HELP 0x15 #define ZC_HKEY_MAX 0x15 #define ZC_ESCAPE 0x1b #define ZC_ASCII_MIN 0x20 #define ZC_ASCII_MAX 0x7e #define ZC_BAD 0x7f #define ZC_ARROW_MIN 0x81 #define ZC_ARROW_UP 0x81 #define ZC_ARROW_DOWN 0x82 #define ZC_ARROW_LEFT 0x83 #define ZC_ARROW_RIGHT 0x84 #define ZC_ARROW_MAX 0x84 #define ZC_FKEY_MIN 0x85 #define ZC_FKEY_MAX 0x90 #define ZC_NUMPAD_MIN 0x91 #define ZC_NUMPAD_MAX 0x9a #define ZC_SINGLE_CLICK 0x9b #define ZC_DOUBLE_CLICK 0x9c #define ZC_MENU_CLICK 0x9d #define ZC_LATIN1_MIN 0xa0 #define ZC_LATIN1_MAX 0xff /*** File types ***/ #define FILE_RESTORE 0 #define FILE_SAVE 1 #define FILE_SCRIPT 2 #define FILE_PLAYBACK 3 #define FILE_RECORD 4 #define FILE_LOAD_AUX 5 #define FILE_SAVE_AUX 6 /*** Data access macros ***/ #define SET_BYTE(addr,v) { zmp[addr] = v; } #define LOW_BYTE(addr,v) { v = zmp[addr]; } #define CODE_BYTE(v) { v = *pcp++; } #if defined (AMIGA) extern zbyte *pcp; extern zbyte *zmp; #define lo(v) ((zbyte *)&v)[1] #define hi(v) ((zbyte *)&v)[0] #define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); } #define LOW_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; } #define HIGH_WORD(addr,v) { hi(v) = zmp[addr]; lo(v) = zmp[addr+1]; } #define CODE_WORD(v) { hi(v) = *pcp++; lo(v) = *pcp++; } #define GET_PC(v) { v = pcp - zmp; } #define SET_PC(v) { pcp = zmp + v; } #endif #if defined (MSDOS_16BIT) extern zbyte *pcp; extern zbyte *zmp; #define lo(v) ((zbyte *)&v)[0] #define hi(v) ((zbyte *)&v)[1] #define SET_WORD(addr,v) asm {\ les bx,zmp;\ add bx,addr;\ mov ax,v;\ xchg al,ah;\ mov es:[bx],ax } #define LOW_WORD(addr,v) asm {\ les bx,zmp;\ add bx,addr;\ mov ax,es:[bx];\ xchg al,ah;\ mov v,ax } #define HIGH_WORD(addr,v) asm {\ mov bx,word ptr zmp;\ add bx,word ptr addr;\ mov al,bh;\ mov bh,0;\ mov ah,0;\ adc ah,byte ptr addr+2;\ mov cl,4;\ shl ax,cl;\ add ax,word ptr zmp+2;\ mov es,ax;\ mov ax,es:[bx];\ xchg al,ah;\ mov v,ax } #define CODE_WORD(v) asm {\ les bx,pcp;\ mov ax,es:[bx];\ xchg al,ah;\ mov v,ax;\ add word ptr pcp,2 } #define GET_PC(v) asm {\ mov bx,word ptr pcp+2;\ sub bx,word ptr zmp+2;\ mov ax,bx;\ mov cl,4;\ shl bx,cl;\ mov cl,12;\ shr ax,cl;\ add bx,word ptr pcp;\ adc al,0;\ sub bx,word ptr zmp;\ sbb al,0;\ mov word ptr v,bx;\ mov word ptr v+2,ax } #define SET_PC(v) asm {\ mov bx,word ptr zmp;\ add bx,word ptr v;\ mov al,bh;\ mov bh,0;\ mov ah,0;\ adc ah,byte ptr v+2;\ mov cl,4;\ shl ax,cl;\ add ax,word ptr zmp+2;\ mov word ptr pcp,bx;\ mov word ptr pcp+2,ax } #endif /* MSDOS_16BIT */ #if !defined (AMIGA) && !defined (MSDOS_16BIT) extern zbyte *pcp; extern zbyte *zmp; #define lo(v) (v & 0xff) #define hi(v) (v >> 8) #define SET_WORD(addr,v) { zmp[addr] = hi(v); zmp[addr+1] = lo(v); } #define LOW_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; } #define HIGH_WORD(addr,v) { v = ((zword) zmp[addr] << 8) | zmp[addr+1]; } #define CODE_WORD(v) { v = ((zword) pcp[0] << 8) | pcp[1]; pcp += 2; } #define GET_PC(v) { v = pcp - zmp; } #define SET_PC(v) { pcp = zmp + v; } #endif /*** Story file header data ***/ extern zbyte h_version; extern zbyte h_config; extern zword h_release; extern zword h_resident_size; extern zword h_start_pc; extern zword h_dictionary; extern zword h_objects; extern zword h_globals; extern zword h_dynamic_size; extern zword h_flags; extern zbyte h_serial[6]; extern zword h_abbreviations; extern zword h_file_size; extern zword h_checksum; extern zbyte h_interpreter_number; extern zbyte h_interpreter_version; extern zbyte h_screen_rows; extern zbyte h_screen_cols; extern zword h_screen_width; extern zword h_screen_height; extern zbyte h_font_height; extern zbyte h_font_width; extern zword h_functions_offset; extern zword h_strings_offset; extern zbyte h_default_background; extern zbyte h_default_foreground; extern zword h_terminating_keys; extern zword h_line_width; extern zbyte h_standard_high; extern zbyte h_standard_low; extern zword h_alphabet; extern zword h_extension_table; extern zbyte h_user_name[8]; extern zword hx_table_size; extern zword hx_mouse_x; extern zword hx_mouse_y; extern zword hx_unicode_table; extern zword hx_flags; extern zword hx_fore_colour; extern zword hx_back_colour; /*** Various data ***/ extern enum story story_id; extern long story_size; extern zword stack[STACK_SIZE]; extern zword *sp; extern zword *fp; extern zword frame_count; extern zword zargs[8]; extern int zargc; extern bool ostream_screen; extern bool ostream_script; extern bool ostream_memory; extern bool ostream_record; extern bool istream_replay; extern bool message; extern int cwin; extern int mwin; extern int mouse_x; extern int mouse_y; extern int menu_selected; extern int mouse_button; extern bool enable_wrapping; extern bool enable_scripting; extern bool enable_scrolling; extern bool enable_buffering; extern char *option_zcode_path; /* dg */ extern long reserve_mem; /*** Z-machine opcodes ***/ void z_add (void); void z_and (void); void z_art_shift (void); void z_buffer_mode (void); void z_call_n (void); void z_call_s (void); void z_catch (void); void z_check_arg_count (void); void z_check_unicode (void); void z_clear_attr (void); void z_copy_table (void); void z_dec (void); void z_dec_chk (void); void z_div (void); void z_draw_picture (void); void z_encode_text (void); void z_erase_line (void); void z_erase_picture (void); void z_erase_window (void); void z_get_child (void); void z_get_cursor (void); void z_get_next_prop (void); void z_get_parent (void); void z_get_prop (void); void z_get_prop_addr (void); void z_get_prop_len (void); void z_get_sibling (void); void z_get_wind_prop (void); void z_inc (void); void z_inc_chk (void); void z_input_stream (void); void z_insert_obj (void); void z_je (void); void z_jg (void); void z_jin (void); void z_jl (void); void z_jump (void); void z_jz (void); void z_load (void); void z_loadb (void); void z_loadw (void); void z_log_shift (void); void z_make_menu (void); void z_mod (void); void z_mouse_window (void); void z_move_window (void); void z_mul (void); void z_new_line (void); void z_nop (void); void z_not (void); void z_or (void); void z_output_stream (void); void z_picture_data (void); void z_picture_table (void); void z_piracy (void); void z_pop (void); void z_pop_stack (void); void z_print (void); void z_print_addr (void); void z_print_char (void); void z_print_form (void); void z_print_num (void); void z_print_obj (void); void z_print_paddr (void); void z_print_ret (void); void z_print_table (void); void z_print_unicode (void); void z_pull (void); void z_push (void); void z_push_stack (void); void z_put_prop (void); void z_put_wind_prop (void); void z_quit (void); void z_random (void); void z_read (void); void z_read_char (void); void z_read_mouse (void); void z_remove_obj (void); void z_restart (void); void z_restore (void); void z_restore_undo (void); void z_ret (void); void z_ret_popped (void); void z_rfalse (void); void z_rtrue (void); void z_save (void); void z_save_undo (void); void z_scan_table (void); void z_scroll_window (void); void z_set_attr (void); void z_set_font (void); void z_set_colour (void); void z_set_cursor (void); void z_set_margins (void); void z_set_window (void); void z_set_text_style (void); void z_show_status (void); void z_sound_effect (void); void z_split_window (void); void z_store (void); void z_storeb (void); void z_storew (void); void z_sub (void); void z_test (void); void z_test_attr (void); void z_throw (void); void z_tokenise (void); void z_verify (void); void z_window_size (void); void z_window_style (void); /* Definitions for error handling functions and error codes. */ /* extern int err_report_mode; */ void init_err (void); void runtime_error (int); /* Error codes */ #define ERR_TEXT_BUF_OVF 1 /* Text buffer overflow */ #define ERR_STORE_RANGE 2 /* Store out of dynamic memory */ #define ERR_DIV_ZERO 3 /* Division by zero */ #define ERR_ILL_OBJ 4 /* Illegal object */ #define ERR_ILL_ATTR 5 /* Illegal attribute */ #define ERR_NO_PROP 6 /* No such property */ #define ERR_STK_OVF 7 /* Stack overflow */ #define ERR_ILL_CALL_ADDR 8 /* Call to illegal address */ #define ERR_CALL_NON_RTN 9 /* Call to non-routine */ #define ERR_STK_UNDF 10 /* Stack underflow */ #define ERR_ILL_OPCODE 11 /* Illegal opcode */ #define ERR_BAD_FRAME 12 /* Bad stack frame */ #define ERR_ILL_JUMP_ADDR 13 /* Jump to illegal address */ #define ERR_SAVE_IN_INTER 14 /* Can't save while in interrupt */ #define ERR_STR3_NESTING 15 /* Nesting stream #3 too deep */ #define ERR_ILL_WIN 16 /* Illegal window */ #define ERR_ILL_WIN_PROP 17 /* Illegal window property */ #define ERR_ILL_PRINT_ADDR 18 /* Print at illegal address */ #define ERR_MAX_FATAL 18 /* Less serious errors */ #define ERR_JIN_0 19 /* @jin called with object 0 */ #define ERR_GET_CHILD_0 20 /* @get_child called with object 0 */ #define ERR_GET_PARENT_0 21 /* @get_parent called with object 0 */ #define ERR_GET_SIBLING_0 22 /* @get_sibling called with object 0 */ #define ERR_GET_PROP_ADDR_0 23 /* @get_prop_addr called with object 0 */ #define ERR_GET_PROP_0 24 /* @get_prop called with object 0 */ #define ERR_PUT_PROP_0 25 /* @put_prop called with object 0 */ #define ERR_CLEAR_ATTR_0 26 /* @clear_attr called with object 0 */ #define ERR_SET_ATTR_0 27 /* @set_attr called with object 0 */ #define ERR_TEST_ATTR_0 28 /* @test_attr called with object 0 */ #define ERR_MOVE_OBJECT_0 29 /* @move_object called moving object 0 */ #define ERR_MOVE_OBJECT_TO_0 30 /* @move_object called moving into object 0 */ #define ERR_REMOVE_OBJECT_0 31 /* @remove_object called with object 0 */ #define ERR_GET_NEXT_PROP_0 32 /* @get_next_prop called with object 0 */ #define ERR_NUM_ERRORS (32) /* There are four error reporting modes: never report errors; report only the first time a given error type occurs; report every time an error occurs; or treat all errors as fatal errors, killing the interpreter. I strongly recommend "report once" as the default. But you can compile in a different default by changing the definition of ERR_DEFAULT_REPORT_MODE. In any case, the player can specify a report mode on the command line by typing "-Z 0" through "-Z 3". */ #define ERR_REPORT_NEVER (0) #define ERR_REPORT_ONCE (1) #define ERR_REPORT_ALWAYS (2) #define ERR_REPORT_FATAL (3) #define ERR_DEFAULT_REPORT_MODE ERR_REPORT_ONCE /*** Assorted initialization functions ***/ void init_buffer (void); void init_process (void); void init_sound (void); /*** Various global functions ***/ zchar translate_from_zscii (zbyte); zbyte translate_to_zscii (zchar); void flush_buffer (void); void new_line (void); void print_char (zchar); void print_num (zword); void print_object (zword); void print_string (const char *); void stream_mssg_on (void); void stream_mssg_off (void); void ret (zword); void store (zword); void branch (bool); void storeb (zword, zbyte); void storew (zword, zword); /*** returns the current window ***/ Zwindow * curwinrec( void); /*** Interface functions ***/ void os_beep (int); int os_char_width (zchar); void os_display_char (zchar); void os_display_string (const zchar *); void os_draw_picture (int, int, int); void os_erase_area (int, int, int, int, int); void os_fatal (const char *, ...); void os_finish_with_sample (); int os_font_data (int, int *, int *); void os_init_screen (void); void os_more_prompt (void); int os_peek_colour (void); int os_picture_data (int, int *, int *); void os_prepare_sample (int); void os_process_arguments (int, char *[]); int os_random_seed (void); int os_read_file_name (char *, const char *, int); zchar os_read_key (int, int); zchar os_read_line (int, zchar *, int, int, int); void os_reset_screen (void); void os_restart_game (int); void os_scroll_area (int, int, int, int, int); void os_set_colour (int, int); void os_set_cursor (int, int); void os_set_font (int); void os_set_text_style (int); void os_start_sample (int, int, int, zword); void os_stop_sample (); int os_string_width (const zchar *); void os_init_setup (void); int os_speech_output(const zchar *); frotz-2.44/src/common/files.c0000644000175000017500000002240512527045477013075 00000000000000/* files.c - Transcription, recording and playback * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "frotz.h" #ifndef SEEK_SET #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif extern void set_more_prompts (bool); extern bool is_terminator (zchar); extern bool read_yes_or_no (const char *); /* char script_name[MAX_FILE_NAME + 1] = DEFAULT_SCRIPT_NAME; */ /* char command_name[MAX_FILE_NAME + 1] = DEFAULT_COMMAND_NAME; */ #ifdef __MSDOS__ extern char latin1_to_ibm[]; #endif static int script_width = 0; static FILE *sfp = NULL; static FILE *rfp = NULL; static FILE *pfp = NULL; /* * script_open * * Open the transcript file. 'AMFV' makes this more complicated as it * turns transcription on/off several times to exclude some text from * the transcription file. This wasn't a problem for the original V4 * interpreters which always sent transcription to the printer, but it * means a problem to modern interpreters that offer to open a new file * every time transcription is turned on. Our solution is to append to * the old transcription file in V1 to V4, and to ask for a new file * name in V5+. * */ void script_open (void) { static bool script_valid = FALSE; char new_name[MAX_FILE_NAME + 1]; h_flags &= ~SCRIPTING_FLAG; if (h_version >= V5 || !script_valid) { if (!os_read_file_name (new_name, f_setup.script_name, FILE_SCRIPT)) goto done; strcpy (f_setup.script_name, new_name); } /* Opening in "at" mode doesn't work for script_erase_input... */ if ((sfp = fopen (f_setup.script_name, "r+t")) != NULL || (sfp = fopen (f_setup.script_name, "w+t")) != NULL) { fseek (sfp, 0, SEEK_END); h_flags |= SCRIPTING_FLAG; script_valid = TRUE; ostream_script = TRUE; script_width = 0; } else print_string ("Cannot open file\n"); done: SET_WORD (H_FLAGS, h_flags); }/* script_open */ /* * script_close * * Stop transcription. * */ void script_close (void) { h_flags &= ~SCRIPTING_FLAG; SET_WORD (H_FLAGS, h_flags); fclose (sfp); ostream_script = FALSE; }/* script_close */ /* * script_new_line * * Write a newline to the transcript file. * */ void script_new_line (void) { if (fputc ('\n', sfp) == EOF) script_close (); script_width = 0; }/* script_new_line */ /* * script_char * * Write a single character to the transcript file. * */ void script_char (zchar c) { if (c == ZC_INDENT && script_width != 0) c = ' '; if (c == ZC_INDENT) { script_char (' '); script_char (' '); script_char (' '); return; } if (c == ZC_GAP) { script_char (' '); script_char (' '); return; } #ifdef __MSDOS__ if (c >= ZC_LATIN1_MIN) c = latin1_to_ibm[c - ZC_LATIN1_MIN]; #endif fputc (c, sfp); script_width++; }/* script_char */ /* * script_word * * Write a string to the transcript file. * */ void script_word (const zchar *s) { int width; int i; if (*s == ZC_INDENT && script_width != 0) script_char (*s++); for (i = 0, width = 0; s[i] != 0; i++) if (s[i] == ZC_NEW_STYLE || s[i] == ZC_NEW_FONT) i++; else if (s[i] == ZC_GAP) width += 3; else if (s[i] == ZC_INDENT) width += 2; else width += 1; if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) { if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP) s++; script_new_line (); } for (i = 0; s[i] != 0; i++) if (s[i] == ZC_NEW_FONT || s[i] == ZC_NEW_STYLE) i++; else script_char (s[i]); }/* script_word */ /* * script_write_input * * Send an input line to the transcript file. * */ void script_write_input (const zchar *buf, zchar key) { int width; int i; for (i = 0, width = 0; buf[i] != 0; i++) width++; if (f_setup.script_cols != 0 && script_width + width > f_setup.script_cols) script_new_line (); for (i = 0; buf[i] != 0; i++) script_char (buf[i]); if (key == ZC_RETURN) script_new_line (); }/* script_write_input */ /* * script_erase_input * * Remove an input line from the transcript file. * */ void script_erase_input (const zchar *buf) { int width; int i; for (i = 0, width = 0; buf[i] != 0; i++) width++; fseek (sfp, -width, SEEK_CUR); script_width -= width; }/* script_erase_input */ /* * script_mssg_on * * Start sending a "debugging" message to the transcript file. * */ void script_mssg_on (void) { if (script_width != 0) script_new_line (); script_char (ZC_INDENT); }/* script_mssg_on */ /* * script_mssg_off * * Stop writing a "debugging" message. * */ void script_mssg_off (void) { script_new_line (); }/* script_mssg_off */ /* * record_open * * Open a file to record the player's input. * */ void record_open (void) { char new_name[MAX_FILE_NAME + 1]; if (os_read_file_name (new_name, f_setup.command_name, FILE_RECORD)) { strcpy (f_setup.command_name, new_name); if ((rfp = fopen (new_name, "wt")) != NULL) ostream_record = TRUE; else print_string ("Cannot open file\n"); } }/* record_open */ /* * record_close * * Stop recording the player's input. * */ void record_close (void) { fclose (rfp); ostream_record = FALSE; }/* record_close */ /* * record_code * * Helper function for record_char. * */ static void record_code (int c, bool force_encoding) { if (force_encoding || c == '[' || c < 0x20 || c > 0x7e) { int i; fputc ('[', rfp); for (i = 10000; i != 0; i /= 10) if (c >= i || i == 1) fputc ('0' + (c / i) % 10, rfp); fputc (']', rfp); } else fputc (c, rfp); }/* record_code */ /* * record_char * * Write a character to the command file. * */ static void record_char (zchar c) { if (c != ZC_RETURN) { if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) { record_code (translate_to_zscii (c), FALSE); if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) { record_code (mouse_x, TRUE); record_code (mouse_y, TRUE); } } else record_code (1000 + c - ZC_HKEY_MIN, TRUE); } }/* record_char */ /* * record_write_key * * Copy a keystroke to the command file. * */ void record_write_key (zchar key) { record_char (key); if (fputc ('\n', rfp) == EOF) record_close (); }/* record_write_key */ /* * record_write_input * * Copy a line of input to a command file. * */ void record_write_input (const zchar *buf, zchar key) { zchar c; while ((c = *buf++) != 0) record_char (c); record_char (key); if (fputc ('\n', rfp) == EOF) record_close (); }/* record_write_input */ /* * replay_open * * Open a file of commands for playback. * */ void replay_open (void) { char new_name[MAX_FILE_NAME + 1]; if (os_read_file_name (new_name, f_setup.command_name, FILE_PLAYBACK)) { strcpy (f_setup.command_name, new_name); if ((pfp = fopen (new_name, "rt")) != NULL) { set_more_prompts (read_yes_or_no ("Do you want MORE prompts")); istream_replay = TRUE; } else print_string ("Cannot open file\n"); } }/* replay_open */ /* * replay_close * * Stop playback of commands. * */ void replay_close (void) { set_more_prompts (TRUE); fclose (pfp); istream_replay = FALSE; }/* replay_close */ /* * replay_code * * Helper function for replay_key and replay_line. * */ static int replay_code (void) { int c; if ((c = fgetc (pfp)) == '[') { int c2; c = 0; while ((c2 = fgetc (pfp)) != EOF && c2 >= '0' && c2 <= '9') c = 10 * c + c2 - '0'; return (c2 == ']') ? c : EOF; } else return c; }/* replay_code */ /* * replay_char * * Read a character from the command file. * */ static zchar replay_char (void) { int c; if ((c = replay_code ()) != EOF) { if (c != '\n') { if (c < 1000) { c = translate_from_zscii (c); if (c == ZC_SINGLE_CLICK || c == ZC_DOUBLE_CLICK) { mouse_x = replay_code (); mouse_y = replay_code (); } return c; } else return ZC_HKEY_MIN + c - 1000; } ungetc ('\n', pfp); return ZC_RETURN; } else return ZC_BAD; }/* replay_char */ /* * replay_read_key * * Read a keystroke from a command file. * */ zchar replay_read_key (void) { zchar key; key = replay_char (); if (fgetc (pfp) != '\n') { replay_close (); return ZC_BAD; } else return key; }/* replay_read_key */ /* * replay_read_input * * Read a line of input from a command file. * */ zchar replay_read_input (zchar *buf) { zchar c; for (;;) { c = replay_char (); if (c == ZC_BAD || is_terminator (c)) break; *buf++ = c; } *buf = 0; if (fgetc (pfp) != '\n') { replay_close (); return ZC_BAD; } else return c; }/* replay_read_input */ frotz-2.44/src/common/screen.c0000644000175000017500000010435612527045477013260 00000000000000/* screen.c - Generic screen manipulation * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" extern void set_header_extension (int, zword); extern int direct_call (zword); static struct { enum story story_id; int pic; int pic1; int pic2; } mapper[] = { { ZORK_ZERO, 5, 497, 498 }, { ZORK_ZERO, 6, 501, 502 }, { ZORK_ZERO, 7, 499, 500 }, { ZORK_ZERO, 8, 503, 504 }, { ARTHUR, 54, 170, 171 }, { SHOGUN, 50, 61, 62 }, { UNKNOWN, 0, 0, 0 } }; static int font_height = 1; static int font_width = 1; static bool input_redraw = FALSE; static bool more_prompts = TRUE; static bool discarding = FALSE; static bool cursor = TRUE; static int input_window = 0; static Zwindow wp[8], *cwp = wp; Zwindow * curwinrec() { return cwp;} /* * winarg0 * * Return the window number in zargs[0]. In V6 only, -3 refers to the * current window. * */ static zword winarg0 (void) { if (h_version == V6 && (short) zargs[0] == -3) return cwin; if (zargs[0] >= ((h_version == V6) ? 8 : 2)) runtime_error (ERR_ILL_WIN); return zargs[0]; }/* winarg0 */ /* * winarg2 * * Return the (optional) window number in zargs[2]. -3 refers to the * current window. This optional window number was only used by some * V6 opcodes: set_cursor, set_margins, set_colour. * */ static zword winarg2 (void) { if (zargc < 3 || (short) zargs[2] == -3) return cwin; if (zargs[2] >= 8) runtime_error (ERR_ILL_WIN); return zargs[2]; }/* winarg2 */ /* * update_cursor * * Move the hardware cursor to make it match the window properties. * */ static void update_cursor (void) { os_set_cursor ( cwp->y_pos + cwp->y_cursor - 1, cwp->x_pos + cwp->x_cursor - 1); }/* update_cursor */ /* * reset_cursor * * Reset the cursor of a given window to its initial position. * */ static void reset_cursor (zword win) { int lines = 0; if (h_version <= V4 && win == 0) lines = wp[0].y_size / hi (wp[0].font_size) - 1; wp[win].y_cursor = hi (wp[0].font_size) * lines + 1; wp[win].x_cursor = wp[win].left + 1; if (win == cwin) update_cursor (); }/* reset_cursor */ /* * set_more_prompts * * Turn more prompts on/off. * */ void set_more_prompts (bool flag) { if (flag && !more_prompts) cwp->line_count = 0; more_prompts = flag; }/* set_more_prompts */ /* * units_left * * Return the #screen units from the cursor to the end of the line. * */ static int units_left (void) { return cwp->x_size - cwp->right - cwp->x_cursor + 1; }/* units_left */ /* * get_max_width * * Return maximum width of a line in the given window. This is used in * connection with the extended output stream #3 call in V6. * */ zword get_max_width (zword win) { if (h_version == V6) { if (win >= 8) runtime_error (ERR_ILL_WIN); return wp[win].x_size - wp[win].left - wp[win].right; } else return 0xffff; }/* get_max_width */ /* * countdown * * Decrement the newline counter. Call the newline interrupt when the * counter hits zero. This is a helper function for screen_new_line. * */ static void countdown (void) { if (cwp->nl_countdown != 0) if (--cwp->nl_countdown == 0) direct_call (cwp->nl_routine); }/* countdown */ /* * screen_new_line * * Print a newline to the screen. * */ void screen_new_line (void) { if (discarding) return; /* Handle newline interrupts at the start (for most cases) */ if (h_interpreter_number != INTERP_MSDOS || story_id != ZORK_ZERO || h_release != 393) countdown (); /* Check whether the last input line gets destroyed */ if (input_window == cwin) input_redraw = TRUE; /* If the cursor has not reached the bottom line, then move it to the next line; otherwise scroll the window or reset the cursor to the top left. */ cwp->x_cursor = cwp->left + 1; if (cwp->y_cursor + 2 * font_height - 1 > cwp->y_size) if (enable_scrolling) { zword y = cwp->y_pos; zword x = cwp->x_pos; os_scroll_area (y, x, y + cwp->y_size - 1, x + cwp->x_size - 1, font_height); } else cwp->y_cursor = 1; else cwp->y_cursor += font_height; update_cursor (); /* See if we need to print a more prompt (unless the game has set the line counter to -999 in order to suppress more prompts). */ if (enable_scrolling && (short) cwp->line_count != -999) { zword above = (cwp->y_cursor - 1) / font_height; zword below = (cwp->y_size - cwp->y_cursor + 1) / font_height; cwp->line_count++; if ((short) cwp->line_count >= (short) above + below - 1) { if (more_prompts) os_more_prompt (); cwp->line_count = f_setup.context_lines; } } /* Handle newline interrupts at the end for Zork Zero under DOS */ if (h_interpreter_number == INTERP_MSDOS && story_id == ZORK_ZERO && h_release == 393) countdown (); }/* screen_new_line */ /* * screen_char * * Display a single character on the screen. * */ void screen_char (zchar c) { int width; if (discarding) return; if (c == ZC_INDENT && cwp->x_cursor != cwp->left + 1) c = ' '; if (units_left () < (width = os_char_width (c))) { if (!enable_wrapping) { cwp->x_cursor = cwp->x_size - cwp->right; return; } screen_new_line (); } os_display_char (c); cwp->x_cursor += width; }/* screen_char */ /* * screen_word * * Display a string of characters on the screen. If the word doesn't fit * then use wrapping or clipping depending on the current setting of the * enable_wrapping flag. * */ void screen_word (const zchar *s) { int width; if (discarding) return; if (*s == ZC_INDENT && cwp->x_cursor != cwp->left + 1) screen_char (*s++); if (units_left () < (width = os_string_width (s))) { if (!enable_wrapping) { zchar c; while ((c = *s++) != 0) if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) { int arg = (int) *s++; if (c == ZC_NEW_FONT) os_set_font (arg); if (c == ZC_NEW_STYLE) os_set_text_style (arg); } else screen_char (c); return; } if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP) width = os_string_width (++s); #ifdef AMIGA if (cwin == 0) Justifiable (); #endif screen_new_line (); } os_display_string (s); cwp->x_cursor += width; }/* screen_word */ /* * screen_write_input * * Display an input line on the screen. This is required during playback. * */ void screen_write_input (const zchar *buf, zchar key) { int width; if (units_left () < (width = os_string_width (buf))) screen_new_line (); os_display_string (buf); cwp->x_cursor += width; if (key == ZC_RETURN) screen_new_line (); }/* screen_write_input */ /* * screen_erase_input * * Remove an input line that has already been printed from the screen * as if it was deleted by the player. This could be necessary during * playback. * */ void screen_erase_input (const zchar *buf) { if (buf[0] != 0) { int width = os_string_width (buf); zword y; zword x; cwp->x_cursor -= width; y = cwp->y_pos + cwp->y_cursor - 1; x = cwp->x_pos + cwp->x_cursor - 1; os_erase_area (y, x, y + font_height - 1, x + width - 1, -1); os_set_cursor (y, x); } }/* screen_erase_input */ /* * console_read_input * * Read an input line from the keyboard and return the terminating key. * */ zchar console_read_input (int max, zchar *buf, zword timeout, bool continued) { zchar key; int i; /* Make sure there is some space for input */ if (cwin == 0 && units_left () + os_string_width (buf) < 10 * font_width) screen_new_line (); /* Make sure the input line is visible */ if (continued && input_redraw) screen_write_input (buf, -1); input_window = cwin; input_redraw = FALSE; /* Get input line from IO interface */ cwp->x_cursor -= os_string_width (buf); key = os_read_line (max, buf, timeout, units_left (), continued); cwp->x_cursor += os_string_width (buf); if (key != ZC_TIME_OUT) for (i = 0; i < 8; i++) wp[i].line_count = 0; /* Add a newline if the input was terminated normally */ if (key == ZC_RETURN) screen_new_line (); return key; }/* console_read_input */ /* * console_read_key * * Read a single keystroke and return it. * */ zchar console_read_key (zword timeout) { zchar key; int i; key = os_read_key (timeout, cursor); if (key != ZC_TIME_OUT) for (i = 0; i < 8; i++) wp[i].line_count = 0; return key; }/* console_read_key */ /* * update_attributes * * Set the three enable_*** variables to make them match the attributes * of the current window. * */ static void update_attributes (void) { zword attr = cwp->attribute; enable_wrapping = attr & 1; enable_scrolling = attr & 2; enable_scripting = attr & 4; enable_buffering = attr & 8; /* Some story files forget to select wrapping for printing hints */ if (story_id == ZORK_ZERO && h_release == 366) if (cwin == 0) enable_wrapping = TRUE; if (story_id == SHOGUN && h_release <= 295) if (cwin == 0) enable_wrapping = TRUE; }/* update_attributes */ /* * refresh_text_style * * Set the right text style. This can be necessary when the fixed font * flag is changed, or when a new window is selected, or when the game * uses the set_text_style opcode. * */ void refresh_text_style (void) { zword style; if (h_version != V6) { style = wp[0].style; if (cwin != 0 || h_flags & FIXED_FONT_FLAG) style |= FIXED_WIDTH_STYLE; } else style = cwp->style; if (!ostream_memory && ostream_screen && enable_buffering) { print_char (ZC_NEW_STYLE); print_char (style); } else os_set_text_style (style); }/* refresh_text_style */ /* * set_window * * Set the current window. In V6 every window has its own set of window * properties such as colours, text style, cursor position and size. * */ static void set_window (zword win) { flush_buffer (); cwin = win; cwp = wp + win; update_attributes (); if (h_version == V6) { os_set_colour (lo (cwp->colour), hi (cwp->colour)); if (os_font_data (cwp->font, &font_height, &font_width)) os_set_font (cwp->font); os_set_text_style (cwp->style); } else refresh_text_style (); if (h_version != V6 && win != 0) { wp[win].y_cursor = 1; wp[win].x_cursor = 1; } update_cursor (); }/* set_window */ /* * erase_window * * Erase a window to background colour. * */ void erase_window (zword win) { zword y = wp[win].y_pos; zword x = wp[win].x_pos; if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA) os_set_colour (lo (wp[win].colour), hi (wp[win].colour)); os_erase_area (y, x, y + wp[win].y_size - 1, x + wp[win].x_size - 1, win); if (h_version == V6 && win != cwin && h_interpreter_number != INTERP_AMIGA) os_set_colour (lo (cwp->colour), hi (cwp->colour)); reset_cursor (win); wp[win].line_count = 0; }/* erase_window */ /* * split_window * * Divide the screen into upper (1) and lower (0) windows. In V3 the upper * window appears below the status line. * */ void split_window (zword height) { zword stat_height = 0; flush_buffer (); /* Calculate height of status line and upper window */ if (h_version != V6) height *= hi (wp[1].font_size); if (h_version <= V3) stat_height = hi (wp[7].font_size); /* Cursor of upper window mustn't be swallowed by the lower window */ wp[1].y_cursor += wp[1].y_pos - 1 - stat_height; wp[1].y_pos = 1 + stat_height; wp[1].y_size = height; if ((short) wp[1].y_cursor > (short) wp[1].y_size) reset_cursor (1); /* Cursor of lower window mustn't be swallowed by the upper window */ wp[0].y_cursor += wp[0].y_pos - 1 - stat_height - height; wp[0].y_pos = 1 + stat_height + height; wp[0].y_size = h_screen_height - stat_height - height; if ((short) wp[0].y_cursor < 1) reset_cursor (0); /* Erase the upper window in V3 only */ if (h_version == V3 && height != 0) erase_window (1); }/* split_window */ /* * erase_screen * * Erase the entire screen to background colour. * */ static void erase_screen (zword win) { int i; os_erase_area (1, 1, h_screen_height, h_screen_width, -2); if ((short) win == -1) { split_window (0); set_window (0); reset_cursor (0); } for (i = 0; i < 8; i++) wp[i].line_count = 0; }/* erase_screen */ /* #ifdef AMIGA */ /* * resize_screen * * Try to adapt the window properties to a new screen size. * */ void resize_screen (void) { if (h_version != V6) { wp[0].x_size = h_screen_width; wp[1].x_size = h_screen_width; wp[7].x_size = h_screen_width; wp[0].y_size = h_screen_height - wp[1].y_size - wp[7].y_size; } }/* resize_screen */ /* #endif */ /* * restart_screen * * Prepare the screen for a new game. * */ void restart_screen (void) { /* Use default settings */ os_set_colour (h_default_foreground, h_default_background); if (os_font_data (TEXT_FONT, &font_height, &font_width)) os_set_font (TEXT_FONT); os_set_text_style (0); cursor = TRUE; /* Initialise window properties */ mwin = 1; for (cwp = wp; cwp < wp + 8; cwp++) { cwp->y_pos = 1; cwp->x_pos = 1; cwp->y_size = 0; cwp->x_size = 0; cwp->y_cursor = 1; cwp->x_cursor = 1; cwp->left = 0; cwp->right = 0; cwp->nl_routine = 0; cwp->nl_countdown = 0; cwp->style = 0; cwp->colour = (h_default_background << 8) | h_default_foreground; cwp->font = TEXT_FONT; cwp->font_size = (font_height << 8) | font_width; cwp->attribute = 8; } /* Prepare lower/upper windows and status line */ wp[0].attribute = 15; wp[0].left = f_setup.left_margin; wp[0].right = f_setup.right_margin; wp[0].x_size = h_screen_width; wp[1].x_size = h_screen_width; if (h_version <= V3) wp[7].x_size = h_screen_width; os_restart_game (RESTART_WPROP_SET); /* Clear the screen, unsplit it and select window 0 */ erase_screen ((zword) (-1)); }/* restart_screen */ /* * validate_click * * Return false if the last mouse click occured outside the current * mouse window; otherwise write the mouse arrow coordinates to the * memory of the header extension table and return true. * */ bool validate_click (void) { if (mwin >= 0) { if (mouse_y < wp[mwin].y_pos || mouse_y >= wp[mwin].y_pos + wp[mwin].y_size) return FALSE; if (mouse_x < wp[mwin].x_pos || mouse_x >= wp[mwin].x_pos + wp[mwin].x_size) return FALSE; hx_mouse_y = mouse_y - wp[mwin].y_pos + 1; hx_mouse_x = mouse_x - wp[mwin].x_pos + 1; } else { if (mouse_y < 1 || mouse_y > h_screen_height) return FALSE; if (mouse_x < 1 || mouse_x > h_screen_width) return FALSE; hx_mouse_y = mouse_y; hx_mouse_x = mouse_x; } if (h_version != V6) { hx_mouse_y = (hx_mouse_y - 1) / h_font_height + 1; hx_mouse_x = (hx_mouse_x - 1) / h_font_width + 1; } set_header_extension (HX_MOUSE_Y, hx_mouse_y); set_header_extension (HX_MOUSE_X, hx_mouse_x); return TRUE; }/* validate_click */ /* * screen_mssg_on * * Start printing a so-called debugging message. The contents of the * message are passed to the message stream, a Frotz specific output * stream with maximum priority. * */ void screen_mssg_on (void) { if (cwin == 0) { /* messages in window 0 only */ os_set_text_style (0); if (cwp->x_cursor != cwp->left + 1) screen_new_line (); screen_char (ZC_INDENT); } else discarding = TRUE; /* discard messages in other windows */ }/* screen_mssg_on */ /* * screen_mssg_off * * Stop printing a "debugging" message. * */ void screen_mssg_off (void) { if (cwin == 0) { /* messages in window 0 only */ screen_new_line (); refresh_text_style (); } else discarding = FALSE; /* message has been discarded */ }/* screen_mssg_off */ /* * z_buffer_mode, turn text buffering on/off. * * zargs[0] = new text buffering flag (0 or 1) * */ void z_buffer_mode (void) { /* Infocom's V6 games rarely use the buffer_mode opcode. If they do then only to print text immediately, without any delay. This was used to give the player some sign of life while the game was spending much time on parsing a complicated input line. (To turn off word wrapping, V6 games use the window_style opcode instead.) Today we can afford to ignore buffer_mode in V6. */ if (h_version != V6) { flush_buffer (); wp[0].attribute &= ~8; if (zargs[0] != 0) wp[0].attribute |= 8; update_attributes (); } }/* z_buffer_mode */ /* * z_draw_picture, draw a picture. * * zargs[0] = number of picture to draw * zargs[1] = y-coordinate of top left corner * zargs[2] = x-coordinate of top left corner * */ void z_draw_picture (void) { zword pic = zargs[0]; zword y = zargs[1]; zword x = zargs[2]; int i; flush_buffer (); if (y == 0) /* use cursor line if y-coordinate is 0 */ y = cwp->y_cursor; if (x == 0) /* use cursor column if x-coordinate is 0 */ x = cwp->x_cursor; y += cwp->y_pos - 1; x += cwp->x_pos - 1; /* The following is necessary to make Amiga and Macintosh story files work with MCGA graphics files. Some screen-filling pictures of the original Amiga release like the borders of Zork Zero were split into several MCGA pictures (left, right and top borders). We pretend this has not happened. */ for (i = 0; mapper[i].story_id != UNKNOWN; i++) if (story_id == mapper[i].story_id && pic == mapper[i].pic) { int height1, width1; int height2, width2; int delta = 0; os_picture_data (pic, &height1, &width1); os_picture_data (mapper[i].pic2, &height2, &width2); if (story_id == ARTHUR && pic == 54) delta = h_screen_width / 160; os_draw_picture (mapper[i].pic1, y + height1, x + delta); os_draw_picture (mapper[i].pic2, y + height1, x + width1 - width2 - delta); } os_draw_picture (pic, y, x); if (story_id == SHOGUN) if (pic == 3) { int height, width; os_picture_data (59, &height, &width); os_draw_picture (59, y, h_screen_width - width + 1); } }/* z_draw_picture */ /* * z_erase_line, erase the line starting at the cursor position. * * zargs[0] = 1 + #units to erase (1 clears to the end of the line) * */ void z_erase_line (void) { zword pixels = zargs[0]; zword y, x; flush_buffer (); /* Clipping at the right margin of the current window */ if (--pixels == 0 || pixels > units_left ()) pixels = units_left (); /* Erase from cursor position */ y = cwp->y_pos + cwp->y_cursor - 1; x = cwp->x_pos + cwp->x_cursor - 1; os_erase_area (y, x, y + font_height - 1, x + pixels - 1, -1); }/* z_erase_line */ /* * z_erase_picture, erase a picture with background colour. * * zargs[0] = number of picture to erase * zargs[1] = y-coordinate of top left corner (optional) * zargs[2] = x-coordinate of top left corner (optional) * */ void z_erase_picture (void) { int height, width; zword y = zargs[1]; zword x = zargs[2]; flush_buffer (); if (y == 0) /* use cursor line if y-coordinate is 0 */ y = cwp->y_cursor; if (x == 0) /* use cursor column if x-coordinate is 0 */ x = cwp->x_cursor; os_picture_data (zargs[0], &height, &width); y += cwp->y_pos - 1; x += cwp->x_pos - 1; os_erase_area (y, x, y + height - 1, x + width - 1, -1); }/* z_erase_picture */ /* * z_erase_window, erase a window or the screen to background colour. * * zargs[0] = window (-3 current, -2 screen, -1 screen & unsplit) * */ void z_erase_window (void) { flush_buffer (); if ((short) zargs[0] == -1 || (short) zargs[0] == -2) erase_screen (zargs[0]); else erase_window (winarg0 ()); }/* z_erase_window */ /* * z_get_cursor, write the cursor coordinates into a table. * * zargs[0] = address to write information to * */ void z_get_cursor (void) { zword y, x; flush_buffer (); y = cwp->y_cursor; x = cwp->x_cursor; if (h_version != V6) { /* convert to grid positions */ y = (y - 1) / h_font_height + 1; x = (x - 1) / h_font_width + 1; } storew ((zword) (zargs[0] + 0), y); storew ((zword) (zargs[0] + 2), x); }/* z_get_cursor */ /* * z_get_wind_prop, store the value of a window property. * * zargs[0] = window (-3 is the current one) * zargs[1] = number of window property to be stored * */ void z_get_wind_prop (void) { flush_buffer (); if (zargs[1] >= 16) runtime_error (ERR_ILL_WIN_PROP); store (((zword *) (wp + winarg0 ())) [zargs[1]]); }/* z_get_wind_prop */ /* * z_mouse_window, select a window as mouse window. * * zargs[0] = window number (-3 is the current) or -1 for the screen * */ void z_mouse_window (void) { mwin = ((short) zargs[0] == -1) ? -1 : winarg0 (); }/* z_mouse_window */ /* * z_move_window, place a window on the screen. * * zargs[0] = window (-3 is the current one) * zargs[1] = y-coordinate * zargs[2] = x-coordinate * */ void z_move_window (void) { zword win = winarg0 (); flush_buffer (); wp[win].y_pos = zargs[1]; wp[win].x_pos = zargs[2]; if (win == cwin) update_cursor (); }/* z_move_window */ /* * z_picture_data, get information on a picture or the graphics file. * * zargs[0] = number of picture or 0 for the graphics file * zargs[1] = address to write information to * */ void z_picture_data (void) { zword pic = zargs[0]; zword table = zargs[1]; int height, width; int i; bool avail = os_picture_data (pic, &height, &width); for (i = 0; mapper[i].story_id != UNKNOWN; i++) if (story_id == mapper[i].story_id) { if (pic == mapper[i].pic) { int height2, width2; avail &= os_picture_data (mapper[i].pic1, &height2, &width2); avail &= os_picture_data (mapper[i].pic2, &height2, &width2); height += height2; } else if (pic == mapper[i].pic1 || pic == mapper[i].pic2) avail = FALSE; } storew ((zword) (table + 0), (zword) (height)); storew ((zword) (table + 2), (zword) (width)); branch (avail); }/* z_picture_data */ /* * z_picture_table, prepare a group of pictures for faster display. * * zargs[0] = address of table holding the picture numbers * */ void z_picture_table (void) { /* This opcode is used by Shogun and Zork Zero when the player encounters built-in games such as Peggleboz. Nowadays it is not very helpful to hold the picture data in memory because even a small disk cache avoids re-loading of data. */ }/* z_picture_table */ /* * z_print_table, print ASCII text in a rectangular area. * * zargs[0] = address of text to be printed * zargs[1] = width of rectangular area * zargs[2] = height of rectangular area (optional) * zargs[3] = number of char's to skip between lines (optional) * */ void z_print_table (void) { zword addr = zargs[0]; zword x; int i, j; flush_buffer (); /* Supply default arguments */ if (zargc < 3) zargs[2] = 1; if (zargc < 4) zargs[3] = 0; /* Write text in width x height rectangle */ x = cwp->x_cursor; for (i = 0; i < zargs[2]; i++) { if (i != 0) { flush_buffer (); cwp->y_cursor += font_height; cwp->x_cursor = x; update_cursor (); } for (j = 0; j < zargs[1]; j++) { zbyte c; LOW_BYTE (addr, c) addr++; print_char (c); } addr += zargs[3]; } }/* z_print_table */ /* * z_put_wind_prop, set the value of a window property. * * zargs[0] = window (-3 is the current one) * zargs[1] = number of window property to set * zargs[2] = value to set window property to * */ void z_put_wind_prop (void) { flush_buffer (); if (zargs[1] >= 16) runtime_error (ERR_ILL_WIN_PROP); ((zword *) (wp + winarg0 ())) [zargs[1]] = zargs[2]; }/* z_put_wind_prop */ /* * z_scroll_window, scroll a window up or down. * * zargs[0] = window (-3 is the current one) * zargs[1] = #screen units to scroll up (positive) or down (negative) * */ void z_scroll_window (void) { zword win = winarg0 (); zword y, x; flush_buffer (); /* Use the correct set of colours when scrolling the window */ if (win != cwin && h_interpreter_number != INTERP_AMIGA) os_set_colour (lo (wp[win].colour), hi (wp[win].colour)); y = wp[win].y_pos; x = wp[win].x_pos; os_scroll_area (y, x, y + wp[win].y_size - 1, x + wp[win].x_size - 1, (short) zargs[1]); if (win != cwin && h_interpreter_number != INTERP_AMIGA) os_set_colour (lo (cwp->colour), hi (cwp->colour)); }/* z_scroll_window */ /* * z_set_colour, set the foreground and background colours. * * zargs[0] = foreground colour * zargs[1] = background colour * zargs[2] = window (-3 is the current one, optional) * */ void z_set_colour (void) { zword win = (h_version == V6) ? winarg2 () : 0; zword fg = zargs[0]; zword bg = zargs[1]; flush_buffer (); if ((short) fg == -1) /* colour -1 is the colour at the cursor */ fg = os_peek_colour (); if ((short) bg == -1) bg = os_peek_colour (); if (fg == 0) /* colour 0 means keep current colour */ fg = lo (wp[win].colour); if (bg == 0) bg = hi (wp[win].colour); if (fg == 1) /* colour 1 is the system default colour */ fg = h_default_foreground; if (bg == 1) bg = h_default_background; if (h_version == V6 && h_interpreter_number == INTERP_AMIGA) /* Changing colours of window 0 affects the entire screen */ if (win == 0) { int i; for (i = 1; i < 8; i++) { zword bg2 = hi (wp[i].colour); zword fg2 = lo (wp[i].colour); if (bg2 < 16) bg2 = (bg2 == lo (wp[0].colour)) ? fg : bg; if (fg2 < 16) fg2 = (fg2 == lo (wp[0].colour)) ? fg : bg; wp[i].colour = (bg2 << 8) | fg2; } } wp[win].colour = (bg << 8) | fg; if (win == cwin || h_version != V6) os_set_colour (fg, bg); }/* z_set_colour */ /* * z_set_font, set the font for text output and store the previous font. * * zargs[0] = number of font or 0 to keep current font * */ void z_set_font (void) { zword win = (h_version == V6) ? cwin : 0; zword font = zargs[0]; if (font != 0) { if (os_font_data (font, &font_height, &font_width)) { store (wp[win].font); wp[win].font = font; wp[win].font_size = (font_height << 8) | font_width; if (!ostream_memory && ostream_screen && enable_buffering) { print_char (ZC_NEW_FONT); print_char (font); } else os_set_font (font); } else store (0); } else store (wp[win].font); }/* z_set_font */ /* * z_set_cursor, set the cursor position or turn the cursor on/off. * * zargs[0] = y-coordinate or -2/-1 for cursor on/off * zargs[1] = x-coordinate * zargs[2] = window (-3 is the current one, optional) * */ void z_set_cursor (void) { zword win = (h_version == V6) ? winarg2 () : 1; zword y = zargs[0]; zword x = zargs[1]; flush_buffer (); /* Supply default arguments */ if (zargc < 3) zargs[2] = -3; /* Handle cursor on/off */ if ((short) y < 0) { if ((short) y == -2) cursor = TRUE; if ((short) y == -1) cursor = FALSE; return; } /* Convert grid positions to screen units if this is not V6 */ if (h_version != V6) { if (cwin == 0) return; y = (y - 1) * h_font_height + 1; x = (x - 1) * h_font_width + 1; } /* Protect the margins */ if (y == 0) /* use cursor line if y-coordinate is 0 */ y = wp[win].y_cursor; if (x == 0) /* use cursor column if x-coordinate is 0 */ x = wp[win].x_cursor; if (x <= wp[win].left || x > wp[win].x_size - wp[win].right) x = wp[win].left + 1; /* Move the cursor */ wp[win].y_cursor = y; wp[win].x_cursor = x; if (win == cwin) update_cursor (); }/* z_set_cursor */ /* * z_set_margins, set the left and right margins of a window. * * zargs[0] = left margin in pixels * zargs[1] = right margin in pixels * zargs[2] = window (-3 is the current one, optional) * */ void z_set_margins (void) { zword win = winarg2 (); flush_buffer (); wp[win].left = zargs[0]; wp[win].right = zargs[1]; /* Protect the margins */ if (wp[win].x_cursor <= zargs[0] || wp[win].x_cursor > wp[win].x_size - zargs[1]) { wp[win].x_cursor = zargs[0] + 1; if (win == cwin) update_cursor (); } }/* z_set_margins */ /* * z_set_text_style, set the style for text output. * * zargs[0] = style flags to set or 0 to reset text style * */ void z_set_text_style (void) { zword win = (h_version == V6) ? cwin : 0; zword style = zargs[0]; wp[win].style |= style; if (style == 0) wp[win].style = 0; refresh_text_style (); }/* z_set_text_style */ /* * z_set_window, select the current window. * * zargs[0] = window to be selected (-3 is the current one) * */ void z_set_window (void) { set_window (winarg0 ()); }/* z_set_window */ /* * pad_status_line * * Pad the status line with spaces up to the given position. * */ static void pad_status_line (int column) { int spaces; flush_buffer (); spaces = units_left () / os_char_width (' ') - column; /* while (spaces--) */ /* Justin Wesley's fix for narrow displays (Agenda PDA) */ while (spaces-- > 0) screen_char (' '); }/* pad_status_line */ /* * z_show_status, display the status line for V1 to V3 games. * * no zargs used * */ void z_show_status (void) { zword global0; zword global1; zword global2; zword addr; bool brief = FALSE; /* One V5 game (Wishbringer Solid Gold) contains this opcode by accident, so just return if the version number does not fit */ if (h_version >= V4) return; /* Read all relevant global variables from the memory of the Z-machine into local variables */ addr = h_globals; LOW_WORD (addr, global0) addr += 2; LOW_WORD (addr, global1) addr += 2; LOW_WORD (addr, global2) /* Frotz uses window 7 for the status line. Don't forget to select reverse and fixed width text style */ set_window (7); print_char (ZC_NEW_STYLE); print_char (REVERSE_STYLE | FIXED_WIDTH_STYLE); /* If the screen width is below 55 characters then we have to use the brief status line format */ if (h_screen_cols < 55) brief = TRUE; /* Print the object description for the global variable 0 */ print_char (' '); print_object (global0); /* A header flag tells us whether we have to display the current time or the score/moves information */ if (h_config & CONFIG_TIME) { /* print hours and minutes */ zword hours = (global1 + 11) % 12 + 1; pad_status_line (brief ? 15 : 20); print_string ("Time: "); if (hours < 10) print_char (' '); print_num (hours); print_char (':'); if (global2 < 10) print_char ('0'); print_num (global2); print_char (' '); print_char ((global1 >= 12) ? 'p' : 'a'); print_char ('m'); } else { /* print score and moves */ pad_status_line (brief ? 15 : 30); print_string (brief ? "S: " : "Score: "); print_num (global1); pad_status_line (brief ? 8 : 14); print_string (brief ? "M: " : "Moves: "); print_num (global2); } /* Pad the end of the status line with spaces */ pad_status_line (0); /* Return to the lower window */ set_window (0); }/* z_show_status */ /* * z_split_window, split the screen into an upper (1) and lower (0) window. * * zargs[0] = height of upper window in screen units (V6) or #lines * */ void z_split_window (void) { split_window (zargs[0]); }/* z_split_window */ /* * z_window_size, change the width and height of a window. * * zargs[0] = window (-3 is the current one) * zargs[1] = new height in screen units * zargs[2] = new width in screen units * */ void z_window_size (void) { zword win = winarg0 (); flush_buffer (); wp[win].y_size = zargs[1]; wp[win].x_size = zargs[2]; /* Keep the cursor within the window */ if (wp[win].y_cursor > zargs[1] || wp[win].x_cursor > zargs[2]) reset_cursor (win); }/* z_window_size */ /* * z_window_style, set / clear / toggle window attributes. * * zargs[0] = window (-3 is the current one) * zargs[1] = window attribute flags * zargs[2] = operation to perform (optional, defaults to 0) * */ void z_window_style (void) { zword win = winarg0 (); zword flags = zargs[1]; flush_buffer (); /* Supply default arguments */ if (zargc < 3) zargs[2] = 0; /* Set window style */ switch (zargs[2]) { case 0: wp[win].attribute = flags; break; case 1: wp[win].attribute |= flags; break; case 2: wp[win].attribute &= ~flags; break; case 3: wp[win].attribute ^= flags; break; } if (cwin == win) update_attributes (); }/* z_window_style */ /* * get_window_colours * * Get the colours for a given window. * */ void get_window_colours (zword win, zbyte* fore, zbyte* back) { *fore = lo (wp[win].colour); *back = hi (wp[win].colour); }/* get_window_colours */ /* * get_window_font * * Get the font for a given window. * */ zword get_window_font (zword win) { zword font = wp[win].font; if (font == TEXT_FONT) { if (h_version != V6) { if (win != 0 || h_flags & FIXED_FONT_FLAG) font = FIXED_WIDTH_FONT; } else { if (wp[win].style & FIXED_WIDTH_STYLE) font = FIXED_WIDTH_FONT; } } return font; }/* get_window_font */ /* * colour_in_use * * Check if a colour is set in any window. * */ int colour_in_use (zword colour) { int max = (h_version == V6) ? 8 : 2; int i; for (i = 0; i < max; i++) { zword bg = hi (wp[i].colour); zword fg = lo (wp[i].colour); if (colour == fg || colour == bg) return 1; } return 0; }/* colour_in_use */ /* * get_current_window * * Get the currently active window. * */ zword get_current_window (void) { return cwp - wp; }/* get_current_window */ frotz-2.44/src/common/quetzal.c0000644000175000017500000003771412527045477013471 00000000000000/* quetzal.c - Saving and restoring of Quetzal files. * Written by Martin Frost * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include "frotz.h" #ifdef MSDOS_16BIT #include #define malloc(size) farmalloc (size) #define realloc(size,p) farrealloc (size,p) #define free(size) farfree (size) #define memcpy(d,s,n) _fmemcpy (d,s,n) #else #include #ifndef SEEK_SET #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #define far #endif #define get_c fgetc #define put_c fputc /* * externs */ extern int os_storyfile_seek(FILE * fp, long offset, int whence); typedef unsigned long zlong; /* * This is used only by save_quetzal. It probably should be allocated * dynamically rather than statically. */ static zword frames[STACK_SIZE/4+1]; /* * ID types. */ #define makeid(a,b,c,d) ((zlong) (((zlong)(a)<<24) | ((zlong)(b)<<16) | ((zlong)(c)<<8) | (zlong)(d))) #define ID_FORM makeid ('F','O','R','M') #define ID_IFZS makeid ('I','F','Z','S') #define ID_IFhd makeid ('I','F','h','d') #define ID_UMem makeid ('U','M','e','m') #define ID_CMem makeid ('C','M','e','m') #define ID_Stks makeid ('S','t','k','s') #define ID_ANNO makeid ('A','N','N','O') /* * Various parsing states within restoration. */ #define GOT_HEADER 0x01 #define GOT_STACK 0x02 #define GOT_MEMORY 0x04 #define GOT_NONE 0x00 #define GOT_ALL 0x07 #define GOT_ERROR 0x80 /* * Macros used to write the files. */ #define write_byte(fp,b) (put_c (b, fp) != EOF) #define write_bytx(fp,b) write_byte (fp, (b) & 0xFF) #define write_word(fp,w) \ (write_bytx (fp, (w) >> 8) && write_bytx (fp, (w))) #define write_long(fp,l) \ (write_bytx (fp, (l) >> 24) && write_bytx (fp, (l) >> 16) && \ write_bytx (fp, (l) >> 8) && write_bytx (fp, (l))) #define write_chnk(fp,id,len) \ (write_long (fp, (id)) && write_long (fp, (len))) #define write_run(fp,run) \ (write_byte (fp, 0) && write_byte (fp, (run))) /* Read one word from file; return TRUE if OK. */ static bool read_word (FILE *f, zword *result) { int a, b; if ((a = get_c (f)) == EOF) return FALSE; if ((b = get_c (f)) == EOF) return FALSE; *result = ((zword) a << 8) | (zword) b; return TRUE; } /* Read one long from file; return TRUE if OK. */ static bool read_long (FILE *f, zlong *result) { int a, b, c, d; if ((a = get_c (f)) == EOF) return FALSE; if ((b = get_c (f)) == EOF) return FALSE; if ((c = get_c (f)) == EOF) return FALSE; if ((d = get_c (f)) == EOF) return FALSE; *result = ((zlong) a << 24) | ((zlong) b << 16) | ((zlong) c << 8) | (zlong) d; return TRUE; } /* * Restore a saved game using Quetzal format. Return 2 if OK, 0 if an error * occurred before any damage was done, -1 on a fatal error. */ zword restore_quetzal (FILE *svf, FILE *stf) { zlong ifzslen, currlen, tmpl; zlong pc; zword i, tmpw; zword fatal = 0; /* Set to -1 when errors must be fatal. */ zbyte skip, progress = GOT_NONE; int x, y; /* Check it's really an `IFZS' file. */ if (!read_long (svf, &tmpl) || !read_long (svf, &ifzslen) || !read_long (svf, &currlen)) return 0; if (tmpl != ID_FORM || currlen != ID_IFZS) { print_string ("This is not a saved game file!\n"); return 0; } if ((ifzslen & 1) || ifzslen<4) /* Sanity checks. */ return 0; ifzslen -= 4; /* Read each chunk and process it. */ while (ifzslen > 0) { /* Read chunk header. */ if (ifzslen < 8) /* Couldn't contain a chunk. */ return 0; if (!read_long (svf, &tmpl) || !read_long (svf, &currlen)) return 0; ifzslen -= 8; /* Reduce remaining by size of header. */ /* Handle chunk body. */ if (ifzslen < currlen) /* Chunk goes past EOF?! */ return 0; skip = currlen & 1; ifzslen -= currlen + (zlong) skip; switch (tmpl) { /* `IFhd' header chunk; must be first in file. */ case ID_IFhd: if (progress & GOT_HEADER) { print_string ("Save file has two IFZS chunks!\n"); return fatal; } progress |= GOT_HEADER; if (currlen < 13 || !read_word (svf, &tmpw)) return fatal; if (tmpw != h_release) progress = GOT_ERROR; for (i=H_SERIAL; i STACK_SIZE) { print_string ("Save-file has too much stack (and I can't cope).\n"); return fatal; } currlen -= 8; if (currlen < tmpw*2) return fatal; for (i=0; i 0; currlen -= 8, ++frame_count) { if (currlen < 8) return fatal; if (sp - stack < 4) /* No space for frame. */ { print_string ("Save-file has too much stack (and I can't cope).\n"); return fatal; } /* Read PC, procedure flag and formal param count. */ if (!read_long (svf, &tmpl)) return fatal; y = (int) (tmpl & 0x0F); /* Number of formals. */ tmpw = y << 8; /* Read result variable. */ if ((x = get_c (svf)) == EOF) return fatal; /* Check the procedure flag... */ if (tmpl & 0x10) { tmpw |= 0x1000; /* It's a procedure. */ tmpl >>= 8; /* Shift to get PC value. */ } else { /* Functions have type 0, so no need to or anything. */ tmpl >>= 8; /* Shift to get PC value. */ --tmpl; /* Point at result byte. */ /* Sanity check on result variable... */ if (zmp[tmpl] != (zbyte) x) { print_string ("Save-file has wrong variable number on stack (possibly wrong game version?)\n"); return fatal; } } *--sp = (zword) (tmpl >> 9); /* High part of PC */ *--sp = (zword) (tmpl & 0x1FF); /* Low part of PC */ *--sp = (zword) (fp - stack - 1); /* FP */ /* Read and process argument mask. */ if ((x = get_c (svf)) == EOF) return fatal; ++x; /* Should now be a power of 2 */ for (i=0; i<8; ++i) if (x & (1< 0; --currlen) { if ((x = get_c (svf)) == EOF) return fatal; if (x == 0) /* Start run. */ { /* Check for bogus run. */ if (currlen < 2) { print_string ("File contains bogus `CMem' chunk.\n"); for (; currlen > 0; --currlen) (void) get_c (svf); /* Skip rest. */ currlen = 1; i = 0xFFFF; break; /* Keep going; may be a `UMem' too. */ } /* Copy story file to memory during the run. */ --currlen; if ((x = get_c (svf)) == EOF) return fatal; for (; x >= 0 && i h_dynamic_size) { print_string ("warning: `CMem' chunk too long!\n"); for (; currlen > 1; --currlen) (void) get_c (svf); /* Skip rest. */ break; /* Keep going; there may be a `UMem' too. */ } } /* If chunk is short, assume a run. */ for (; i 0) { for (; j > 0x100; j -= 0x100) { if (!write_run (svf, 0xFF)) return 0; cmemlen += 2; } if (!write_run (svf, j-1)) return 0; cmemlen += 2; j = 0; } /* Any runs are now written. Write this (nonzero) byte. */ if (!write_byte (svf, (zbyte) c)) return 0; ++cmemlen; } } /* * Reached end of dynamic memory. We ignore any unwritten run there may be * at this point. */ if (cmemlen & 1) /* Chunk length must be even. */ if (!write_byte (svf, 0)) return 0; /* Write `Stks' chunk. You are not expected to understand this. ;) */ if ((stkspos = ftell (svf)) < 0) return 0; if (!write_chnk (svf, ID_Stks, 0)) return 0; /* * We construct a list of frame indices, most recent first, in `frames'. * These indices are the offsets into the `stack' array of the word before * the first word pushed in each frame. */ frames[0] = sp - stack; /* The frame we'd get by doing a call now. */ for (i = fp - stack + 4, n=0; i < STACK_SIZE+4; i = stack[i-3] + 5) frames[++n] = i; /* * All versions other than V6 can use evaluation stack outside a function * context. We write a faked stack frame (most fields zero) to cater for * this. */ if (h_version != V6) { for (i=0; i<6; ++i) if (!write_byte (svf, 0)) return 0; nstk = STACK_SIZE - frames[n]; if (!write_word (svf, nstk)) return 0; for (j=STACK_SIZE-1; j >= frames[n]; --j) if (!write_word (svf, stack[j])) return 0; stkslen = 8 + 2*nstk; } /* Write out the rest of the stack frames. */ for (i=n; i>0; --i) { p = stack + frames[i] - 4; /* Points to call frame. */ nvars = (p[0] & 0x0F00) >> 8; nargs = p[0] & 0x00FF; nstk = frames[i] - frames[i-1] - nvars - 4; pc = ((zlong) p[3] << 9) | p[2]; switch (p[0] & 0xF000) /* Check type of call. */ { case 0x0000: /* Function. */ var = zmp[pc]; pc = ((pc + 1) << 8) | nvars; break; case 0x1000: /* Procedure. */ var = 0; pc = (pc << 8) | 0x10 | nvars; /* Set procedure flag. */ break; /* case 0x2000: */ default: runtime_error (ERR_SAVE_IN_INTER); return 0; } if (nargs != 0) nargs = (1 << nargs) - 1; /* Make args into bitmap. */ /* Write the main part of the frame... */ if (!write_long (svf, pc) || !write_byte (svf, var) || !write_byte (svf, nargs) || !write_word (svf, nstk)) return 0; /* Write the variables and eval stack. */ for (j=0, --p; j #include "frotz.h" extern void stream_char (zchar); extern void stream_word (const zchar *); extern void stream_new_line (void); static zchar buffer[TEXT_BUFFER_SIZE]; static int bufpos = 0; static zchar prev_c = 0; /* * flush_buffer * * Copy the contents of the text buffer to the output streams. * */ void flush_buffer (void) { static bool locked = FALSE; /* Make sure we stop when flush_buffer is called from flush_buffer. Note that this is difficult to avoid as we might print a newline during flush_buffer, which might cause a newline interrupt, that might execute any arbitrary opcode, which might flush the buffer. */ if (locked || bufpos == 0) return; /* Send the buffer to the output streams */ buffer[bufpos] = 0; locked = TRUE; stream_word (buffer); #ifdef SPEECH_OUTPUT os_speech_output(buffer); #endif locked = FALSE; /* Reset the buffer */ bufpos = 0; prev_c = 0; }/* flush_buffer */ /* * print_char * * High level output function. * */ void print_char (zchar c) { static bool flag = FALSE; if (message || ostream_memory || enable_buffering) { if (!flag) { /* Characters 0 and ZC_RETURN are special cases */ if (c == ZC_RETURN) { new_line (); return; } if (c == 0) return; /* Flush the buffer before a whitespace or after a hyphen */ if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || (prev_c == '-' && c != '-')) flush_buffer (); /* Set the flag if this is part one of a style or font change */ if (c == ZC_NEW_FONT || c == ZC_NEW_STYLE) flag = TRUE; /* Remember the current character code */ prev_c = c; } else flag = FALSE; /* Insert the character into the buffer */ buffer[bufpos++] = c; if (bufpos == TEXT_BUFFER_SIZE) runtime_error (ERR_TEXT_BUF_OVF); } else stream_char (c); }/* print_char */ /* * new_line * * High level newline function. * */ void new_line (void) { flush_buffer (); stream_new_line (); }/* new_line */ /* * init_buffer * * Initialize buffer variables. * */ void init_buffer(void) { memset(buffer, 0, sizeof (zchar) * TEXT_BUFFER_SIZE); bufpos = 0; prev_c = 0; } frotz-2.44/src/common/setup.h0000644000175000017500000000333712527045477013143 00000000000000/* * Various status thingies for the interpreter and interface. * */ typedef struct frotz_setup_struct { int attribute_assignment; /* done */ int attribute_testing; /* done */ int context_lines; /* done */ int object_locating; /* done */ int object_movement; /* done */ int left_margin; /* done */ int right_margin; /* done */ int ignore_errors; /* done */ int interpreter_number; /* Just dumb frotz now */ int piracy; /* done */ int undo_slots; /* done */ int expand_abbreviations; /* done */ int script_cols; /* done */ int sound; /* done */ int err_report_mode; /* done */ char *story_file; char *story_name; char *story_base; char *script_name; char *command_name; char *save_name; char *aux_name; char *story_path; char *zcode_path; } f_setup_t; extern f_setup_t f_setup; /*** Story file header data ***/ /* typedef struct zcode_header_struct { zbyte version; zbyte config; zword release; zword resident_size; zword start_pc; zword dictionary; zword objects; zword globals; zword dynamic_size; zword flags; zbyte serial[6]; zword abbreviations; zword file_size; zword checksum; zbyte interpreter_number; zbyte interpreter_version; zbyte screen_rows; zbyte screen_cols; zword screen_width; zword screen_height; zbyte font_height; zbyte font_width; zword functions_offset; zword strings_offset; zbyte default_background; zbyte default_foreground; zword terminating_keys; zword line_width; zbyte standard_high; zbyte standard_low; zword alphabet; zword extension_table; zbyte user_name[8]; zword hx_table_size; zword hx_mouse_x; zword hx_mouse_y; zword hx_unicode_table; } z_header_t; extern z_header_t z_header; */ frotz-2.44/src/common/main.c0000644000175000017500000000663612527045477012727 00000000000000/* main.c - Frotz V2.40 main function * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * This is an interpreter for Infocom V1 to V6 games. It also supports * the recently defined V7 and V8 games. * */ #include "frotz.h" #ifndef MSDOS_16BIT #define cdecl #endif extern void interpret (void); extern void init_memory (void); extern void init_undo (void); extern void reset_memory (void); /* Story file name, id number and size */ char *story_name = 0; enum story story_id = UNKNOWN; long story_size = 0; /* Story file header data */ zbyte h_version = 0; zbyte h_config = 0; zword h_release = 0; zword h_resident_size = 0; zword h_start_pc = 0; zword h_dictionary = 0; zword h_objects = 0; zword h_globals = 0; zword h_dynamic_size = 0; zword h_flags = 0; zbyte h_serial[6] = { 0, 0, 0, 0, 0, 0 }; zword h_abbreviations = 0; zword h_file_size = 0; zword h_checksum = 0; zbyte h_interpreter_number = 0; zbyte h_interpreter_version = 0; zbyte h_screen_rows = 0; zbyte h_screen_cols = 0; zword h_screen_width = 0; zword h_screen_height = 0; zbyte h_font_height = 1; zbyte h_font_width = 1; zword h_functions_offset = 0; zword h_strings_offset = 0; zbyte h_default_background = 0; zbyte h_default_foreground = 0; zword h_terminating_keys = 0; zword h_line_width = 0; zbyte h_standard_high = 1; zbyte h_standard_low = 0; zword h_alphabet = 0; zword h_extension_table = 0; zbyte h_user_name[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; zword hx_table_size = 0; zword hx_mouse_x = 0; zword hx_mouse_y = 0; zword hx_unicode_table = 0; /* Stack data */ zword stack[STACK_SIZE]; zword *sp = 0; zword *fp = 0; zword frame_count = 0; /* IO streams */ bool ostream_screen = TRUE; bool ostream_script = FALSE; bool ostream_memory = FALSE; bool ostream_record = FALSE; bool istream_replay = FALSE; bool message = FALSE; /* Current window and mouse data */ int cwin = 0; int mwin = 0; int mouse_y = 0; int mouse_x = 0; /* Window attributes */ bool enable_wrapping = FALSE; bool enable_scripting = FALSE; bool enable_scrolling = FALSE; bool enable_buffering = FALSE; int option_sound = 1; char *option_zcode_path; /* Size of memory to reserve (in bytes) */ long reserve_mem = 0; /* * z_piracy, branch if the story file is a legal copy. * * no zargs used * */ void z_piracy (void) { branch (!f_setup.piracy); }/* z_piracy */ /* * main * * Prepare and run the game. * */ int cdecl main (int argc, char *argv[]) { os_init_setup (); os_process_arguments (argc, argv); init_buffer (); init_err (); init_memory (); init_process (); init_sound (); os_init_screen (); init_undo (); z_restart (); interpret (); reset_memory (); os_reset_screen (); return 0; }/* main */ frotz-2.44/src/common/redirect.c0000644000175000017500000000636212527045477013600 00000000000000/* redirect.c - Output redirection to Z-machine memory * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" #define MAX_NESTING 16 extern zword get_max_width (zword); static int depth = -1; static struct { zword xsize; zword table; zword width; zword total; } redirect[MAX_NESTING]; /* * memory_open * * Begin output redirection to the memory of the Z-machine. * */ void memory_open (zword table, zword xsize, bool buffering) { if (++depth < MAX_NESTING) { if (!buffering) xsize = 0xffff; else { if ((short) xsize >= 0) xsize = get_max_width (xsize); else xsize = -xsize; } storew (table, 0); redirect[depth].table = table; redirect[depth].width = 0; redirect[depth].total = 0; redirect[depth].xsize = xsize; ostream_memory = TRUE; } else runtime_error (ERR_STR3_NESTING); }/* memory_open */ /* * memory_new_line * * Redirect a newline to the memory of the Z-machine. * */ void memory_new_line (void) { zword size; zword addr; redirect[depth].total += redirect[depth].width; redirect[depth].width = 0; addr = redirect[depth].table; LOW_WORD (addr, size) addr += 2; if (redirect[depth].xsize != 0xffff) { redirect[depth].table = addr + size; size = 0; } else storeb ((zword) (addr + (size++)), 13); storew (redirect[depth].table, size); }/* memory_new_line */ /* * memory_word * * Redirect a string of characters to the memory of the Z-machine. * */ void memory_word (const zchar *s) { zword size; zword addr; zchar c; if (h_version == V6) { int width = os_string_width (s); if (redirect[depth].xsize != 0xffff) if (redirect[depth].width + width > redirect[depth].xsize) { if (*s == ' ' || *s == ZC_INDENT || *s == ZC_GAP) width = os_string_width (++s); memory_new_line (); } redirect[depth].width += width; } addr = redirect[depth].table; LOW_WORD (addr, size) addr += 2; while ((c = *s++) != 0) storeb ((zword) (addr + (size++)), translate_to_zscii (c)); storew (redirect[depth].table, size); }/* memory_word */ /* * memory_close * * End of output redirection. * */ void memory_close (void) { if (depth >= 0) { if (redirect[depth].xsize != 0xffff) memory_new_line (); if (h_version == V6) { h_line_width = (redirect[depth].xsize != 0xffff) ? redirect[depth].total : redirect[depth].width; SET_WORD (H_LINE_WIDTH, h_line_width) } if (depth == 0) ostream_memory = FALSE; depth--; } }/* memory_close */ frotz-2.44/src/common/stream.c0000644000175000017500000001626112527045477013271 00000000000000/* stream.c - IO stream implementation * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" extern bool handle_hot_key (zchar); extern bool validate_click (void); extern void replay_open (void); extern void replay_close (void); extern void memory_open (zword, zword, bool); extern void memory_close (void); extern void record_open (void); extern void record_close (void); extern void script_open (void); extern void script_close (void); extern void memory_word (const zchar *); extern void memory_new_line (void); extern void record_write_key (zchar); extern void record_write_input (const zchar *, zchar); extern void script_char (zchar); extern void script_word (const zchar *); extern void script_new_line (void); extern void script_write_input (const zchar *, zchar); extern void script_erase_input (const zchar *); extern void script_mssg_on (void); extern void script_mssg_off (void); extern void screen_char (zchar); extern void screen_word (const zchar *); extern void screen_new_line (void); extern void screen_write_input (const zchar *, zchar); extern void screen_erase_input (const zchar *); extern void screen_mssg_on (void); extern void screen_mssg_off (void); extern zchar replay_read_key (void); extern zchar replay_read_input (zchar *); extern zchar console_read_key (zword); extern zchar console_read_input (int, zchar *, zword, bool); extern int direct_call (zword); /* * stream_mssg_on * * Start printing a "debugging" message. * */ void stream_mssg_on (void) { flush_buffer (); if (ostream_screen) screen_mssg_on (); if (ostream_script && enable_scripting) script_mssg_on (); message = TRUE; }/* stream_mssg_on */ /* * stream_mssg_off * * Stop printing a "debugging" message. * */ void stream_mssg_off (void) { flush_buffer (); if (ostream_screen) screen_mssg_off (); if (ostream_script && enable_scripting) script_mssg_off (); message = FALSE; }/* stream_mssg_off */ /* * z_output_stream, open or close an output stream. * * zargs[0] = stream to open (positive) or close (negative) * zargs[1] = address to redirect output to (stream 3 only) * zargs[2] = width of redirected output (stream 3 only, optional) * */ void z_output_stream (void) { flush_buffer (); switch ((short) zargs[0]) { case 1: ostream_screen = TRUE; break; case -1: ostream_screen = FALSE; break; case 2: if (!ostream_script) script_open (); break; case -2: if (ostream_script) script_close (); break; case 3: memory_open (zargs[1], zargs[2], zargc >= 3); break; case -3: memory_close (); break; case 4: if (!ostream_record) record_open (); break; case -4: if (ostream_record) record_close (); break; } }/* z_output_stream */ /* * stream_char * * Send a single character to the output stream. * */ void stream_char (zchar c) { if (ostream_screen) screen_char (c); if (ostream_script && enable_scripting) script_char (c); }/* stream_char */ /* * stream_word * * Send a string of characters to the output streams. * */ void stream_word (const zchar *s) { if (ostream_memory && !message) memory_word (s); else { if (ostream_screen) screen_word (s); if (ostream_script && enable_scripting) script_word (s); } }/* stream_word */ /* * stream_new_line * * Send a newline to the output streams. * */ void stream_new_line (void) { if (ostream_memory && !message) memory_new_line (); else { if (ostream_screen) screen_new_line (); if (ostream_script && enable_scripting) script_new_line (); } }/* stream_new_line */ /* * z_input_stream, select an input stream. * * zargs[0] = input stream to be selected * */ void z_input_stream (void) { flush_buffer (); if (zargs[0] == 0 && istream_replay) replay_close (); if (zargs[0] == 1 && !istream_replay) replay_open (); }/* z_input_stream */ /* * stream_read_key * * Read a single keystroke from the current input stream. * */ zchar stream_read_key ( zword timeout, zword routine, bool hot_keys ) { zchar key = ZC_BAD; flush_buffer (); /* Read key from current input stream */ continue_input: do { if (istream_replay) key = replay_read_key (); else key = console_read_key (timeout); } while (key == ZC_BAD); /* Verify mouse clicks */ if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK) if (!validate_click ()) goto continue_input; /* Copy key to the command file */ if (ostream_record && !istream_replay) record_write_key (key); /* Handle timeouts */ if (key == ZC_TIME_OUT) if (direct_call (routine) == 0) goto continue_input; /* Handle hot keys */ if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) { if (h_version == V4 && key == ZC_HKEY_UNDO) goto continue_input; if (!handle_hot_key (key)) goto continue_input; return ZC_BAD; } /* Return key */ return key; }/* stream_read_key */ /* * stream_read_input * * Read a line of input from the current input stream. * */ zchar stream_read_input ( int max, zchar *buf, zword timeout, zword routine, bool hot_keys, bool no_scripting ) { zchar key = ZC_BAD; flush_buffer (); /* Remove initial input from the transcript file or from the screen */ if (ostream_script && enable_scripting && !no_scripting) script_erase_input (buf); if (istream_replay) screen_erase_input (buf); /* Read input line from current input stream */ continue_input: do { if (istream_replay) key = replay_read_input (buf); else key = console_read_input (max, buf, timeout, key != ZC_BAD); } while (key == ZC_BAD); /* Verify mouse clicks */ if (key == ZC_SINGLE_CLICK || key == ZC_DOUBLE_CLICK) if (!validate_click ()) goto continue_input; /* Copy input line to the command file */ if (ostream_record && !istream_replay) record_write_input (buf, key); /* Handle timeouts */ if (key == ZC_TIME_OUT) if (direct_call (routine) == 0) goto continue_input; /* Handle hot keys */ if (hot_keys && key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) { if (!handle_hot_key (key)) goto continue_input; return ZC_BAD; } /* Copy input line to transcript file or to the screen */ if (ostream_script && enable_scripting && !no_scripting) script_write_input (buf, key); if (istream_replay) screen_write_input (buf, key); /* Return terminating key */ return key; }/* stream_read_input */ frotz-2.44/src/common/object.c0000644000175000017500000004366512527045477013254 00000000000000/* object.c - Object manipulation opcodes * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" #define MAX_OBJECT 2000 #define O1_PARENT 4 #define O1_SIBLING 5 #define O1_CHILD 6 #define O1_PROPERTY_OFFSET 7 #define O1_SIZE 9 #define O4_PARENT 6 #define O4_SIBLING 8 #define O4_CHILD 10 #define O4_PROPERTY_OFFSET 12 #define O4_SIZE 14 /* * object_address * * Calculate the address of an object. * */ static zword object_address (zword obj) { /* zchar obj_num[10]; */ /* Check object number */ if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) { print_string("@Attempt to address illegal object "); print_num(obj); print_string(". This is normally fatal."); new_line(); runtime_error (ERR_ILL_OBJ); } /* Return object address */ if (h_version <= V3) return h_objects + ((obj - 1) * O1_SIZE + 62); else return h_objects + ((obj - 1) * O4_SIZE + 126); }/* object_address */ /* * object_name * * Return the address of the given object's name. * */ zword object_name (zword object) { zword obj_addr; zword name_addr; obj_addr = object_address (object); /* The object name address is found at the start of the properties */ if (h_version <= V3) obj_addr += O1_PROPERTY_OFFSET; else obj_addr += O4_PROPERTY_OFFSET; LOW_WORD (obj_addr, name_addr) return name_addr; }/* object_name */ /* * first_property * * Calculate the start address of the property list associated with * an object. * */ static zword first_property (zword obj) { zword prop_addr; zbyte size; /* Fetch address of object name */ prop_addr = object_name (obj); /* Get length of object name */ LOW_BYTE (prop_addr, size) /* Add name length to pointer */ return prop_addr + 1 + 2 * size; }/* first_property */ /* * next_property * * Calculate the address of the next property in a property list. * */ static zword next_property (zword prop_addr) { zbyte value; /* Load the current property id */ LOW_BYTE (prop_addr, value) prop_addr++; /* Calculate the length of this property */ if (h_version <= V3) value >>= 5; else if (!(value & 0x80)) value >>= 6; else { LOW_BYTE (prop_addr, value) value &= 0x3f; if (value == 0) value = 64; /* demanded by Spec 1.0 */ } /* Add property length to current property pointer */ return prop_addr + value + 1; }/* next_property */ /* * unlink_object * * Unlink an object from its parent and siblings. * */ static void unlink_object (zword object) { zword obj_addr; zword parent_addr; zword sibling_addr; if (object == 0) { runtime_error (ERR_REMOVE_OBJECT_0); return; } obj_addr = object_address (object); if (h_version <= V3) { zbyte parent; zbyte younger_sibling; zbyte older_sibling; zbyte zero = 0; /* Get parent of object, and return if no parent */ obj_addr += O1_PARENT; LOW_BYTE (obj_addr, parent) if (!parent) return; /* Get (older) sibling of object and set both parent and sibling pointers to 0 */ SET_BYTE (obj_addr, zero) obj_addr += O1_SIBLING - O1_PARENT; LOW_BYTE (obj_addr, older_sibling) SET_BYTE (obj_addr, zero) /* Get first child of parent (the youngest sibling of the object) */ parent_addr = object_address (parent) + O1_CHILD; LOW_BYTE (parent_addr, younger_sibling) /* Remove object from the list of siblings */ if (younger_sibling == object) SET_BYTE (parent_addr, older_sibling) else { do { sibling_addr = object_address (younger_sibling) + O1_SIBLING; LOW_BYTE (sibling_addr, younger_sibling) } while (younger_sibling != object); SET_BYTE (sibling_addr, older_sibling) } } else { zword parent; zword younger_sibling; zword older_sibling; zword zero = 0; /* Get parent of object, and return if no parent */ obj_addr += O4_PARENT; LOW_WORD (obj_addr, parent) if (!parent) return; /* Get (older) sibling of object and set both parent and sibling pointers to 0 */ SET_WORD (obj_addr, zero) obj_addr += O4_SIBLING - O4_PARENT; LOW_WORD (obj_addr, older_sibling) SET_WORD (obj_addr, zero) /* Get first child of parent (the youngest sibling of the object) */ parent_addr = object_address (parent) + O4_CHILD; LOW_WORD (parent_addr, younger_sibling) /* Remove object from the list of siblings */ if (younger_sibling == object) SET_WORD (parent_addr, older_sibling) else { do { sibling_addr = object_address (younger_sibling) + O4_SIBLING; LOW_WORD (sibling_addr, younger_sibling) } while (younger_sibling != object); SET_WORD (sibling_addr, older_sibling) } } }/* unlink_object */ /* * z_clear_attr, clear an object attribute. * * zargs[0] = object * zargs[1] = number of attribute to be cleared * */ void z_clear_attr (void) { zword obj_addr; zbyte value; if (story_id == SHERLOCK) if (zargs[1] == 48) return; if (zargs[1] > ((h_version <= V3) ? 31 : 47)) runtime_error (ERR_ILL_ATTR); /* If we are monitoring attribute assignment display a short note */ if (f_setup.attribute_assignment) { stream_mssg_on (); print_string ("@clear_attr "); print_object (zargs[0]); print_string (" "); print_num (zargs[1]); stream_mssg_off (); } if (zargs[0] == 0) { runtime_error (ERR_CLEAR_ATTR_0); return; } /* Get attribute address */ obj_addr = object_address (zargs[0]) + zargs[1] / 8; /* Clear attribute bit */ LOW_BYTE (obj_addr, value) value &= ~(0x80 >> (zargs[1] & 7)); SET_BYTE (obj_addr, value) }/* z_clear_attr */ /* * z_jin, branch if the first object is inside the second. * * zargs[0] = first object * zargs[1] = second object * */ void z_jin (void) { zword obj_addr; /* If we are monitoring object locating display a short note */ if (f_setup.object_locating) { stream_mssg_on (); print_string ("@jin "); print_object (zargs[0]); print_string (" "); print_object (zargs[1]); stream_mssg_off (); } if (zargs[0] == 0) { runtime_error (ERR_JIN_0); branch (0 == zargs[1]); return; } obj_addr = object_address (zargs[0]); if (h_version <= V3) { zbyte parent; /* Get parent id from object */ obj_addr += O1_PARENT; LOW_BYTE (obj_addr, parent) /* Branch if the parent is obj2 */ branch (parent == zargs[1]); } else { zword parent; /* Get parent id from object */ obj_addr += O4_PARENT; LOW_WORD (obj_addr, parent) /* Branch if the parent is obj2 */ branch (parent == zargs[1]); } }/* z_jin */ /* * z_get_child, store the child of an object. * * zargs[0] = object * */ void z_get_child (void) { zword obj_addr; /* If we are monitoring object locating display a short note */ if (f_setup.object_locating) { stream_mssg_on (); print_string ("@get_child "); print_object (zargs[0]); stream_mssg_off (); } if (zargs[0] == 0) { runtime_error (ERR_GET_CHILD_0); store (0); branch (FALSE); return; } obj_addr = object_address (zargs[0]); if (h_version <= V3) { zbyte child; /* Get child id from object */ obj_addr += O1_CHILD; LOW_BYTE (obj_addr, child) /* Store child id and branch */ store (child); branch (child); } else { zword child; /* Get child id from object */ obj_addr += O4_CHILD; LOW_WORD (obj_addr, child) /* Store child id and branch */ store (child); branch (child); } }/* z_get_child */ /* * z_get_next_prop, store the number of the first or next property. * * zargs[0] = object * zargs[1] = address of current property (0 gets the first property) * */ void z_get_next_prop (void) { zword prop_addr; zbyte value; zbyte mask; if (zargs[0] == 0) { runtime_error (ERR_GET_NEXT_PROP_0); store (0); return; } /* Property id is in bottom five (six) bits */ mask = (h_version <= V3) ? 0x1f : 0x3f; /* Load address of first property */ prop_addr = first_property (zargs[0]); if (zargs[1] != 0) { /* Scan down the property list */ do { LOW_BYTE (prop_addr, value) prop_addr = next_property (prop_addr); } while ((value & mask) > zargs[1]); /* Exit if the property does not exist */ if ((value & mask) != zargs[1]) runtime_error (ERR_NO_PROP); } /* Return the property id */ LOW_BYTE (prop_addr, value) store ((zword) (value & mask)); }/* z_get_next_prop */ /* * z_get_parent, store the parent of an object. * * zargs[0] = object * */ void z_get_parent (void) { zword obj_addr; /* If we are monitoring object locating display a short note */ if (f_setup.object_locating) { stream_mssg_on (); print_string ("@get_parent "); print_object (zargs[0]); stream_mssg_off (); } if (zargs[0] == 0) { runtime_error (ERR_GET_PARENT_0); store (0); return; } obj_addr = object_address (zargs[0]); if (h_version <= V3) { zbyte parent; /* Get parent id from object */ obj_addr += O1_PARENT; LOW_BYTE (obj_addr, parent) /* Store parent */ store (parent); } else { zword parent; /* Get parent id from object */ obj_addr += O4_PARENT; LOW_WORD (obj_addr, parent) /* Store parent */ store (parent); } }/* z_get_parent */ /* * z_get_prop, store the value of an object property. * * zargs[0] = object * zargs[1] = number of property to be examined * */ void z_get_prop (void) { zword prop_addr; zword wprop_val; zbyte bprop_val; zbyte value; zbyte mask; if (zargs[0] == 0) { runtime_error (ERR_GET_PROP_0); store (0); return; } /* Property id is in bottom five (six) bits */ mask = (h_version <= V3) ? 0x1f : 0x3f; /* Load address of first property */ prop_addr = first_property (zargs[0]); /* Scan down the property list */ for (;;) { LOW_BYTE (prop_addr, value) if ((value & mask) <= zargs[1]) break; prop_addr = next_property (prop_addr); } if ((value & mask) == zargs[1]) { /* property found */ /* Load property (byte or word sized) */ prop_addr++; if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) { LOW_BYTE (prop_addr, bprop_val) wprop_val = bprop_val; } else LOW_WORD (prop_addr, wprop_val) } else { /* property not found */ /* Load default value */ prop_addr = h_objects + 2 * (zargs[1] - 1); LOW_WORD (prop_addr, wprop_val) } /* Store the property value */ store (wprop_val); }/* z_get_prop */ /* * z_get_prop_addr, store the address of an object property. * * zargs[0] = object * zargs[1] = number of property to be examined * */ void z_get_prop_addr (void) { zword prop_addr; zbyte value; zbyte mask; if (zargs[0] == 0) { runtime_error (ERR_GET_PROP_ADDR_0); store (0); return; } if (story_id == BEYOND_ZORK) if (zargs[0] > MAX_OBJECT) { store (0); return; } /* Property id is in bottom five (six) bits */ mask = (h_version <= V3) ? 0x1f : 0x3f; /* Load address of first property */ prop_addr = first_property (zargs[0]); /* Scan down the property list */ for (;;) { LOW_BYTE (prop_addr, value) if ((value & mask) <= zargs[1]) break; prop_addr = next_property (prop_addr); } /* Calculate the property address or return zero */ if ((value & mask) == zargs[1]) { if (h_version >= V4 && (value & 0x80)) prop_addr++; store ((zword) (prop_addr + 1)); } else store (0); }/* z_get_prop_addr */ /* * z_get_prop_len, store the length of an object property. * * zargs[0] = address of property to be examined * */ void z_get_prop_len (void) { zword addr; zbyte value; /* Back up the property pointer to the property id */ addr = zargs[0] - 1; LOW_BYTE (addr, value) /* Calculate length of property */ if (h_version <= V3) value = (value >> 5) + 1; else if (!(value & 0x80)) value = (value >> 6) + 1; else { value &= 0x3f; if (value == 0) value = 64; /* demanded by Spec 1.0 */ } /* Store length of property */ store (value); }/* z_get_prop_len */ /* * z_get_sibling, store the sibling of an object. * * zargs[0] = object * */ void z_get_sibling (void) { zword obj_addr; if (zargs[0] == 0) { runtime_error (ERR_GET_SIBLING_0); store (0); branch (FALSE); return; } obj_addr = object_address (zargs[0]); if (h_version <= V3) { zbyte sibling; /* Get sibling id from object */ obj_addr += O1_SIBLING; LOW_BYTE (obj_addr, sibling) /* Store sibling and branch */ store (sibling); branch (sibling); } else { zword sibling; /* Get sibling id from object */ obj_addr += O4_SIBLING; LOW_WORD (obj_addr, sibling) /* Store sibling and branch */ store (sibling); branch (sibling); } }/* z_get_sibling */ /* * z_insert_obj, make an object the first child of another object. * * zargs[0] = object to be moved * zargs[1] = destination object * */ void z_insert_obj (void) { zword obj1 = zargs[0]; zword obj2 = zargs[1]; zword obj1_addr; zword obj2_addr; /* If we are monitoring object movements display a short note */ if (f_setup.object_movement) { stream_mssg_on (); print_string ("@move_obj "); print_object (obj1); print_string (" "); print_object (obj2); stream_mssg_off (); } if (obj1 == 0) { runtime_error (ERR_MOVE_OBJECT_0); return; } if (obj2 == 0) { runtime_error (ERR_MOVE_OBJECT_TO_0); return; } /* Get addresses of both objects */ obj1_addr = object_address (obj1); obj2_addr = object_address (obj2); /* Remove object 1 from current parent */ unlink_object (obj1); /* Make object 1 first child of object 2 */ if (h_version <= V3) { zbyte child; obj1_addr += O1_PARENT; SET_BYTE (obj1_addr, obj2) obj2_addr += O1_CHILD; LOW_BYTE (obj2_addr, child) SET_BYTE (obj2_addr, obj1) obj1_addr += O1_SIBLING - O1_PARENT; SET_BYTE (obj1_addr, child) } else { zword child; obj1_addr += O4_PARENT; SET_WORD (obj1_addr, obj2) obj2_addr += O4_CHILD; LOW_WORD (obj2_addr, child) SET_WORD (obj2_addr, obj1) obj1_addr += O4_SIBLING - O4_PARENT; SET_WORD (obj1_addr, child) } }/* z_insert_obj */ /* * z_put_prop, set the value of an object property. * * zargs[0] = object * zargs[1] = number of property to set * zargs[2] = value to set property to * */ void z_put_prop (void) { zword prop_addr; zword value; zbyte mask; if (zargs[0] == 0) { runtime_error (ERR_PUT_PROP_0); return; } /* Property id is in bottom five or six bits */ mask = (h_version <= V3) ? 0x1f : 0x3f; /* Load address of first property */ prop_addr = first_property (zargs[0]); /* Scan down the property list */ for (;;) { LOW_BYTE (prop_addr, value) if ((value & mask) <= zargs[1]) break; prop_addr = next_property (prop_addr); } /* Exit if the property does not exist */ if ((value & mask) != zargs[1]) runtime_error (ERR_NO_PROP); /* Store the new property value (byte or word sized) */ prop_addr++; if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) { zbyte v = zargs[2]; SET_BYTE (prop_addr, v) } else { zword v = zargs[2]; SET_WORD (prop_addr, v) } }/* z_put_prop */ /* * z_remove_obj, unlink an object from its parent and siblings. * * zargs[0] = object * */ void z_remove_obj (void) { /* If we are monitoring object movements display a short note */ if (f_setup.object_movement) { stream_mssg_on (); print_string ("@remove_obj "); print_object (zargs[0]); stream_mssg_off (); } /* Call unlink_object to do the job */ unlink_object (zargs[0]); }/* z_remove_obj */ /* * z_set_attr, set an object attribute. * * zargs[0] = object * zargs[1] = number of attribute to set * */ void z_set_attr (void) { zword obj_addr; zbyte value; if (story_id == SHERLOCK) if (zargs[1] == 48) return; if (zargs[1] > ((h_version <= V3) ? 31 : 47)) runtime_error (ERR_ILL_ATTR); /* If we are monitoring attribute assignment display a short note */ if (f_setup.attribute_assignment) { stream_mssg_on (); print_string ("@set_attr "); print_object (zargs[0]); print_string (" "); print_num (zargs[1]); stream_mssg_off (); } if (zargs[0] == 0) { runtime_error (ERR_SET_ATTR_0); return; } /* Get attribute address */ obj_addr = object_address (zargs[0]) + zargs[1] / 8; /* Load attribute byte */ LOW_BYTE (obj_addr, value) /* Set attribute bit */ value |= 0x80 >> (zargs[1] & 7); /* Store attribute byte */ SET_BYTE (obj_addr, value) }/* z_set_attr */ /* * z_test_attr, branch if an object attribute is set. * * zargs[0] = object * zargs[1] = number of attribute to test * */ void z_test_attr (void) { zword obj_addr; zbyte value; if (zargs[1] > ((h_version <= V3) ? 31 : 47)) runtime_error (ERR_ILL_ATTR); /* If we are monitoring attribute testing display a short note */ if (f_setup.attribute_testing) { stream_mssg_on (); print_string ("@test_attr "); print_object (zargs[0]); print_string (" "); print_num (zargs[1]); stream_mssg_off (); } if (zargs[0] == 0) { runtime_error (ERR_TEST_ATTR_0); branch (FALSE); return; } /* Get attribute address */ obj_addr = object_address (zargs[0]) + zargs[1] / 8; /* Load attribute byte */ LOW_BYTE (obj_addr, value) /* Test attribute */ branch (value & (0x80 >> (zargs[1] & 7))); }/* z_test_attr */ frotz-2.44/src/common/hotkey.c0000644000175000017500000001162012527045477013273 00000000000000/* hotkey.c - Hot key functions * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" extern int restore_undo (void); extern int read_number (void); extern bool read_yes_or_no (const char *); extern void replay_open (void); extern void replay_close (void); extern void record_open (void); extern void record_close (void); extern void seed_random (int); /* * hot_key_debugging * * ...allows user to toggle cheating options on/off. * */ static bool hot_key_debugging (void) { print_string ("Debugging options\n"); f_setup.attribute_assignment = read_yes_or_no ("Watch attribute assignment"); f_setup.attribute_testing = read_yes_or_no ("Watch attribute testing"); f_setup.object_movement = read_yes_or_no ("Watch object movement"); f_setup.object_locating = read_yes_or_no ("Watch object locating"); return FALSE; }/* hot_key_debugging */ /* * hot_key_help * * ...displays a list of all hot keys. * */ static bool hot_key_help (void) { print_string ("Help\n"); print_string ( "\n" "Alt-D debugging options\n" "Alt-H help\n" "Alt-N new game\n" "Alt-P playback on\n" "Alt-R recording on/off\n" "Alt-S seed random numbers\n" "Alt-U undo one turn\n" "Alt-X exit game\n"); return FALSE; }/* hot_key_help */ /* * hot_key_playback * * ...allows user to turn playback on. * */ static bool hot_key_playback (void) { print_string ("Playback on\n"); if (!istream_replay) replay_open (); return FALSE; }/* hot_key_playback */ /* * hot_key_recording * * ...allows user to turn recording on/off. * */ static bool hot_key_recording (void) { if (istream_replay) { print_string ("Playback off\n"); replay_close (); } else if (ostream_record) { print_string ("Recording off\n"); record_close (); } else { print_string ("Recording on\n"); record_open (); } return FALSE; }/* hot_key_recording */ /* * hot_key_seed * * ...allows user to seed the random number seed. * */ static bool hot_key_seed (void) { print_string ("Seed random numbers\n"); print_string ("Enter seed value (or return to randomize): "); seed_random (read_number ()); return FALSE; }/* hot_key_seed */ /* * hot_key_undo * * ...allows user to undo the previous turn. * */ static bool hot_key_undo (void) { print_string ("Undo one turn\n"); if (restore_undo ()) { if (h_version >= V5) { /* for V5+ games we must */ store (2); /* store 2 (for success) */ return TRUE; /* and abort the input */ } if (h_version <= V3) { /* for V3- games we must */ z_show_status (); /* draw the status line */ return FALSE; /* and continue input */ } } else print_string ("No more undo information available.\n"); return FALSE; }/* hot_key_undo */ /* * hot_key_restart * * ...allows user to start a new game. * */ static bool hot_key_restart (void) { print_string ("New game\n"); if (read_yes_or_no ("Do you wish to restart")) { z_restart (); return TRUE; } else return FALSE; }/* hot_key_restart */ /* * hot_key_quit * * ...allows user to exit the game. * */ static bool hot_key_quit (void) { print_string ("Exit game\n"); if (read_yes_or_no ("Do you wish to quit")) { z_quit (); return TRUE; } else return FALSE; }/* hot_key_quit */ /* * handle_hot_key * * Perform the action associated with a so-called hot key. Return * true to abort the current input action. * */ bool handle_hot_key (zchar key) { if (cwin == 0) { bool aborting; aborting = FALSE; print_string ("\nHot key -- "); switch (key) { case ZC_HKEY_RECORD: aborting = hot_key_recording (); break; case ZC_HKEY_PLAYBACK: aborting = hot_key_playback (); break; case ZC_HKEY_SEED: aborting = hot_key_seed (); break; case ZC_HKEY_UNDO: aborting = hot_key_undo (); break; case ZC_HKEY_RESTART: aborting = hot_key_restart (); break; case ZC_HKEY_QUIT: aborting = hot_key_quit (); break; case ZC_HKEY_DEBUG: aborting = hot_key_debugging (); break; case ZC_HKEY_HELP: aborting = hot_key_help (); break; } if (aborting) return TRUE; print_string ("\nContinue input...\n"); } return FALSE; }/* handle_hot_key */ frotz-2.44/src/common/variable.c0000644000175000017500000001232012527045477013553 00000000000000/* variable.c - Variable and stack related opcodes * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" /* * z_dec, decrement a variable. * * zargs[0] = variable to decrement * */ void z_dec (void) { zword value; if (zargs[0] == 0) (*sp)--; else if (zargs[0] < 16) (*(fp - zargs[0]))--; else { zword addr = h_globals + 2 * (zargs[0] - 16); LOW_WORD (addr, value) value--; SET_WORD (addr, value) } }/* z_dec */ /* * z_dec_chk, decrement a variable and branch if now less than value. * * zargs[0] = variable to decrement * zargs[1] = value to check variable against * */ void z_dec_chk (void) { zword value; if (zargs[0] == 0) value = --(*sp); else if (zargs[0] < 16) value = --(*(fp - zargs[0])); else { zword addr = h_globals + 2 * (zargs[0] - 16); LOW_WORD (addr, value) value--; SET_WORD (addr, value) } branch ((short) value < (short) zargs[1]); }/* z_dec_chk */ /* * z_inc, increment a variable. * * zargs[0] = variable to increment * */ void z_inc (void) { zword value; if (zargs[0] == 0) (*sp)++; else if (zargs[0] < 16) (*(fp - zargs[0]))++; else { zword addr = h_globals + 2 * (zargs[0] - 16); LOW_WORD (addr, value) value++; SET_WORD (addr, value) } }/* z_inc */ /* * z_inc_chk, increment a variable and branch if now greater than value. * * zargs[0] = variable to increment * zargs[1] = value to check variable against * */ void z_inc_chk (void) { zword value; if (zargs[0] == 0) value = ++(*sp); else if (zargs[0] < 16) value = ++(*(fp - zargs[0])); else { zword addr = h_globals + 2 * (zargs[0] - 16); LOW_WORD (addr, value) value++; SET_WORD (addr, value) } branch ((short) value > (short) zargs[1]); }/* z_inc_chk */ /* * z_load, store the value of a variable. * * zargs[0] = variable to store * */ void z_load (void) { zword value; if (zargs[0] == 0) value = *sp; else if (zargs[0] < 16) value = *(fp - zargs[0]); else { zword addr = h_globals + 2 * (zargs[0] - 16); LOW_WORD (addr, value) } store (value); }/* z_load */ /* * z_pop, pop a value off the game stack and discard it. * * no zargs used * */ void z_pop (void) { sp++; }/* z_pop */ /* * z_pop_stack, pop n values off the game or user stack and discard them. * * zargs[0] = number of values to discard * zargs[1] = address of user stack (optional) * */ void z_pop_stack (void) { if (zargc == 2) { /* it's a user stack */ zword size; zword addr = zargs[1]; LOW_WORD (addr, size) size += zargs[0]; storew (addr, size); } else sp += zargs[0]; /* it's the game stack */ }/* z_pop_stack */ /* * z_pull, pop a value off... * * a) ...the game or a user stack and store it (V6) * * zargs[0] = address of user stack (optional) * * b) ...the game stack and write it to a variable (other than V6) * * zargs[0] = variable to write value to * */ void z_pull (void) { zword value; if (h_version != V6) { /* not a V6 game, pop stack and write */ value = *sp++; if (zargs[0] == 0) *sp = value; else if (zargs[0] < 16) *(fp - zargs[0]) = value; else { zword addr = h_globals + 2 * (zargs[0] - 16); SET_WORD (addr, value) } } else { /* it's V6, but is there a user stack? */ if (zargc == 1) { /* it's a user stack */ zword size; zword addr = zargs[0]; LOW_WORD (addr, size) size++; storew (addr, size); addr += 2 * size; LOW_WORD (addr, value) } else value = *sp++; /* it's the game stack */ store (value); } }/* z_pull */ /* * z_push, push a value onto the game stack. * * zargs[0] = value to push onto the stack * */ void z_push (void) { *--sp = zargs[0]; }/* z_push */ /* * z_push_stack, push a value onto a user stack then branch if successful. * * zargs[0] = value to push onto the stack * zargs[1] = address of user stack * */ void z_push_stack (void) { zword size; zword addr = zargs[1]; LOW_WORD (addr, size) if (size != 0) { storew ((zword) (addr + 2 * size), zargs[0]); size--; storew (addr, size); } branch (size); }/* z_push_stack */ /* * z_store, write a value to a variable. * * zargs[0] = variable to be written to * zargs[1] = value to write * */ void z_store (void) { zword value = zargs[1]; if (zargs[0] == 0) *sp = value; else if (zargs[0] < 16) *(fp - zargs[0]) = value; else { zword addr = h_globals + 2 * (zargs[0] - 16); SET_WORD (addr, value) } }/* z_store */ frotz-2.44/src/common/err.c0000644000175000017500000000777712527045477012602 00000000000000/* err.c - Runtime error reporting functions * Written by Jim Dunleavy * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" /*f_setup_t f_setup; */ /* Define stuff for stricter Z-code error checking, for the generic Unix/DOS/etc terminal-window interface. Feel free to change the way player prefs are specified, or replace report_zstrict_error() completely if you want to change the way errors are reported. */ /* int err_report_mode = ERR_DEFAULT_REPORT_MODE; */ static int error_count[ERR_NUM_ERRORS]; static char *err_messages[] = { "Text buffer overflow", "Store out of dynamic memory", "Division by zero", "Illegal object", "Illegal attribute", "No such property", "Stack overflow", "Call to illegal address", "Call to non-routine", "Stack underflow", "Illegal opcode", "Bad stack frame", "Jump to illegal address", "Can't save while in interrupt", "Nesting stream #3 too deep", "Illegal window", "Illegal window property", "Print at illegal address", "@jin called with object 0", "@get_child called with object 0", "@get_parent called with object 0", "@get_sibling called with object 0", "@get_prop_addr called with object 0", "@get_prop called with object 0", "@put_prop called with object 0", "@clear_attr called with object 0", "@set_attr called with object 0", "@test_attr called with object 0", "@move_object called moving object 0", "@move_object called moving into object 0", "@remove_object called with object 0", "@get_next_prop called with object 0" }; static void print_long (unsigned long value, int base); /* * init_err * * Initialise error reporting. * */ void init_err (void) { int i; /* Initialize the counters. */ for (i = 0; i < ERR_NUM_ERRORS; i++) error_count[i] = 0; } /* * runtime_error * * An error has occurred. Ignore it, pass it to os_fatal or report * it according to err_report_mode. * * errnum : Numeric code for error (1 to ERR_NUM_ERRORS) * */ void runtime_error (int errnum) { int wasfirst; if (errnum <= 0 || errnum > ERR_NUM_ERRORS) return; if (f_setup.err_report_mode == ERR_REPORT_FATAL || (!f_setup.ignore_errors && errnum <= ERR_MAX_FATAL)) { flush_buffer (); os_fatal (err_messages[errnum - 1]); return; } wasfirst = (error_count[errnum - 1] == 0); error_count[errnum - 1]++; if ((f_setup.err_report_mode == ERR_REPORT_ALWAYS) || (f_setup.err_report_mode == ERR_REPORT_ONCE && wasfirst)) { long pc; GET_PC (pc); print_string ("Warning: "); print_string (err_messages[errnum - 1]); print_string (" (PC = "); print_long (pc, 16); print_char (')'); if (f_setup.err_report_mode == ERR_REPORT_ONCE) { print_string (" (will ignore further occurrences)"); } else { print_string (" (occurence "); print_long (error_count[errnum - 1], 10); print_char (')'); } new_line (); } } /* report_error */ /* * print_long * * Print an unsigned 32bit number in decimal or hex. * */ static void print_long (unsigned long value, int base) { unsigned long i; char c; for (i = (base == 10 ? 1000000000 : 0x10000000); i != 0; i /= base) if (value >= i || i == 1) { c = (value / i) % base; print_char (c + (c <= 9 ? '0' : 'a' - 10)); } }/* print_long */ frotz-2.44/src/common/fastmem.c0000644000175000017500000005123012527045477013425 00000000000000/* fastmem.c - Memory related functions (fast version without virtual memory) * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /* * New undo mechanism added by Jim Dunleavy */ #include #include #include "frotz.h" #ifdef MSDOS_16BIT #include #define malloc(size) farmalloc (size) #define realloc(size,p) farrealloc (size,p) #define free(size) farfree (size) #define memcpy(d,s,n) _fmemcpy (d,s,n) #else #include #ifndef SEEK_SET #define SEEK_SET 0 #define SEEK_CUR 1 #define SEEK_END 2 #endif #define far #endif extern void seed_random (int); extern void restart_screen (void); extern void refresh_text_style (void); extern void call (zword, int, zword *, int); extern void split_window (zword); extern void script_open (void); extern void script_close (void); extern FILE *os_path_open (const char *, const char *); extern FILE *os_load_story (void); extern int os_storyfile_seek (FILE * fp, long offset, int whence); extern int os_storyfile_tell (FILE * fp); extern zword save_quetzal (FILE *, FILE *); extern zword restore_quetzal (FILE *, FILE *); extern void erase_window (zword); extern void (*op0_opcodes[]) (void); extern void (*op1_opcodes[]) (void); extern void (*op2_opcodes[]) (void); extern void (*var_opcodes[]) (void); /* char save_name[MAX_FILE_NAME + 1] = DEFAULT_SAVE_NAME; */ char auxilary_name[MAX_FILE_NAME + 1] = DEFAULT_AUXILARY_NAME; zbyte far *zmp = NULL; zbyte far *pcp = NULL; static FILE *story_fp = NULL; /* * Data for the undo mechanism. * This undo mechanism is based on the scheme used in Evin Robertson's * Nitfol interpreter. * Undo blocks are stored as differences between states. */ typedef struct undo_struct undo_t; struct undo_struct { undo_t *next; undo_t *prev; long pc; long diff_size; zword frame_count; zword stack_size; zword frame_offset; /* undo diff and stack data follow */ }; static undo_t *first_undo = NULL, *last_undo = NULL, *curr_undo = NULL; static zbyte *undo_mem = NULL, *prev_zmp, *undo_diff; static int undo_count = 0; /* * get_header_extension * * Read a value from the header extension (former mouse table). * */ zword get_header_extension (int entry) { zword addr; zword val; if (h_extension_table == 0 || entry > hx_table_size) return 0; addr = h_extension_table + 2 * entry; LOW_WORD (addr, val); return val; }/* get_header_extension */ /* * set_header_extension * * Set an entry in the header extension (former mouse table). * */ void set_header_extension (int entry, zword val) { zword addr; if (h_extension_table == 0 || entry > hx_table_size) return; addr = h_extension_table + 2 * entry; SET_WORD (addr, val); }/* set_header_extension */ /* * restart_header * * Set all header fields which hold information about the interpreter. * */ void restart_header (void) { zword screen_x_size; zword screen_y_size; zbyte font_x_size; zbyte font_y_size; int i; SET_BYTE (H_CONFIG, h_config); SET_WORD (H_FLAGS, h_flags); if (h_version >= V4) { SET_BYTE (H_INTERPRETER_NUMBER, h_interpreter_number); SET_BYTE (H_INTERPRETER_VERSION, h_interpreter_version); SET_BYTE (H_SCREEN_ROWS, h_screen_rows); SET_BYTE (H_SCREEN_COLS, h_screen_cols); } /* It's less trouble to use font size 1x1 for V5 games, especially because of a bug in the unreleased German version of "Zork 1" */ if (h_version != V6) { screen_x_size = (zword) h_screen_cols; screen_y_size = (zword) h_screen_rows; font_x_size = 1; font_y_size = 1; } else { screen_x_size = h_screen_width; screen_y_size = h_screen_height; font_x_size = h_font_width; font_y_size = h_font_height; } if (h_version >= V5) { SET_WORD (H_SCREEN_WIDTH, screen_x_size); SET_WORD (H_SCREEN_HEIGHT, screen_y_size); SET_BYTE (H_FONT_HEIGHT, font_y_size); SET_BYTE (H_FONT_WIDTH, font_x_size); SET_BYTE (H_DEFAULT_BACKGROUND, h_default_background); SET_BYTE (H_DEFAULT_FOREGROUND, h_default_foreground); } if (h_version == V6) for (i = 0; i < 8; i++) storeb ((zword) (H_USER_NAME + i), h_user_name[i]); SET_BYTE (H_STANDARD_HIGH, h_standard_high); SET_BYTE (H_STANDARD_LOW, h_standard_low); }/* restart_header */ /* * init_memory * * Allocate memory and load the story file. * */ void init_memory (void) { long size; zword addr; unsigned n; int i, j; static struct { enum story story_id; zword release; zbyte serial[6]; } records[] = { { SHERLOCK, 21, "871214" }, { SHERLOCK, 26, "880127" }, { BEYOND_ZORK, 47, "870915" }, { BEYOND_ZORK, 49, "870917" }, { BEYOND_ZORK, 51, "870923" }, { BEYOND_ZORK, 57, "871221" }, { ZORK_ZERO, 296, "881019" }, { ZORK_ZERO, 366, "890323" }, { ZORK_ZERO, 383, "890602" }, { ZORK_ZERO, 393, "890714" }, { SHOGUN, 292, "890314" }, { SHOGUN, 295, "890321" }, { SHOGUN, 311, "890510" }, { SHOGUN, 322, "890706" }, { ARTHUR, 54, "890606" }, { ARTHUR, 63, "890622" }, { ARTHUR, 74, "890714" }, { JOURNEY, 26, "890316" }, { JOURNEY, 30, "890322" }, { JOURNEY, 77, "890616" }, { JOURNEY, 83, "890706" }, { LURKING_HORROR, 203, "870506" }, { LURKING_HORROR, 219, "870912" }, { LURKING_HORROR, 221, "870918" }, { UNKNOWN, 0, "------" } }; /* Open story file */ if ((story_fp = os_load_story()) == NULL) os_fatal ("Cannot open story file"); /* Allocate memory for story header */ if ((zmp = (zbyte far *) malloc (64)) == NULL) os_fatal ("Out of memory"); /* Load header into memory */ if (fread (zmp, 1, 64, story_fp) != 64) os_fatal ("Story file read error"); /* Copy header fields to global variables */ LOW_BYTE (H_VERSION, h_version); if (h_version < V1 || h_version > V8) os_fatal ("Unknown Z-code version"); LOW_BYTE (H_CONFIG, h_config); if (h_version == V3 && (h_config & CONFIG_BYTE_SWAPPED)) os_fatal ("Byte swapped story file"); LOW_WORD (H_RELEASE, h_release); LOW_WORD (H_RESIDENT_SIZE, h_resident_size); LOW_WORD (H_START_PC, h_start_pc); LOW_WORD (H_DICTIONARY, h_dictionary); LOW_WORD (H_OBJECTS, h_objects); LOW_WORD (H_GLOBALS, h_globals); LOW_WORD (H_DYNAMIC_SIZE, h_dynamic_size); LOW_WORD (H_FLAGS, h_flags); for (i = 0, addr = H_SERIAL; i < 6; i++, addr++) LOW_BYTE (addr, h_serial[i]); /* Auto-detect buggy story files that need special fixes */ story_id = UNKNOWN; for (i = 0; records[i].story_id != UNKNOWN; i++) { if (h_release == records[i].release) { for (j = 0; j < 6; j++) if (h_serial[j] != records[i].serial[j]) goto no_match; story_id = records[i].story_id; } no_match: ; /* null statement */ } LOW_WORD (H_ABBREVIATIONS, h_abbreviations); LOW_WORD (H_FILE_SIZE, h_file_size); /* Calculate story file size in bytes */ if (h_file_size != 0) { story_size = (long) 2 * h_file_size; if (h_version >= V4) story_size *= 2; if (h_version >= V6) story_size *= 2; } else { /* some old games lack the file size entry */ os_storyfile_seek (story_fp, 0, SEEK_END); story_size = os_storyfile_tell (story_fp); os_storyfile_seek (story_fp, 64, SEEK_SET); } LOW_WORD (H_CHECKSUM, h_checksum); LOW_WORD (H_ALPHABET, h_alphabet); LOW_WORD (H_FUNCTIONS_OFFSET, h_functions_offset); LOW_WORD (H_STRINGS_OFFSET, h_strings_offset); LOW_WORD (H_TERMINATING_KEYS, h_terminating_keys); LOW_WORD (H_EXTENSION_TABLE, h_extension_table); /* Zork Zero Macintosh doesn't have the graphics flag set */ if (story_id == ZORK_ZERO && h_release == 296) h_flags |= GRAPHICS_FLAG; /* Adjust opcode tables */ if (h_version <= V4) { op0_opcodes[0x09] = z_pop; op1_opcodes[0x0f] = z_not; } else { op0_opcodes[0x09] = z_catch; op1_opcodes[0x0f] = z_call_n; } /* Allocate memory for story data */ if ((zmp = (zbyte far *) realloc (zmp, story_size)) == NULL) os_fatal ("Out of memory"); /* Load story file in chunks of 32KB */ n = 0x8000; for (size = 64; size < story_size; size += n) { if (story_size - size < 0x8000) n = (unsigned) (story_size - size); SET_PC (size); if (fread (pcp, 1, n, story_fp) != n) os_fatal ("Story file read error"); } /* Read header extension table */ hx_table_size = get_header_extension (HX_TABLE_SIZE); hx_unicode_table = get_header_extension (HX_UNICODE_TABLE); }/* init_memory */ /* * init_undo * * Allocate memory for multiple undo. It is important not to occupy * all the memory available, since the IO interface may need memory * during the game, e.g. for loading sounds or pictures. * */ void init_undo (void) { void far *reserved; reserved = NULL; /* makes compilers shut up */ if (reserve_mem != 0) { if ((reserved = malloc (reserve_mem)) == NULL) return; } /* Allocate h_dynamic_size bytes for previous dynamic zmp state + 1.5 h_dynamic_size for Quetzal diff + 2. */ undo_mem = malloc ((h_dynamic_size * 5) / 2 + 2); if (undo_mem != NULL) { prev_zmp = undo_mem; undo_diff = undo_mem + h_dynamic_size; memcpy (prev_zmp, zmp, h_dynamic_size); } else f_setup.undo_slots = 0; if (reserve_mem != 0) free (reserved); }/* init_undo */ /* * free_undo * * Free count undo blocks from the beginning of the undo list. * */ static void free_undo (int count) { undo_t *p; if (count > undo_count) count = undo_count; while (count--) { p = first_undo; if (curr_undo == first_undo) curr_undo = curr_undo->next; first_undo = first_undo->next; free (p); undo_count--; } if (first_undo) first_undo->prev = NULL; else last_undo = NULL; }/* free_undo */ /* * reset_memory * * Close the story file and deallocate memory. * */ void reset_memory (void) { if (story_fp) fclose (story_fp); story_fp = NULL; if (undo_mem) { free_undo (undo_count); free (undo_mem); } undo_mem = NULL; undo_count = 0; if (zmp) free (zmp); zmp = NULL; }/* reset_memory */ /* * storeb * * Write a byte value to the dynamic Z-machine memory. * */ void storeb (zword addr, zbyte value) { if (addr >= h_dynamic_size) runtime_error (ERR_STORE_RANGE); if (addr == H_FLAGS + 1) { /* flags register is modified */ h_flags &= ~(SCRIPTING_FLAG | FIXED_FONT_FLAG); h_flags |= value & (SCRIPTING_FLAG | FIXED_FONT_FLAG); if (value & SCRIPTING_FLAG) { if (!ostream_script) script_open (); } else { if (ostream_script) script_close (); } refresh_text_style (); } SET_BYTE (addr, value); }/* storeb */ /* * storew * * Write a word value to the dynamic Z-machine memory. * */ void storew (zword addr, zword value) { storeb ((zword) (addr + 0), hi (value)); storeb ((zword) (addr + 1), lo (value)); }/* storew */ /* * z_restart, re-load dynamic area, clear the stack and set the PC. * * no zargs used * */ void z_restart (void) { static bool first_restart = TRUE; flush_buffer (); os_restart_game (RESTART_BEGIN); seed_random (0); if (!first_restart) { os_storyfile_seek (story_fp, 0, SEEK_SET); if (fread (zmp, 1, h_dynamic_size, story_fp) != h_dynamic_size) os_fatal ("Story file read error"); } else first_restart = FALSE; restart_header (); restart_screen (); sp = fp = stack + STACK_SIZE; frame_count = 0; if (h_version != V6) { long pc = (long) h_start_pc; SET_PC (pc); } else call (h_start_pc, 0, NULL, 0); os_restart_game (RESTART_END); }/* z_restart */ /* * get_default_name * * Read a default file name from the memory of the Z-machine and * copy it to a string. * */ static void get_default_name (char *default_name, zword addr) { if (addr != 0) { zbyte len; int i; LOW_BYTE (addr, len); addr++; for (i = 0; i < len; i++) { zbyte c; LOW_BYTE (addr, c); addr++; if (c >= 'A' && c <= 'Z') c += 'a' - 'A'; default_name[i] = c; } default_name[i] = 0; if (strchr (default_name, '.') == NULL) strcpy (default_name + i, ".AUX"); } else strcpy (default_name, f_setup.aux_name); }/* get_default_name */ /* * z_restore, restore [a part of] a Z-machine state from disk * * zargs[0] = address of area to restore (optional) * zargs[1] = number of bytes to restore * zargs[2] = address of suggested file name * */ void z_restore (void) { char new_name[MAX_FILE_NAME + 1]; char default_name[MAX_FILE_NAME + 1]; FILE *gfp; zword success = 0; if (zargc != 0) { /* Get the file name */ get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0); if (os_read_file_name (new_name, default_name, FILE_LOAD_AUX) == 0) goto finished; strcpy (f_setup.aux_name, default_name); /* Open auxilary file */ if ((gfp = fopen (new_name, "rb")) == NULL) goto finished; /* Load auxilary file */ success = fread (zmp + zargs[0], 1, zargs[1], gfp); /* Close auxilary file */ fclose (gfp); } else { long pc; zword release; zword addr; int i; /* Get the file name */ if (os_read_file_name (new_name, f_setup.save_name, FILE_RESTORE) == 0) goto finished; strcpy (f_setup.save_name, new_name); /* Open game file */ if ((gfp = fopen (new_name, "rb")) == NULL) goto finished; success = restore_quetzal (gfp, story_fp); if ((short) success >= 0) { /* Close game file */ fclose (gfp); if ((short) success > 0) { zbyte old_screen_rows; zbyte old_screen_cols; /* In V3, reset the upper window. */ if (h_version == V3) split_window (0); LOW_BYTE (H_SCREEN_ROWS, old_screen_rows); LOW_BYTE (H_SCREEN_COLS, old_screen_cols); /* Reload cached header fields. */ restart_header (); /* * Since QUETZAL files may be saved on many different machines, * the screen sizes may vary a lot. Erasing the status window * seems to cover up most of the resulting badness. */ if (h_version > V3 && h_version != V6 && (h_screen_rows != old_screen_rows || h_screen_cols != old_screen_cols)) erase_window (1); } } else os_fatal ("Error reading save file"); } finished: if (h_version <= V3) branch (success); else store (success); }/* z_restore */ /* * mem_diff * * Set diff to a Quetzal-like difference between a and b, * copying a to b as we go. It is assumed that diff points to a * buffer which is large enough to hold the diff. * mem_size is the number of bytes to compare. * Returns the number of bytes copied to diff. * */ static long mem_diff (zbyte *a, zbyte *b, zword mem_size, zbyte *diff) { unsigned size = mem_size; zbyte *p = diff; unsigned j; zbyte c; for (;;) { for (j = 0; size > 0 && (c = *a++ ^ *b++) == 0; j++) size--; if (size == 0) break; size--; if (j > 0x8000) { *p++ = 0; *p++ = 0xff; *p++ = 0xff; j -= 0x8000; } if (j > 0) { *p++ = 0; j--; if (j <= 0x7f) { *p++ = j; } else { *p++ = (j & 0x7f) | 0x80; *p++ = (j & 0x7f80) >> 7; } } *p++ = c; *(b - 1) ^= c; } return p - diff; }/* mem_diff */ /* * mem_undiff * * Applies a quetzal-like diff to dest * */ static void mem_undiff (zbyte *diff, long diff_length, zbyte *dest) { zbyte c; while (diff_length) { c = *diff++; diff_length--; if (c == 0) { unsigned runlen; if (!diff_length) return; /* Incomplete run */ runlen = *diff++; diff_length--; if (runlen & 0x80) { if (!diff_length) return; /* Incomplete extended run */ c = *diff++; diff_length--; runlen = (runlen & 0x7f) | (((unsigned) c) << 7); } dest += runlen + 1; } else { *dest++ ^= c; } } }/* mem_undiff */ /* * restore_undo * * This function does the dirty work for z_restore_undo. * */ int restore_undo (void) { long pc = curr_undo->pc; if (f_setup.undo_slots == 0) /* undo feature unavailable */ return -1; if (curr_undo == NULL) /* no saved game state */ return 0; /* undo possible */ memcpy (zmp, prev_zmp, h_dynamic_size); SET_PC (pc); sp = stack + STACK_SIZE - curr_undo->stack_size; fp = stack + curr_undo->frame_offset; frame_count = curr_undo->frame_count; mem_undiff ((zbyte *) (curr_undo + 1), curr_undo->diff_size, prev_zmp); memcpy (sp, (zbyte *)(curr_undo + 1) + curr_undo->diff_size, curr_undo->stack_size * sizeof (*sp)); curr_undo = curr_undo->prev; restart_header (); return 2; }/* restore_undo */ /* * z_restore_undo, restore a Z-machine state from memory. * * no zargs used * */ void z_restore_undo (void) { store ((zword) restore_undo ()); }/* z_restore_undo */ /* * z_save, save [a part of] the Z-machine state to disk. * * zargs[0] = address of memory area to save (optional) * zargs[1] = number of bytes to save * zargs[2] = address of suggested file name * */ void z_save (void) { char new_name[MAX_FILE_NAME + 1]; char default_name[MAX_FILE_NAME + 1]; FILE *gfp; zword success = 0; if (zargc != 0) { /* Get the file name */ get_default_name (default_name, (zargc >= 3) ? zargs[2] : 0); if (os_read_file_name (new_name, default_name, FILE_SAVE_AUX) == 0) goto finished; strcpy (f_setup.aux_name, default_name); /* Open auxilary file */ if ((gfp = fopen (new_name, "wb")) == NULL) goto finished; /* Write auxilary file */ success = fwrite (zmp + zargs[0], zargs[1], 1, gfp); /* Close auxilary file */ fclose (gfp); } else { long pc; zword addr; zword nsp, nfp; int skip; int i; /* Get the file name */ if (os_read_file_name (new_name, f_setup.save_name, FILE_SAVE) == 0) goto finished; strcpy (f_setup.save_name, new_name); /* Open game file */ if ((gfp = fopen (new_name, "wb")) == NULL) goto finished; success = save_quetzal (gfp, story_fp); /* Close game file and check for errors */ if (fclose (gfp) == EOF || ferror (story_fp)) { print_string ("Error writing save file\n"); goto finished; } /* Success */ success = 1; } finished: if (h_version <= V3) branch (success); else store (success); }/* z_save */ /* * save_undo * * This function does the dirty work for z_save_undo. * */ int save_undo (void) { long diff_size; zword stack_size; undo_t *p; long pc; if (f_setup.undo_slots == 0) /* undo feature unavailable */ return -1; /* save undo possible */ while (last_undo != curr_undo) { p = last_undo; last_undo = last_undo->prev; free (p); undo_count--; } if (last_undo) last_undo->next = NULL; else first_undo = NULL; if (undo_count == f_setup.undo_slots) free_undo (1); diff_size = mem_diff (zmp, prev_zmp, h_dynamic_size, undo_diff); stack_size = stack + STACK_SIZE - sp; do { p = malloc (sizeof (undo_t) + diff_size + stack_size * sizeof (*sp)); if (p == NULL) free_undo (1); } while (!p && undo_count); if (p == NULL) return -1; pc = p->pc; GET_PC (pc); /* Turbo C doesn't like seeing p->pc here */ p->pc = pc; p->frame_count = frame_count; p->diff_size = diff_size; p->stack_size = stack_size; p->frame_offset = fp - stack; memcpy (p + 1, undo_diff, diff_size); memcpy ((zbyte *)(p + 1) + diff_size, sp, stack_size * sizeof (*sp)); if (!first_undo) { p->prev = NULL; first_undo = p; } else { last_undo->next = p; p->prev = last_undo; } p->next = NULL; curr_undo = last_undo = p; undo_count++; return 1; }/* save_undo */ /* * z_save_undo, save the current Z-machine state for a future undo. * * no zargs used * */ void z_save_undo (void) { store ((zword) save_undo ()); }/* z_save_undo */ /* * z_verify, check the story file integrity. * * no zargs used * */ void z_verify (void) { zword checksum = 0; long i; /* Sum all bytes in story file except header bytes */ os_storyfile_seek (story_fp, 64, SEEK_SET); for (i = 64; i < story_size; i++) checksum += fgetc (story_fp); /* Branch if the checksums are equal */ branch (checksum == h_checksum); }/* z_verify */ frotz-2.44/src/common/sound.c0000644000175000017500000001004612527045477013121 00000000000000/* sound.c - Sound effect function * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" #ifdef DJGPP #include "djfrotz.h" #endif #define EFFECT_PREPARE 1 #define EFFECT_PLAY 2 #define EFFECT_STOP 3 #define EFFECT_FINISH_WITH 4 extern int direct_call (zword); static zword routine = 0; static int next_sample = 0; static int next_volume = 0; static bool locked = FALSE; static bool playing = FALSE; /* * init_sound * * Initialize sound variables. * */ void init_sound (void) { locked = FALSE; playing = FALSE; } /* init_sound */ /* * start_sample * * Call the IO interface to play a sample. * */ static void start_sample (int number, int volume, int repeats, zword eos) { static zbyte lh_repeats[] = { 0x00, 0x00, 0x00, 0x01, 0xff, 0x00, 0x01, 0x01, 0x01, 0x01, 0xff, 0x01, 0x01, 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff }; if (story_id == LURKING_HORROR) repeats = lh_repeats[number]; os_start_sample (number, volume, repeats, eos); routine = eos; playing = TRUE; }/* start_sample */ /* * start_next_sample * * Play a sample that has been delayed until the previous sound effect has * finished. This is necessary for two samples in The Lurking Horror that * immediately follow other samples. * */ static void start_next_sample (void) { if (next_sample != 0) start_sample (next_sample, next_volume, 0, 0); next_sample = 0; next_volume = 0; }/* start_next_sample */ /* * end_of_sound * * Call the Z-code routine which was given as the last parameter of * a sound_effect call. This function may be called from a hardware * interrupt (which requires extremely careful programming). * */ void end_of_sound (void) { #if defined(DJGPP) && defined(SOUND_SUPPORT) end_of_sound_flag = 0; #endif playing = FALSE; if (!locked) { if (story_id == LURKING_HORROR) start_next_sample (); direct_call (routine); } }/* end_of_sound */ /* * z_sound_effect, load / play / stop / discard a sound effect. * * zargs[0] = number of bleep (1 or 2) or sample * zargs[1] = operation to perform (samples only) * zargs[2] = repeats and volume (play sample only) * zargs[3] = end-of-sound routine (play sample only, optional) * * Note: Volumes range from 1 to 8, volume 255 is the default volume. * Repeats are stored in the high byte, 255 is infinite loop. * */ void z_sound_effect (void) { zword number = zargs[0]; zword effect = zargs[1]; zword volume = zargs[2]; /* By default play sound 1 at volume 8 */ if (zargc < 1) number = 1; if (zargc < 2) effect = EFFECT_PLAY; if (zargc < 3) volume = 8; if (number >= 3 || number == 0) { locked = TRUE; if (story_id == LURKING_HORROR && (number == 9 || number == 16)) { if (effect == EFFECT_PLAY) { next_sample = number; next_volume = volume; locked = FALSE; if (!playing) start_next_sample (); } else locked = FALSE; return; } playing = FALSE; switch (effect) { case EFFECT_PREPARE: os_prepare_sample (number); break; case EFFECT_PLAY: start_sample (number, lo (volume), hi (volume), (zargc == 4) ? zargs[3] : 0); break; case EFFECT_STOP: os_stop_sample (); break; case EFFECT_FINISH_WITH: os_finish_with_sample (); break; } locked = FALSE; } else os_beep (number); }/* z_sound_effect */ frotz-2.44/src/common/random.c0000644000175000017500000000360612527045477013255 00000000000000/* random.c - Z-machine random number generator * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "frotz.h" static long A = 1; static int interval = 0; static int counter = 0; /* * seed_random * * Set the seed value for the random number generator. * */ void seed_random (int value) { if (value == 0) { /* ask interface for seed value */ A = os_random_seed (); interval = 0; } else if (value < 1000) { /* special seed value */ counter = 0; interval = value; } else { /* standard seed value */ A = value; interval = 0; } }/* seed_random */ /* * z_random, store a random number or set the random number seed. * * zargs[0] = range (positive) or seed value (negative) * */ void z_random () { if ((short) zargs[0] <= 0) { /* set random seed */ seed_random (- (short) zargs[0]); store (0); } else { /* generate random number */ zword result; if (interval != 0) { /* ...in special mode */ result = counter++; if (counter == interval) counter = 0; } else { /* ...in standard mode */ A = 0x015a4e35L * A + 1; result = (A >> 16) & 0x7fff; } store ((zword) (result % zargs[0] + 1)); } }/* z_random */ frotz-2.44/src/common/text.c0000644000175000017500000005220712527045477012762 00000000000000/* text.c - Text manipulation functions * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" enum string_type { LOW_STRING, ABBREVIATION, HIGH_STRING, EMBEDDED_STRING, VOCABULARY }; extern zword object_name (zword); static zchar decoded[10]; static zword encoded[3]; /* * According to Matteo De Luigi , * 0xab and 0xbb were in each other's proper positions. * Sat Apr 21, 2001 */ static zchar zscii_to_latin1[] = { 0xe4, 0xf6, 0xfc, 0xc4, 0xd6, 0xdc, 0xdf, 0xbb, 0xab, 0xeb, 0xef, 0xff, 0xcb, 0xcf, 0xe1, 0xe9, 0xed, 0xf3, 0xfa, 0xfd, 0xc1, 0xc9, 0xcd, 0xd3, 0xda, 0xdd, 0xe0, 0xe8, 0xec, 0xf2, 0xf9, 0xc0, 0xc8, 0xcc, 0xd2, 0xd9, 0xe2, 0xea, 0xee, 0xf4, 0xfb, 0xc2, 0xca, 0xce, 0xd4, 0xdb, 0xe5, 0xc5, 0xf8, 0xd8, 0xe3, 0xf1, 0xf5, 0xc3, 0xd1, 0xd5, 0xe6, 0xc6, 0xe7, 0xc7, 0xfe, 0xf0, 0xde, 0xd0, 0xa3, 0x00, 0x00, 0xa1, 0xbf }; /* * translate_from_zscii * * Map a ZSCII character onto the ISO Latin-1 alphabet. * */ zchar translate_from_zscii (zbyte c) { if (c == 0xfc) return ZC_MENU_CLICK; if (c == 0xfd) return ZC_DOUBLE_CLICK; if (c == 0xfe) return ZC_SINGLE_CLICK; if (c >= 0x9b && story_id != BEYOND_ZORK) { if (hx_unicode_table != 0) { /* game has its own Unicode table */ zbyte N; LOW_BYTE (hx_unicode_table, N) if (c - 0x9b < N) { zword addr = hx_unicode_table + 1 + 2 * (c - 0x9b); zword unicode; LOW_WORD (addr, unicode) return (unicode < 0x100) ? (zchar) unicode : '?'; } else return '?'; } else /* game uses standard set */ if (c <= 0xdf) { if (c == 0xdc || c == 0xdd) /* Oe and oe ligatures */ return '?'; /* are not ISO-Latin 1 */ return zscii_to_latin1[c - 0x9b]; } else return '?'; } return c; }/* translate_from_zscii */ /* * translate_to_zscii * * Map an ISO Latin-1 character onto the ZSCII alphabet. * */ zbyte translate_to_zscii (zchar c) { int i; if (c == ZC_SINGLE_CLICK) return 0xfe; if (c == ZC_DOUBLE_CLICK) return 0xfd; if (c == ZC_MENU_CLICK) return 0xfc; if (c >= ZC_LATIN1_MIN) { if (hx_unicode_table != 0) { /* game has its own Unicode table */ zbyte N; int i; LOW_BYTE (hx_unicode_table, N) for (i = 0x9b; i < 0x9b + N; i++) { zword addr = hx_unicode_table + 1 + 2 * (i - 0x9b); zword unicode; LOW_WORD (addr, unicode) if (c == unicode) return (zbyte) i; } return '?'; } else { /* game uses standard set */ for (i = 0x9b; i <= 0xdf; i++) if (c == zscii_to_latin1[i - 0x9b]) return (zbyte) i; return '?'; } } if (c == 0) /* Safety thing from David Kinder */ c = '?'; /* regarding his Unicode patches */ /* Sept 15, 2002 */ return c; }/* translate_to_zscii */ /* * alphabet * * Return a character from one of the three character sets. * */ static zchar alphabet (int set, int index) { if (h_alphabet != 0) { /* game uses its own alphabet */ zbyte c; zword addr = h_alphabet + 26 * set + index; LOW_BYTE (addr, c) return translate_from_zscii (c); } else /* game uses default alphabet */ if (set == 0) return 'a' + index; else if (set == 1) return 'A' + index; else if (h_version == V1) return " 0123456789.,!?_#'\"/\\<-:()"[index]; else return " ^0123456789.,!?_#'\"/\\-:()"[index]; }/* alphabet */ /* * load_string * * Copy a ZSCII string from the memory to the global "decoded" string. * */ static void load_string (zword addr, zword length) { int resolution = (h_version <= V3) ? 2 : 3; int i = 0; while (i < 3 * resolution) if (i < length) { zbyte c; LOW_BYTE (addr, c) addr++; decoded[i++] = translate_from_zscii (c); } else decoded[i++] = 0; }/* load_string */ /* * encode_text * * Encode the Unicode text in the global "decoded" string then write * the result to the global "encoded" array. (This is used to look up * words in the dictionary.) Up to V3 the vocabulary resolution is * two, since V4 it is three words. Because each word contains three * Z-characters, that makes six or nine Z-characters respectively. * Longer words are chopped to the proper size, shorter words are are * padded out with 5's. For word completion we pad with 0s and 31s, * the minimum and maximum Z-characters. * */ static void encode_text (int padding) { static zchar again[] = { 'a', 'g', 'a', 'i', 'n', 0 }; static zchar examine[] = { 'e', 'x', 'a', 'm', 'i', 'n', 'e', 0 }; static zchar wait[] = { 'w', 'a', 'i', 't', 0 }; zbyte zchars[12]; const zchar *ptr = decoded; zchar c; int resolution = (h_version <= V3) ? 2 : 3; int i = 0; /* Expand abbreviations that some old Infocom games lack */ if (f_setup.expand_abbreviations) if (padding == 0x05 && decoded[1] == 0) switch (decoded[0]) { case 'g': ptr = again; break; case 'x': ptr = examine; break; case 'z': ptr = wait; break; } /* Translate string to a sequence of Z-characters */ while (i < 3 * resolution) if ((c = *ptr++) != 0) { int index, set; zbyte c2; /* Search character in the alphabet */ for (set = 0; set < 3; set++) for (index = 0; index < 26; index++) if (c == alphabet (set, index)) goto letter_found; /* Character not found, store its ZSCII value */ c2 = translate_to_zscii (c); zchars[i++] = 5; zchars[i++] = 6; zchars[i++] = c2 >> 5; zchars[i++] = c2 & 0x1f; continue; letter_found: /* Character found, store its index */ if (set != 0) zchars[i++] = ((h_version <= V2) ? 1 : 3) + set; zchars[i++] = index + 6; } else zchars[i++] = padding; /* Three Z-characters make a 16bit word */ for (i = 0; i < resolution; i++) encoded[i] = (zchars[3 * i + 0] << 10) | (zchars[3 * i + 1] << 5) | (zchars[3 * i + 2]); encoded[resolution - 1] |= 0x8000; }/* encode_text */ /* * z_check_unicode, test if a unicode character can be read and printed. * * zargs[0] = Unicode * */ void z_check_unicode (void) { zword c = zargs[0]; if (c >= 0x20 && c <= 0x7e) store (3); else if (c == 0xa0) store (1); else if (c >= 0xa1 && c <= 0xff) store (3); else store (0); }/* z_check_unicode */ /* * z_encode_text, encode a ZSCII string for use in a dictionary. * * zargs[0] = address of text buffer * zargs[1] = length of ASCII string * zargs[2] = offset of ASCII string within the text buffer * zargs[3] = address to store encoded text in * * This is a V5+ opcode and therefore the dictionary resolution must be * three 16bit words. * */ void z_encode_text (void) { int i; load_string ((zword) (zargs[0] + zargs[2]), zargs[1]); encode_text (0x05); for (i = 0; i < 3; i++) storew ((zword) (zargs[3] + 2 * i), encoded[i]); }/* z_encode_text */ /* * decode_text * * Convert encoded text to Unicode. The encoded text consists of 16bit * words. Every word holds 3 Z-characters (5 bits each) plus a spare * bit to mark the last word. The Z-characters translate to ZSCII by * looking at the current current character set. Some select another * character set, others refer to abbreviations. * * There are several different string types: * * LOW_STRING - from the lower 64KB (byte address) * ABBREVIATION - from the abbreviations table (word address) * HIGH_STRING - from the end of the memory map (packed address) * EMBEDDED_STRING - from the instruction stream (at PC) * VOCABULARY - from the dictionary (byte address) * * The last type is only used for word completion. * */ #define outchar(c) if (st==VOCABULARY) *ptr++=c; else print_char(c) static void decode_text (enum string_type st, zword addr) { zchar *ptr; long byte_addr; zchar c2; zword code; zbyte c, prev_c = 0; int shift_state = 0; int shift_lock = 0; int status = 0; ptr = NULL; /* makes compilers shut up */ byte_addr = 0; /* Calculate the byte address if necessary */ if (st == ABBREVIATION) byte_addr = (long) addr << 1; else if (st == HIGH_STRING) { if (h_version <= V3) byte_addr = (long) addr << 1; else if (h_version <= V5) byte_addr = (long) addr << 2; else if (h_version <= V7) byte_addr = ((long) addr << 2) + ((long) h_strings_offset << 3); else /* h_version == V8 */ byte_addr = (long) addr << 3; if (byte_addr >= story_size) runtime_error (ERR_ILL_PRINT_ADDR); } /* Loop until a 16bit word has the highest bit set */ if (st == VOCABULARY) ptr = decoded; do { int i; /* Fetch the next 16bit word */ if (st == LOW_STRING || st == VOCABULARY) { LOW_WORD (addr, code) addr += 2; } else if (st == HIGH_STRING || st == ABBREVIATION) { HIGH_WORD (byte_addr, code) byte_addr += 2; } else CODE_WORD (code) /* Read its three Z-characters */ for (i = 10; i >= 0; i -= 5) { zword abbr_addr; zword ptr_addr; c = (code >> i) & 0x1f; switch (status) { case 0: /* normal operation */ if (shift_state == 2 && c == 6) status = 2; else if (h_version == V1 && c == 1) new_line (); else if (h_version >= V2 && shift_state == 2 && c == 7) new_line (); else if (c >= 6) outchar (alphabet (shift_state, c - 6)); else if (c == 0) outchar (' '); else if (h_version >= V2 && c == 1) status = 1; else if (h_version >= V3 && c <= 3) status = 1; else { shift_state = (shift_lock + (c & 1) + 1) % 3; if (h_version <= V2 && c >= 4) shift_lock = shift_state; break; } shift_state = shift_lock; break; case 1: /* abbreviation */ ptr_addr = h_abbreviations + 64 * (prev_c - 1) + 2 * c; LOW_WORD (ptr_addr, abbr_addr) decode_text (ABBREVIATION, abbr_addr); status = 0; break; case 2: /* ZSCII character - first part */ status = 3; break; case 3: /* ZSCII character - second part */ c2 = translate_from_zscii ((prev_c << 5) | c); outchar (c2); status = 0; break; } prev_c = c; } } while (!(code & 0x8000)); if (st == VOCABULARY) *ptr = 0; }/* decode_text */ #undef outchar /* * z_new_line, print a new line. * * no zargs used * */ void z_new_line (void) { new_line (); }/* z_new_line */ /* * z_print, print a string embedded in the instruction stream. * * no zargs used * */ void z_print (void) { decode_text (EMBEDDED_STRING, 0); }/* z_print */ /* * z_print_addr, print a string from the lower 64KB. * * zargs[0] = address of string to print * */ void z_print_addr (void) { decode_text (LOW_STRING, zargs[0]); }/* z_print_addr */ /* * z_print_char print a single ZSCII character. * * zargs[0] = ZSCII character to be printed * */ void z_print_char (void) { print_char (translate_from_zscii (zargs[0])); }/* z_print_char */ /* * z_print_form, print a formatted table. * * zargs[0] = address of formatted table to be printed * */ void z_print_form (void) { zword count; zword addr = zargs[0]; bool first = TRUE; for (;;) { LOW_WORD (addr, count) addr += 2; if (count == 0) break; if (!first) new_line (); while (count--) { zbyte c; LOW_BYTE (addr, c) addr++; print_char (translate_from_zscii (c)); } first = FALSE; } }/* z_print_form */ /* * print_num * * Print a signed 16bit number. * */ void print_num (zword value) { int i; /* Print sign */ if ((short) value < 0) { print_char ('-'); value = - (short) value; } /* Print absolute value */ for (i = 10000; i != 0; i /= 10) if (value >= i || i == 1) print_char ('0' + (value / i) % 10); }/* print_num */ /* * z_print_num, print a signed number. * * zargs[0] = number to print * */ void z_print_num (void) { print_num (zargs[0]); }/* z_print_num */ /* * print_object * * Print an object description. * */ void print_object (zword object) { zword addr = object_name (object); zword code = 0x94a5; zbyte length; LOW_BYTE (addr, length) addr++; if (length != 0) LOW_WORD (addr, code) if (code == 0x94a5) { /* encoded text 0x94a5 == empty string */ print_string ("object#"); /* supply a generic name */ print_num (object); /* for anonymous objects */ } else decode_text (LOW_STRING, addr); }/* print_object */ /* * z_print_obj, print an object description. * * zargs[0] = number of object to be printed * */ void z_print_obj (void) { print_object (zargs[0]); }/* z_print_obj */ /* * z_print_paddr, print the string at the given packed address. * * zargs[0] = packed address of string to be printed * */ void z_print_paddr (void) { decode_text (HIGH_STRING, zargs[0]); }/* z_print_paddr */ /* * z_print_ret, print the string at PC, print newline then return true. * * no zargs used * */ void z_print_ret (void) { decode_text (EMBEDDED_STRING, 0); new_line (); ret (1); }/* z_print_ret */ /* * print_string * * Print a string of ASCII characters. * */ void print_string (const char *s) { char c; while ((c = *s++) != 0) if (c == '\n') new_line (); else print_char (c); }/* print_string */ /* * z_print_unicode * * zargs[0] = Unicode * */ void z_print_unicode (void) { print_char ((zargs[0] <= 0xff) ? zargs[0] : '?'); }/* z_print_unicode */ /* * lookup_text * * Scan a dictionary searching for the given word. The first argument * can be * * 0x00 - find the first word which is >= the given one * 0x05 - find the word which exactly matches the given one * 0x1f - find the last word which is <= the given one * * The return value is 0 if the search fails. * */ static zword lookup_text (int padding, zword dct) { zword entry_addr; zword entry_count; zword entry; zword addr; zbyte entry_len; zbyte sep_count; int resolution = (h_version <= V3) ? 2 : 3; int entry_number; int lower, upper; int i; bool sorted; encode_text (padding); LOW_BYTE (dct, sep_count) /* skip word separators */ dct += 1 + sep_count; LOW_BYTE (dct, entry_len) /* get length of entries */ dct += 1; LOW_WORD (dct, entry_count) /* get number of entries */ dct += 2; if ((short) entry_count < 0) { /* bad luck, entries aren't sorted */ entry_count = - (short) entry_count; sorted = FALSE; } else sorted = TRUE; /* entries are sorted */ lower = 0; upper = entry_count - 1; while (lower <= upper) { if (sorted) /* binary search */ entry_number = (lower + upper) / 2; else /* linear search */ entry_number = lower; entry_addr = dct + entry_number * entry_len; /* Compare word to dictionary entry */ addr = entry_addr; for (i = 0; i < resolution; i++) { LOW_WORD (addr, entry) if (encoded[i] != entry) goto continuing; addr += 2; } return entry_addr; /* exact match found, return now */ continuing: if (sorted) /* binary search */ if (encoded[i] > entry) lower = entry_number + 1; else upper = entry_number - 1; else lower++; /* linear search */ } /* No exact match has been found */ if (padding == 0x05) return 0; entry_number = (padding == 0x00) ? lower : upper; if (entry_number == -1 || entry_number == entry_count) return 0; return dct + entry_number * entry_len; }/* lookup_text */ /* * tokenise_text * * Translate a single word to a token and append it to the token * buffer. Every token consists of the address of the dictionary * entry, the length of the word and the offset of the word from * the start of the text buffer. Unknown words cause empty slots * if the flag is set (such that the text can be scanned several * times with different dictionaries); otherwise they are zero. * */ static void tokenise_text (zword text, zword length, zword from, zword parse, zword dct, bool flag) { zword addr; zbyte token_max, token_count; LOW_BYTE (parse, token_max) parse++; LOW_BYTE (parse, token_count) if (token_count < token_max) { /* sufficient space left for token? */ storeb (parse++, token_count + 1); load_string ((zword) (text + from), length); addr = lookup_text (0x05, dct); if (addr != 0 || !flag) { parse += 4 * token_count; storew ((zword) (parse + 0), addr); storeb ((zword) (parse + 2), length); storeb ((zword) (parse + 3), from); } } }/* tokenise_text */ /* * tokenise_line * * Split an input line into words and translate the words to tokens. * */ void tokenise_line (zword text, zword token, zword dct, bool flag) { zword addr1; zword addr2; zbyte length; zbyte c; length = 0; /* makes compilers shut up */ /* Use standard dictionary if the given dictionary is zero */ if (dct == 0) dct = h_dictionary; /* Remove all tokens before inserting new ones */ storeb ((zword) (token + 1), 0); /* Move the first pointer across the text buffer searching for the beginning of a word. If this succeeds, store the position in a second pointer. Move the first pointer searching for the end of the word. When it is found, "tokenise" the word. Continue until the end of the buffer is reached. */ addr1 = text; addr2 = 0; if (h_version >= V5) { addr1++; LOW_BYTE (addr1, length) } do { zword sep_addr; zbyte sep_count; zbyte separator; /* Fetch next ZSCII character */ addr1++; if (h_version >= V5 && addr1 == text + 2 + length) c = 0; else LOW_BYTE (addr1, c) /* Check for separator */ sep_addr = dct; LOW_BYTE (sep_addr, sep_count) sep_addr++; do { LOW_BYTE (sep_addr, separator) sep_addr++; } while (c != separator && --sep_count != 0); /* This could be the start or the end of a word */ if (sep_count == 0 && c != ' ' && c != 0) { if (addr2 == 0) addr2 = addr1; } else if (addr2 != 0) { tokenise_text ( text, (zword) (addr1 - addr2), (zword) (addr2 - text), token, dct, flag ); addr2 = 0; } /* Translate separator (which is a word in its own right) */ if (sep_count != 0) tokenise_text ( text, (zword) (1), (zword) (addr1 - text), token, dct, flag ); } while (c != 0); }/* tokenise_line */ /* * z_tokenise, make a lexical analysis of a ZSCII string. * * zargs[0] = address of string to analyze * zargs[1] = address of token buffer * zargs[2] = address of dictionary (optional) * zargs[3] = set when unknown words cause empty slots (optional) * */ void z_tokenise (void) { /* Supply default arguments */ if (zargc < 3) zargs[2] = 0; if (zargc < 4) zargs[3] = 0; /* Call tokenise_line to do the real work */ tokenise_line (zargs[0], zargs[1], zargs[2], zargs[3] != 0); }/* z_tokenise */ /* * completion * * Scan the vocabulary to complete the last word on the input line * (similar to "tcsh" under Unix). The return value is * * 2 ==> completion is impossible * 1 ==> completion is ambiguous * 0 ==> completion is successful * * The function also returns a string in its second argument. In case * of 2, the string is empty; in case of 1, the string is the longest * extension of the last word on the input line that is common to all * possible completions (for instance, if the last word on the input * is "fo" and its only possible completions are "follow" and "folly" * then the string is "ll"); in case of 0, the string is an extension * to the last word that results in the only possible completion. * */ int completion (const zchar *buffer, zchar *result) { zword minaddr; zword maxaddr; zchar *ptr; zchar c; int len; int i; *result = 0; /* Copy last word to "decoded" string */ len = 0; while ((c = *buffer++) != 0) if (c != ' ') { if (len < 9) decoded[len++] = c; } else len = 0; decoded[len] = 0; /* Search the dictionary for first and last possible extensions */ minaddr = lookup_text (0x00, h_dictionary); maxaddr = lookup_text (0x1f, h_dictionary); if (minaddr == 0 || maxaddr == 0 || minaddr > maxaddr) return 2; /* Copy first extension to "result" string */ decode_text (VOCABULARY, minaddr); ptr = result; for (i = len; (c = decoded[i]) != 0; i++) *ptr++ = c; *ptr = 0; /* Merge second extension with "result" string */ decode_text (VOCABULARY, maxaddr); for (i = len, ptr = result; (c = decoded[i]) != 0; i++, ptr++) if (*ptr != c) break; *ptr = 0; /* Search was ambiguous or successful */ return (minaddr == maxaddr) ? 0 : 1; }/* completion */ frotz-2.44/src/common/table.c0000644000175000017500000000754612527045477013073 00000000000000/* table.c - Table handling opcodes * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" /* * z_copy_table, copy a table or fill it with zeroes. * * zargs[0] = address of table * zargs[1] = destination address or 0 for fill * zargs[2] = size of table * * Note: Copying is safe even when source and destination overlap; but * if zargs[1] is negative the table _must_ be copied forwards. * */ void z_copy_table (void) { zword addr; zword size = zargs[2]; zbyte value; int i; if (zargs[1] == 0) /* zero table */ for (i = 0; i < size; i++) storeb ((zword) (zargs[0] + i), 0); else if ((short) size < 0 || zargs[0] > zargs[1]) /* copy forwards */ for (i = 0; i < (((short) size < 0) ? - (short) size : size); i++) { addr = zargs[0] + i; LOW_BYTE (addr, value) storeb ((zword) (zargs[1] + i), value); } else /* copy backwards */ for (i = size - 1; i >= 0; i--) { addr = zargs[0] + i; LOW_BYTE (addr, value) storeb ((zword) (zargs[1] + i), value); } }/* z_copy_table */ /* * z_loadb, store a value from a table of bytes. * * zargs[0] = address of table * zargs[1] = index of table entry to store * */ void z_loadb (void) { zword addr = zargs[0] + zargs[1]; zbyte value; LOW_BYTE (addr, value) store (value); }/* z_loadb */ /* * z_loadw, store a value from a table of words. * * zargs[0] = address of table * zargs[1] = index of table entry to store * */ void z_loadw (void) { zword addr = zargs[0] + 2 * zargs[1]; zword value; LOW_WORD (addr, value) store (value); }/* z_loadw */ /* * z_scan_table, find and store the address of a target within a table. * * zargs[0] = target value to be searched for * zargs[1] = address of table * zargs[2] = number of table entries to check value against * zargs[3] = type of table (optional, defaults to 0x82) * * Note: The table is a word array if bit 7 of zargs[3] is set; otherwise * it's a byte array. The lower bits hold the address step. * */ void z_scan_table (void) { zword addr = zargs[1]; int i; /* Supply default arguments */ if (zargc < 4) zargs[3] = 0x82; /* Scan byte or word array */ for (i = 0; i < zargs[2]; i++) { if (zargs[3] & 0x80) { /* scan word array */ zword wvalue; LOW_WORD (addr, wvalue) if (wvalue == zargs[0]) goto finished; } else { /* scan byte array */ zbyte bvalue; LOW_BYTE (addr, bvalue) if (bvalue == zargs[0]) goto finished; } addr += zargs[3] & 0x7f; } addr = 0; finished: store (addr); branch (addr); }/* z_scan_table */ /* * z_storeb, write a byte into a table of bytes. * * zargs[0] = address of table * zargs[1] = index of table entry * zargs[2] = value to be written * */ void z_storeb (void) { storeb ((zword) (zargs[0] + zargs[1]), zargs[2]); }/* z_storeb */ /* * z_storew, write a word into a table of words. * * zargs[0] = address of table * zargs[1] = index of table entry * zargs[2] = value to be written * */ void z_storew (void) { storew ((zword) (zargs[0] + 2 * zargs[1]), zargs[2]); }/* z_storew */ frotz-2.44/src/common/input.c0000644000175000017500000001520412527045477013131 00000000000000/* input.c - High level input functions * Copyright (c) 1995-1997 Stefan Jokisch * * This file is part of Frotz. * * Frotz is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Frotz is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "frotz.h" extern int save_undo (void); extern zchar stream_read_key (zword, zword, bool); extern zchar stream_read_input (int, zchar *, zword, zword, bool, bool); extern void tokenise_line (zword, zword, zword, bool); /* * is_terminator * * Check if the given key is an input terminator. * */ bool is_terminator (zchar key) { if (key == ZC_TIME_OUT) return TRUE; if (key == ZC_RETURN) return TRUE; if (key >= ZC_HKEY_MIN && key <= ZC_HKEY_MAX) return TRUE; if (h_terminating_keys != 0) if (key >= ZC_ARROW_MIN && key <= ZC_MENU_CLICK) { zword addr = h_terminating_keys; zbyte c; do { LOW_BYTE (addr, c); if (c == 255 || key == translate_from_zscii (c)) return TRUE; addr++; } while (c != 0); } return FALSE; }/* is_terminator */ /* * z_make_menu, add or remove a menu and branch if successful. * * zargs[0] = number of menu * zargs[1] = table of menu entries or 0 to remove menu * */ void z_make_menu (void) { /* This opcode was only used for the Macintosh version of Journey. It controls menus with numbers greater than 2 (menus 0, 1 and 2 are system menus). Frotz doesn't implement menus yet. */ branch (FALSE); }/* z_make_menu */ /* * read_yes_or_no * * Ask the user a question; return true if the answer is yes. * */ bool read_yes_or_no (const char *s) { zchar key; print_string (s); print_string ("? (y/n) >"); key = stream_read_key (0, 0, FALSE); if (key == 'y' || key == 'Y') { print_string ("y\n"); return TRUE; } else { print_string ("n\n"); return FALSE; } }/* read_yes_or_no */ /* * read_string * * Read a string from the current input stream. * */ void read_string (int max, zchar *buffer) { zchar key; buffer[0] = 0; do { key = stream_read_input (max, buffer, 0, 0, FALSE, FALSE); } while (key != ZC_RETURN); }/* read_string */ /* * read_number * * Ask the user to type in a number and return it. * */ int read_number (void) { zchar buffer[6]; int value = 0; int i; read_string (5, buffer); for (i = 0; buffer[i] != 0; i++) if (buffer[i] >= '0' && buffer[i] <= '9') value = 10 * value + buffer[i] - '0'; return value; }/* read_number */ /* * z_read, read a line of input and (in V5+) store the terminating key. * * zargs[0] = address of text buffer * zargs[1] = address of token buffer * zargs[2] = timeout in tenths of a second (optional) * zargs[3] = packed address of routine to be called on timeout * */ void z_read (void) { zchar buffer[INPUT_BUFFER_SIZE]; zword addr; zchar key; zbyte max, size; zbyte c; int i; /* Supply default arguments */ if (zargc < 3) zargs[2] = 0; /* Get maximum input size */ addr = zargs[0]; LOW_BYTE (addr, max); if (h_version <= V4) max--; if (max >= INPUT_BUFFER_SIZE) max = INPUT_BUFFER_SIZE - 1; /* Get initial input size */ if (h_version >= V5) { addr++; LOW_BYTE (addr, size); } else size = 0; /* Copy initial input to local buffer */ for (i = 0; i < size; i++) { addr++; LOW_BYTE (addr, c); buffer[i] = translate_from_zscii (c); } buffer[i] = 0; /* Draw status line for V1 to V3 games */ if (h_version <= V3) z_show_status (); /* Read input from current input stream */ key = stream_read_input ( max, buffer, /* buffer and size */ zargs[2], /* timeout value */ zargs[3], /* timeout routine */ TRUE, /* enable hot keys */ h_version == V6); /* no script in V6 */ if (key == ZC_BAD) return; /* Perform save_undo for V1 to V4 games */ if (h_version <= V4) save_undo (); /* Copy local buffer back to dynamic memory */ for (i = 0; buffer[i] != 0; i++) { if (key == ZC_RETURN) { if (buffer[i] >= 'A' && buffer[i] <= 'Z') buffer[i] += 'a' - 'A'; if (buffer[i] >= 0xc0 && buffer[i] <= 0xde && buffer[i] != 0xd7) buffer[i] += 0x20; } storeb ((zword) (zargs[0] + ((h_version <= V4) ? 1 : 2) + i), translate_to_zscii (buffer[i])); } /* Add null character (V1-V4) or write input length into 2nd byte */ if (h_version <= V4) storeb ((zword) (zargs[0] + 1 + i), 0); else storeb ((zword) (zargs[0] + 1), i); /* Tokenise line if a token buffer is present */ if (key == ZC_RETURN && zargs[1] != 0) tokenise_line (zargs[0], zargs[1], 0, FALSE); /* Store key */ if (h_version >= V5) store (translate_to_zscii (key)); }/* z_read */ /* * z_read_char, read and store a key. * * zargs[0] = input device (must be 1) * zargs[1] = timeout in tenths of a second (optional) * zargs[2] = packed address of routine to be called on timeout * */ void z_read_char (void) { zchar key; /* Supply default arguments */ if (zargc < 2) zargs[1] = 0; /* Read input from the current input stream */ key = stream_read_key ( zargs[1], /* timeout value */ zargs[2], /* timeout routine */ TRUE); /* enable hot keys */ if (key == ZC_BAD) return; /* Store key */ /* For timeouts, make sure translate_to_zscii() won't try to convert * 0x00. We should instead return 0x00 as is. * Thanks to Peter Seebach. */ if (key == 0) store(key); else store (translate_to_zscii (key)); }/* z_read_char */ /* * z_read_mouse, write the current mouse status into a table. * * zargs[0] = address of table * */ void z_read_mouse (void) { zword btn; btn = 1; /** I don't remember what was going on here */ /* Read the mouse position and which buttons are down */ /* btn = os_read_mouse (); hx_mouse_y = mouse_y; hx_mouse_x = mouse_x; */ storew ((zword) (zargs[0] + 0), hx_mouse_y); storew ((zword) (zargs[0] + 2), hx_mouse_x); storew ((zword) (zargs[0] + 4), btn); /* mouse button bits */ storew ((zword) (zargs[0] + 6), 0); /* menu selection */ }/* z_read_mouse */ frotz-2.44/src/test/0000755000175000017500000000000012527042452011361 500000000000000frotz-2.44/src/test/strictz.z50000644000175000017500000001000012527042452013252 00000000000000ç Kå981127Bâæµ6.15€ êú À0>I›B‡=Hä¥Bôk.ÍEB]Ó°¥BÔ夿EDÔ娿DÔ嬿HÐÞßÑÒÔÖØÙÚÛàáËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËËx@.," L¥¥”¥€N…¥”¥€x¥¥”¥€yX¥”¥€à?Zÿºf€8²ÂîÌæ¿²€¥æ¿²¦¥>‹ÄD˜‘@ ͳÎM^ê#%ü¥³ÈR÷)—å²ÂîÌæ¿²€¥æ¿²¦¥>™ähŠ€¥ ˳ÈR÷)—å³ÎM^ê#%ü¥¢€3²ÂŠd¶!®Å 濲¦¥>€¥æ¿ ͳ¾:hR÷)—å³¾"—]H俲Šd¶!®Å 濲¦¥>€¥æ¿ ˳¾"—]H俳¾:hR÷)—売Šd¶T×*y€¥æ¿²¦¥>€¥æ¿ Å aK³¾"—]H俳¾:hR÷)—å¡€5²ÂŠd¶aÇEӰ濲¦¥>€¥æ¿ ͳ¾:hR÷)—å³¾"—]H俲Šd¶aÇEӰ濲¦¥>€¥æ¿ ˳¾"—]H俳¾:hR÷)—år²ÂŠd¶VôT¶)Üæ¿²€¥æ¿²¦¥>€¥æ¿ Å aK³¾"—]H俳¾:hR÷)—åq²ÂŠd¶VôÔæ¿²€¥æ¿²¦¥>€¥æ¿ Å aK³¾"—]H俳¾:hR÷)—ål²Â(ׯg7€¥æ¿²€¥æ¿»°k²Â d¶9Üæ¿²€¥æ¿»°j€<²Â*c%XÙæàæ¿²€¥æ¿²¦¥>‹ÄD˜‘@ ͳÎM^ê#%ü¥³ÈR÷)—å²Â*c%XÙæàæ¿²€¥æ¿²¦¥>™ähŠ€¥ ˳ÈR÷)—å G »°³ÎM^ê#%ü¥n²ÂÓaWd¶P濲€¥æ¿»°©²ÂêJ›(¶Pæ¿»°s²ÂŠd¶M]d¶VôÔæ¿²€¥æ¿²¦¥>€¥æ¿ Å aK³¾"—]H俳¾:hR÷)—å²]Èd|eXä§X–ÍOü+â—üäü¤ÿϤÁƒ K²”åóŒ,ϤÁƒ÷îH²”匲7x"îW%TxžDL¿€¥Œÿ±²*c.M€ÂîL½圥ù‡ù‡ù‡ù‡²äeXeÓ0@1YÈ5Ñ$½圥ù¤ù¤ù¤²äeXeÓ0@1YÕêO%t§”åù¾ù¾ù¾²äeXeÓ0@1YØ8ñ:l¥œ§ùÎùÎùβäeXeÓ0@1YÕ^•Æ%7¥œ§ùéùéùé²äeXeÓ0@1YÕ^•¥œ§ùûùûùû²äeXeÓ0@"*åXÙfåt§”åù ù ù ù ²äeXeÓ0@aYÆg7¥œ§ùùù¢æ­²äeXeÓ0@eXd¶9\½圥ùùùùùù²äeXeÓ0@:x*ùÔåt§”åù;ù;ù;ù;²äeXeÓ0]RSjÔåt§”åÚBÚB²äeXeÓ01YÓ+¹Õ^•¥œ§ùHùHùH²äeXdRUEY)%ЧI ×²ä*i‹7x"îW%œ¥ó?ÿþ²äVêc~ x²”åöÿ°ÎM^ê#%; SQ&ec aYÙfîY+:`Pï)¨—åäRé:l4-(|¼F!®M@ié`&ˆjJO%Lf‡=HgêzHê]IO #Hk.mQx ^’©Up×$³Že Pï)zHê\ )Ó0aI4J`3S-:l Ù6š1 eª]@;.—HÑGÀN€cH4ê#%|² ªM `; aJK]Fb“ñ(4Ù•*æeÔOR`Pï)zHê\ 6šE )Ù5Wf: RåL,4ÙØtdLW.ReL PS-:l@-+ *c8×(]ÙeSŽe e¦dcJ¹:“Eœ¥äV* te@e¦d5S+j\*c ;.&1Š$`e^ê#%d³- Rqx(Ó`4ÙÓÓc7i:“êgWMIê^€RàN“Ÿ*ô¾RàæM ) @%ÉL¸d\Ó!¥|5SÙ†`jµS $P²“S e¦dd) c]Ñx+:^j$5@"—]Hd:(²Ž,P+G ;rîg*L5@]Xj9†`S eXeI`i-(+†`N>Ó":%I4*c eªîEÙx,5@:y*õ]Y*åȧäršE zš.A@f€HÐ(7x"îW Q`eª*c ]Xj9`µ¾Åh“—àåx“S* ÓÙeRW 4Øê*`HÉ(P+ 9]Çk*zHêÜÓ‡=HdjG*àH9`eª¦+Æg7*c QS€:i9eX- e®`g78úe@4Øê*`aY`eªÓeWVêeW.$S 9“Rê-(O_HeÔL`d6šE 4Û( RjEü§ÁsSBtr`9]Çk*Áø¥LÒ¨¥"ê›*]H]FåE%Xfôø¥]RÓºl"•ø¥ ÑÄ¥VîÏ%VîO%[4Æ^æø¥VôÔ©VôÔª9Ü©9ܪfrotz-2.44/src/test/random.z50000644000175000017500000001300012527042452013033 00000000000000 @021026BehÄ6.21€ ÀÐâô›B‡=Hä¥Bôk.ÍEB]Ó°¥BHUVWY[]^_`PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPc.," à?œÿº•- (CbçA( "ï_²D\—„\´‡1jF Qk*/ aɨ´º–A)AP"ï_²D\—„\´‡1jF Q`]Ì7 aɨ´º•–Œÿ«ê¿ëí CP€áï_²Vêl½€¥æ¿ï_²&ôWôæ¿ï_²2æOQîgÅôæ¿ï_²·+¦˜TÈ(¦Àf€TÚaEÈ¥ï_²·+¸X¸4Ú;%È¥BÆBQÒú_ºPèŒÏ/âéÿ-þ þIùº•þwÿþBN ÈèŒèUbñ c€+uï¯A(JÚü|Œ JÚü=ŒÚüo•ŒÿÔ•Œÿ°ëí?ÿÿ[ÎÜêöB1ÍC9ÉU0Œ ²å•]X`OÀA^4ê1Ó@€¥ö BP"BÆBQÏú_ºPŒá'╌ÿÝàó$€Å cÍÙ‰(•ŒÿóBÆBQÒú_ºPèŒÏ/âTBÆBQÒú_ºP BŒá+∥ 9 BP"BÆBQÏú_ºPŒá'╌ÿÝíí- ¥öS¹A Yíï_²¦k ¤²öO¹ŒAqÆAQGí?ÿÿ°Œÿ;° ÂN … B2M•çEBÆBFÒú_ºEèŒÏ/„TBÆBFÒú_ºE BŒá+„•Œÿ²T2àó$UöS¹A IöŒAqÆAQGí?ÿÿ±Œÿx°ê¿ëí  BFDBÆBFÒú_ºEèŒÏ/„c]BÆBFÏú_ºEŒÏ/„•Œÿ»ï_濲Ù*æeÔÏ BFeUBÆBFÒú_ºEèŒÏ/„véÿ-þ þIùº•þwÿþX4{ cÔuï¯Úü#•Œÿ알ÿšï_° BF"BÆBFÏú_ºEŒá'„•ŒÿÝàó$Î1UöÿŒÿó°€mó²%|QE_j—­Eœ¥²E|b·(É—­Eœ¥²X¿–iÙEœ¥²äV*  EHd*c%ôö-ÿAÿ1HâŒAÿ2HŒŒ AÿqC°²”åŒÿ“°°²å[Á(¦@·Q—R:lW^—— BEŒ·AN²"&㪻°A O Ú/»°A![²fî) f€VîO È4×—àæ¿»°A" *³Ù(%7+‹Àc7:l¦E*Âô¥A#T2³c7:l¦E*Âô¥A$Z:³Pï)—¦E*Âô¥B ²fî) æ€B€¾Á—N²]F$ ^’€¥Œ ²rîe@æ€Á—E²—…²…˜>濲Ó-¨¥Bϲ¾…>—åUBѲ¾…p¦Åü¥U-ÿÁ—ÿH ŒAÿO²]Ó°¥ ŒAÿJ²&ž* ²×\Þ€¹Ï/Ú/ ²%L5È4 *y]Êàæ¿²U4€¥æ¿³¦E*Âô¥B€VC€QCʲ]F¤¥Œ²rîåE²šg%@IRRþX:l€¥-ÿÁ—ÿQ³…>¦E*Âô¥Á—ÿQ³…p¦ÀÁ(¦E˜]BJ²eXäŒB ÆCL²-Ó$µ@Œ BG²k €¥AU³%Û9*þê^€Á(¦E˜]²—%-ÿAÿP²:ed\ftåÓŒ“AÿR²4Ø Rà-䥌AÿJ²TתyŒsAÿJ²*)«ŒgAÿJ²!®Å%Œ[AÿL²zšMŠÜ¥ŒMAÿL²aÇEÓ°¥Œ?Aÿ L²!®E7ªeŒ1Aÿ L²zšMŠã%Œ#Aÿ J²*)ªåŒAÿ N²Pï)F”Ô¥ŒAÿ Z²Ãt¹ÙS$,f‡=Hf4Ò¥ŒëAÿ#²1Û(¹ÓÙfîY(ÐÚ/³¦E*Âô¥AÿY²]RSj— Ú/³¦E*Âô¥Á•ÿl²J›(¹€¥Ú/²4€¥Ú/AF²`q®! ršE HÐ(4R¥ôÚ/-²Ó€¥Ú/£a?ð²Ó€¥Ú/³¦E*Âô¥³¦E*Âô¥AÿUBÚ/³¦E*Âô¥AÿJ²E˜&ŒAÿJ²EÜ¥Œ AÿE²–E² Ñ`Ú/³¦E*Âô¥B€+Õÿc€  ËFG²"&ã ǪŒ ²N™5Ó°¥²€¥²Ôê# ORW€¥æ¿²—àB[²;N™‹€¥5Ú/Œ<²¦`P^•*ùøÚ/&ÍOOBÆbÙ²¾it\ ~™5W‡=H俲4€¥Ú/ ²¦E*Âô§° ÀBÌÕÿcAà#ç[BÄ›à#ç‡BÄ›±aC±BGBðBÉBE‹ÿÿÉÿÉÿcC°‹ÿÿ6tà#çBÉÙº¸o«Á• ÆA E B ÉC~Å B›ÉCûÅ  Jàº!¸å¿°Ù/ÛAÉÙº#¸­°à/PéÿAÿL²N™5Ó°¥ŒDAÿGªŒ;AÿL²"&㪌-AÿV²×SY:jÙ€¥æ¿²—åŒAÿQ²ØfîM€› 濲—å°ÉÀ €gIÿÏ/Ú/²¥ô¥É€ QÉ?׌;É×Qÿ-ÿO ×CSPtT–ŒÿæOÉÿÍOOCMbIo V²ÁrzHêÜæ¿²Áø¥Œ oÚ/ °Ù/ÛéÿAÿD›AÿD›AÿMFÆCð›±±å™5Ø·Q—@&ôW'1`¹wR@eªj_Àf•’9)E@Q`*ù9Dr…q.ISaÔLÑn*)nF*$;-ª3H-(1`SS!@Qk-()˜Neª\)y—î1¹SeÑ-+À&ôTO4“(,+j\ÑS8Ù-(S9R@qª]@eªxdÈ@T²eª]@ê“(+-`4Ñ,`~*mQ`,)˜Ø-*ê×(F™`²e®`(Ó`4ÙËÀÑDG†{šM `)y`; qÑD 1-()yJ˜dF™@‘: qØ(³ÀÑDG†{šMM€]Ì7 qÑD 1Óf€eªî1¹J˜dF™@È"—%Ó0P5@DÜ`,^‡îEÙx³Ë-(SS!X×(S&F>æM4H³ÀbTS-êF(jû(6šE µ(×*`eªæF8×(SSeIÓ$k :yPæ\ \Õ4²e®`^Œ\Ò.b±Ø! à2æU¥È§ä19  g1+:`eª O*\,5@-ÊE ;]U]X*y) À|ȧÀÑD5È4+9EX4-()y—î1¹‹-(*y*à;]U]X*y) Àȧä6œFOÀ&ôWÅ$6ôi±àUWæF &æreÔä:kRò—iRN|Ù:“™+j_R`%H¨`À&mÉŒ]Ë-Ù4Lkª ª圥 ]FeE``4Û( P,æIY*ø“GÀÁ(¦E˜]Pï)F”T^*`HX(5@Pï)€¥†`Sj$5Ñ(5@F”T) e·SL4d*Á(¦‹¥`q®! ;N™ÀlÑ9 ä`ˆÄ84×*à"‰( RàSYWY¦E*Âô¥fî) f€VîO Æ%7+àR`b’+-:ltdµ@fî) f€VîO ØfîM…|LRJe®M€N™€Àfî) f€VîO Ôê#%|LRJe®M€N™Ó€¥1Û(¹—*c -d\e¦by qÙ4tL¼9]Çk*R`eª‡=HäÁsSBtr`9]Çk*Áø¥LÒ¨¥"ê›*]H]FåE%Xfôø¥]RÓºl"•ø¥ ÑÄ¥VîÏ%VîO%[4Æ^æø¥Ó"šÏ%frotz-2.44/src/test/crashme.inf0000644000175000017500000000453412527042452013427 00000000000000! Self-modifying reproducing Z-code. ! ! Generates random junk and sees how the interpreter behaves. If it's clever ! it shouldn't die except for @quit and stack overflow. ! ! inform \$MAX_STATIC_DATA=40000 crashme.inf ! ! Written by Evin Robertson 1999. Placed in public domain. Array randstuff -> 32767; Array checkfix -> 257; Global replay = 0; Array filename string "CRASHME.MEM"; #iftrue (#version_number <= 3); Constant Granularity = 2; Constant FileMult = 2; #endif; #iftrue (#version_number >= 4 && #version_number <= 5); Constant Granularity = 4; Constant FileMult = 4; #endif; #iftrue (#version_number >= 6 && #version_number <= 7); Constant Granularity = 4; Constant FileMult = 8; #endif; #iftrue (#version_number == 8); Constant Granularity = 8; Constant FileMult = 8; #endif; [ Main i a g r c l t game_size checksum; game_size = FileMult * (0-->13); ! game size r = randstuff % Granularity; if(r) r = Granularity - r; a = randstuff + r; c = a / Granularity; l = 32767 - r; if(replay) { print "You are running crashme's output. This will repeat the test run which generated this output.^"; } else { print "This program generates random Z-code which is run to test the robustness of your Z-machine interpreter. Most likely this will infinite loop. Do not run if you can't kill your interpreter when it is tightly looping.^Will attempt to write CRASHME.MEM which should be a valid Z-machine game which contains the same code that will be run.^"; } print "Press 'q' to abort.^"; @read_char 1 -> i; if(i == 'Q' or 'q') @quit; replay++; if(replay == 1) { for(i=0: i < l: i++) a->i = random(256) - 1; for(i=0: i < 13: i++) a->i = (emptyfunc * Granularity)->i; checksum = 0; for(i=0: i < l: i++) checksum = checksum + a->i; g = 0-->6; ! globals location for(i=0: i < 480: i++) checksum = checksum + g->i; for(i=0: i < 257: i++) { if(-255 <= checksum && checksum <= 0) { checkfix->i = -checksum; break; } else { checkfix->i = 255; checksum = checksum + 255; } } #IFV5; @save 0 game_size filename -> t; #ENDIF; } ! reduce the chances that it'll attempt to write over the same file for(i=0: i <= 11: i++) filename->i = 0; @call_vn c; print "^Done.^"; ]; [ emptyfunc; print "starting...^"; ]; frotz-2.44/src/test/unicode.inf0000644000175000017500000000441712527042452013433 00000000000000! Unicode Test v1.0 ! ! David Kinder, 2002. ! Switches xv5; Release 1; Zcharacter table + '@{a9}' '@{2122}' '@{20ac}'; [ main; print "^Unicode Test v1.0, by David Kinder^"; new_line; print "Testing the Unicode table. This sentence should end with Euro, copyright and trademark symbols @{20ac} @{a9} @{2122}^"; new_line; print "Now, testing print_unicode()...^"; charsets(); new_line; print "Now, testing input (ESC to quit)...^"; input_unicode(); ]; [ input_unicode key; print "Try inputing a character. Since the Euro symbol is declared in this file's Zcharacter table, "; print "it can be input even though it is not part of ISO Latin-1.^"; while (1) { @read_char 1 -> key; if (key == $1b) quit; font off; print "ZSCII $", (hex) key, " = "; font on; switch (key) { 8: print "delete"; 13: print "return"; 129: print "cursor up"; 130: print "cursor down"; 131: print "cursor left"; 132: print "cursor right"; 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144: print "function key ", key-132; 145, 146, 147, 148, 149, 150, 151, 152, 153, 154: print "keypad key ", key-145; 252: print "menu click"; 253: print "mouse double-click"; 254: print "mouse single-click"; default: print (char) key; } new_line; } ]; [ charsets; charset("Basic Latin",$0020,3); charset("Latin-1 Supplement",$00a0,3); charset("Latin Extended-A",$0100,4); charset("Greek and Coptic",$0360,5); charset("Cyrillic",$0400,8); charset("Arabic",$0600,8); ]; [ charset title start lines i; font off; new_line; print (string) title; new_line; for (i = 0: i < lines: i++) charrow(start+(i*32)); font on; ]; [ charrow start i; print (hex) start, " : "; for (i = 0: i < 32: i++) charout(start+i); new_line; ]; [ charout c exist; @"EXT:12S" c -> exist; if (exist & 1) @"EXT:11" c; else print (char) ' '; ]; [ hex x y; y = (x & $7f00) / $100; if (x < 0) y = y + $80; x = x & $ff; print (hexdigit) y/$10, (hexdigit) y, (hexdigit) x/$10, (hexdigit) x; ]; [ hexdigit x; x = x % $10; switch (x) { 0 to 9: print x; 10: print "a"; 11: print "b"; 12: print "c"; 13: print "d"; 14: print "e"; 15: print "f"; } ]; frotz-2.44/src/test/README0000644000175000017500000000255112527042452012164 00000000000000These programs are intended to excercise the interpreter that it is functioning according to spec. As far as I can determine, the programs are in the public domain or made freely available with no restrictions. Copyrights are retained by the original authors where noted. crashme.inf Self-modifying reproducing Z-code. Generates random junk to see how the interpreter behaves. A good interpreter shouldn't die except on fatal errors. Written by Evin Robertson in 1999. etude/ TerpEtude (etude.z5). Exercises the Z-machine for proper behavior. Does not test error-handling. Written by Andrew Plotkin in 1997 gntests.inf Assorted tests for checking proper handling of fonts, accents, input codes, colors, header, and timed input. Conforms to the Z-Spec 0.99 by Graham Nelson. Something of a weaker version of TerpEtude (above). Written by Graham Nelson as a collection of sample programs and unified by Andrew Plotkin in 1997. random.inf Inform Randomization Test v1.0. Assorted tests for checking the performance of the interpreter's random number generator. Written by David Griffith in 2002. strictz.inf Tests an interpreter's error-checking by attempting to cause all possible non-fatal errors. Written by Torbjorn Andersson in 1998. unicode.inf Unicode Test v1.0. Creates assorted unicode characters. Written by David Kinder in 2002. frotz-2.44/src/test/crashme.z50000644000175000017500000011100012527042452013174 00000000000000…ø…ù…ñ …ï010521B$Pý…î6.21€ ÀÐâô›B‡=Hä¥Bôk.ÍEB]Ó°¥BH$<$=$>$@$B$D$E$F$G$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$7$J CRASHME.MEM.," à?!€ÿº à#" 6Øâ Æ5Ô/âWÕ/ÿ È#›Œ#¬²·+¸X¸4ÇRùEœ¥öÁ—QqCº•Aå bWç?Uù*#+•Œÿé B [Ö!Ýà+#ù*#+•Œÿå bRà+#t •Œÿîà#" ÂàRà+#t •Œÿì Â]Ã/ÿ €/C €*5 BÈÂÓú_!á BŒâ+„áŒ)BÈÂÐú_!áŒâ'„áÿT ÿ •Œÿ ¾c…â C €"BÆB Ïú_!á Œâ'…╌ÿÝù¿²ä&“(²”å°²c&_.M…H²Eœ¥°²å[Á(¦@·Q—R:lW^—— BEŒ·AN²"&ãª#é»°A O#óÚ/#O#ú»°A![²fî) f€VîO È4×—àæ¿$»°A" $³Ù(%7+‹Àc7:l¦E*Âô¥A#T$³c7:l¦E*Âô¥A$Z$!³Pï)—¦E*Âô¥B ²fî) æ€B€¾Á—N²]F$ ^’€¥Œ ²rîe@æ€Á—E²—…²…˜>濲Ó-¨¥Bϲ¾…>—åUBѲ¾…p¦Åü¥U-ÿÁ—ÿH ŒAÿO²]Ó°¥ ŒAÿJ²&ž* ²×\Þ€¹Ï/Ú/#I²%L5È4 *y]Êàæ¿²U4€¥æ¿³¦E*Âô¥B€VC€QCʲ]F¤¥Œ²rîåE²šg%@IRRþX:l€¥-ÿÁ—ÿQ³…>¦E*Âô¥Á—ÿQ³…p¦ÀÁ(¦E˜]BJ²eXäŒB ÆCL²-Ó$µ@Œ BG²k €¥AU³%Û9*þê^€Á(¦E˜]²—%-ÿAÿP²:ed\ftåÓŒ“AÿR²4Ø Rà-䥌AÿJ²TתyŒsAÿJ²*)«ŒgAÿJ²!®Å%Œ[AÿL²zšMŠÜ¥ŒMAÿL²aÇEÓ°¥Œ?Aÿ L²!®E7ªeŒ1Aÿ L²zšMŠã%Œ#Aÿ J²*)ªåŒAÿ N²Pï)F”Ô¥ŒAÿ Z²Ãt¹ÙS$,f‡=Hf4Ò¥ŒëAÿ#²1Û(¹ÓÙfîY(ÐÚ/#O³¦E*Âô¥AÿY²]RSj— Ú/#O³¦E*Âô¥Á•ÿl²J›(¹€¥Ú/#O²4€¥Ú/#OAF²`q®! ršE HÐ(4R¥ôÚ/#O-²Ó€¥Ú/#O£a?ð²Ó€¥Ú/#O³¦E*Âô¥³¦E*Âô¥AÿU$)Ú/#O³¦E*Âô¥AÿJ²E˜&ŒAÿJ²EÜ¥Œ AÿE²–E² Ñ`Ú/#O³¦E*Âô¥B€+Õÿc€  ËFG²"&ã ǪŒ ²N™5Ó°¥²€¥²Ôê# ORW€¥æ¿²—àB[²;N™‹€¥5Ú/#OŒ<²¦`P^•*ùøÚ/#fÍOOBÆbÙ²¾it\ ~™5W‡=H俲4€¥Ú/#I²¦E*Âô§° ÀBÌÕÿcAà###›BÄ›à##!~BÄ›±aC±BGBðBÉBE‹ÿÿÉÿÉÿcC°‹ÿÿtà##…øBÉÙ!á¸p«6tà##…øBÉÙ!á¸o«tà##âBÔà##…ïBH ŒEà##ÀBÔà##BH Œ'à##BÔà##BH Œ AE  IÙ!á¸â«°Ù/#AÉÙ!á#¸­°à/#éÿAÿL²N™5Ó°¥ŒDAÿGªŒ;AÿL²"&㪌-AÿV²×SY:jÙ€¥æ¿²—åŒAÿQ²ØfîM€› 濲—å°ÉÀ €gIÿÏ/Ú/#O²¥ô¥É€ QÉ?׌;É×Qÿ-ÿO ×CSPtT–ŒÿæOÉÿÍOOCMbIo V²ÁrzHêÜæ¿²Áø¥Œ oÚ/#I°Ù/#éÿAÿD›AÿD›AÿMFÆCð›±±Ôh]@_SMÓ0\Ø6Jšfºd²™5ØŽF ]U(Ù-(+úL5È4 *j\Ù) e®`k5k%ȧ-;Vô2æH *j\Ù+\Ó&’ŸˆQ*9 ØúLP+-(Púc3+‹ÔjàårF!®M@:y*õ]Y*åHJ˜d: GÀe®`:1Ó-Ó;*4R¥H&€N™úL,S@ ÓF zš\O*^·+*\5SÙØ.1¹GÀF”UÓ0²äqÑDg*J¹4—;*ˆä˜¤HŠDHŠ@q®! a´j)êÀlÑ9 årF!®M@0Ò(5È4RyÓ`5@`Ò(Q*- qÑD(jeȧ ]FeE``4Û( P,æIY*ø“GÀÁ(¦E˜]Pï)F”T^*`HX(5@Pï)€¥†`Sj$5Ñ(5@F”T) e·SL4d*Á(¦‹¥`q®! ;N™ÀlÑ9 ä`ˆÄ84×*à"‰( RàSYWY¦E*Âô¥fî) f€VîO Æ%7+àR`b’+-:ltdµ@fî) f€VîO ØfîM…|LRJe®M€N™€Àfî) f€VîO Ôê#%|LRJe®M€N™Ó€¥1Û(¹—*c -d\e¦by qÙ4tL¼9]Çk*R`eª‡=HäÁsSBtr`9]Çk*Áø¥LÒ¨¥"ê›*]H]FåE%Xfôø¥]RÓºl"•ø¥ ÑÄ¥VîÏ%VîO%[4Æ^æø¥\Ó'ék!ª" »¥-Ñ*fÉEfrotz-2.44/src/test/random.inf0000644000175000017500000001267712527042452013274 00000000000000! Inform Randomization Test version 1.0, by David Griffith August 2002 ! ! Simulates dropping balls through a grid of pegs to test the ! interpreter's random number generator. A bellcurve should be the ! result. ! ! Also contains Andrew Hunter's randomization test which is found in the ! Zoom tarball. I'm not quite sure how it displays randomness, but I ! put it in here anyway. ! Constant LANES 80; Constant PEG_HEIGHT 40; ! For the spread random test Constant usualIter = 20000; Constant time = 50; Global iterations = 0; Global Granularity = 1; Global Height = 0; Global Dropcount_curr = 0; Global Dropcount_last = 0; Array bin --> LANES + 1; Array count --> 70; ! Drop a ball into a given lane. Return the number of the bin the ball ! eventually falls into. A ball should never have the opportunity to ! bounce to the left if it's already at the very left of the pegfield. ! Likewise for the very right. Therefore, balls should always be ! dropped into the very middle. ! [ drop initial_lane current_lane x y; Dropcount_curr++; current_lane = initial_lane; for (x = PEG_HEIGHT: x > 0: x--) { y = random(2); if (y == 1) { ! Ball bounces left. if (current_lane == 0) { @set_cursor 3 20; print "ERROR! Ball fell off left side!"; quit; } else { current_lane--; } } if (y == 2) { ! Ball bounces right. if (current_lane == LANES) { @set_cursor 3 20; print "ERROR! Ball fell of right side!"; quit; } else { current_lane++; } } } return current_lane; ]; [ display_bellcurve x y q top; @split_window Height; @set_window 1; @erase_window 1; for (x = 0: x <= LANES: x++) { @set_cursor 1 1; print "prev: ", Dropcount_last; @set_cursor 2 1; print "drops: ", Dropcount_curr; @set_cursor 3 1; print "granularity: ", Granularity; @set_cursor 1 20; print "Press to pause."; @set_cursor 2 20; print "Press 'q' to quit."; top = bin-->x / Granularity; if (top < 0) { top == 0; } if (top >= Height - 2) { return 0; } for (y = 0: y <= top: y++) { q = Height - y; @set_cursor q x; if (x == LANES/2) { print (char) '|'; } else { if (y == 0) { print (char) '='; } else { print (char) 'o'; } } } } return 1; ]; [ Main_bellcurve x y reps ix num crap; @set_window 0; @erase_window -1; print "^^This program drops ~balls~ from the very top-middle of a vertical two-dimensional field filled with pegs. The balls bounce off the pegs either left or right until they drop into one of several slots at the bottom where they stack up. There are one less than half as many levels of pegs as there are slots. This means that if a ball always bounces left, it will fall the leftmost slot. Likewise, a ball always bouncing right will fall into the rightmost slot. According to the laws of probability, if the bounces are totally random, a smooth bellcurve should appear when the balls are counted and put into a bar graph. This program displays such a bar graph.^"; print "^A ball which settles in the center of the field is represented by '|'.^"; print "A ball which settles to the left or right of the center is represented by 'o'.^"; print "^How many drops (1 through 9) per ball drawn? "; @read_char 1 ix; if (ix >= '1' && ix <= '9') { Granularity = ix - '0'; } else { Granularity = 1; } print "^^Press any key to begin. "; @read_char 1 crap; for (num = 0: num < LANES: num++) { bin-->num = 0; } Height = $24-->0; while (1) { for (reps = 1: reps <= GRANULARITY: reps++) { x = drop(LANES/2); } bin-->x = bin-->x + 1; y = display_bellcurve(); if (y == 0) { for (num = 0: num < LANES: num++) { bin-->num = 0; } @erase_window 0; @erase_window 1; dropcount_last = dropcount_curr; dropcount_curr = 0; display_bellcurve(); } @read_char 1 1 pause -> crap; if (crap == ' ') { @erase_window 1; @set_cursor 1 20; print "Paused."; @read_char 1 pause crap; } else { if (crap == 'q' || crap == 'Q') { @erase_window -1; rtrue; } } } ]; [ randomize_spread x y z; for (x = 0: x < usualIter: ) { for (z = 0: z < time: z++) { x++; y = random(69); count-->y = count-->y + 1; } iterations = iterations + time; Height = $24-->0; display_spread(); @read_char 1 1 pause -> z; if (z == ' ') { @read_char 1 z; } else { ! This does not exit the test correctly. if (z == 'q' || z == 'Q') { @erase_window -1; rfalse; } } } ]; [ display_spread x y z q largest; @split_window Height; @set_window 1; @erase_window 1; largest = 1; for (x = 0: x < 70: x++) { if (count-->x > largest) { largest = count-->x; } } @set_cursor 1 1; print iterations, " iterations"; for (x = 0: x < 70: x++) { z = (count-->x * (Height-2)) / largest; q = 3 + (x % 6); @set_colour q q; for (y = 0: y <= z: y++) { q = Height - y; @set_cursor q x; print (char) '#'; } } @set_colour 1 1; @set_cursor 1 1; ]; [ Main_spread x; for (x = 0: x < 70: x++) { count-->x = 0; } Height = $24-->0; while (1) { randomize_spread(); display_spread(); @read_char -> x; } ]; [ Main mychar; while (1) { print "^Inform Randomization Test version 1.0, by David Griffith August 2002^^"; print "1) Bellcurve graph.^"; print "2) Spread graph.^"; print "q) Quit.^"; print "^Please select a test: "; @read_char 1 mychar; switch (mychar) { '1': Main_bellcurve(); '2': Main_spread(); 'q': rtrue; default: print "^"; } } ]; [ pause; rtrue; ]; frotz-2.44/src/test/unicode.z50000644000175000017500000001100012527042452013177 00000000000000|}t›‘r020606B[º q6.21€  HäöüÄÖÜß«»ëïÿËÏáéíóúýÁÉÍÓÚÝàèìòùÀÈÌÒÙâêîôûÂÊÎÔÛåÅøØãñõÃÑÕæÆçÇþðÞУSR¡¿©!" ¬Qas…›B‡=Hä¥Bôk.ÍEB]Ó°¥BHMNOQSUVWXHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.," à?aÿº§»¯»²tp³*c.M€VîO%[S9%Ex¿EH²”å¿»Ãn°ËÜ7öACºÿ(ÿÿá[ÿ²ä`ˆÄ8˜$Ú/í²¦‡ ÿÉ/ÿýÿÿá[ÿ-ÿAÿJ²%Q«*ŒêAÿ J²]YêóŒÞAÿL²#Wb—ƒUŒÐAÿ‚N²#Wb—4òeŒÀAÿƒN²#Wb—*¯%Œ°Aÿ„N²#Wb—î±¹Œ Á•ÿ…†‡×Á•ÿˆ‰ŠÐÁ•ÿ‹ŒÉÁ•ÿŽW²/S#.R`A^€¥U„濌oÁ•ÿ‘’“ÔÁ•ÿ”•–ÍÁ•ÿ—˜™ÆAÿšU²A^TÉ øU‘濌CAÿüN²IShEÈÀ¥Œ3AÿýT²Jša@&š*ˆEÈÀ¥ŒAÿþT²Jša@aÓ2*ˆEÈÀ¥ŒÚ/H»ŒþɰùÍè ùÍë ùÍïùÍó`ùÍöùÍø°ÿ(ÿÿá[ÿ»Ú/U» bTV tÚ/Ý•ŒÿìÿÉ/ÿýÿÿá[ÿ°Ú/í²½€¥ B PtÚ/敌ÿð»°¾ ¿I É¾ ¿ŒÚH °É×BFT€IÿWÚ/ûÚ/ûWÚ/ûÚ/û°X-ÿBÿÌCÿ Èæ¿Œ;Aÿ H²˜¥Œ1Aÿ H²œ¥Œ'Aÿ H² ¥ŒAÿ H²¤¥ŒAÿH²¨¥Œ AÿE²¬¥°²å[Á(¦@·Q—R:lW^—— BEŒ·AN²"&ãªú»°A OÚ/[ »°A![²fî) f€VîO È4×—àæ¿»°A" "³Ù(%7+‹Àc7:l¦E*Âô¥A#T*³c7:l¦E*Âô¥A$Z2³Pï)—¦E*Âô¥B ²fî) æ€B€¾Á—N²]F$ ^’€¥Œ ²rîe@æ€Á—E²—…²…˜>濲Ó-¨¥Bϲ¾…>—åUBѲ¾…p¦Åü¥U-ÿÁ—ÿH ŒAÿO²]Ó°¥ ŒAÿJ²&ž* ²×\Þ€¹Ï/‘Ú/U²%L5È4 *y]Êàæ¿²U4€¥æ¿³¦E*Âô¥B€VC€QCʲ]F¤¥Œ²rîåE²šg%@IRRþX:l€¥-ÿÁ—ÿQ³…>¦E*Âô¥Á—ÿQ³…p¦ÀÁ(¦E˜]BJ²eXäŒB ÆCL²-Ó$µ@Œ BG²k €¥AU³%Û9*þê^€Á(¦E˜]²—%-ÿAÿP²:ed\ftåÓŒ“AÿR²4Ø Rà-䥌AÿJ²TתyŒsAÿJ²*)«ŒgAÿJ²!®Å%Œ[AÿL²zšMŠÜ¥ŒMAÿL²aÇEÓ°¥Œ?Aÿ L²!®E7ªeŒ1Aÿ L²zšMŠã%Œ#Aÿ J²*)ªåŒAÿ N²Pï)F”Ô¥ŒAÿ Z²Ãt¹ÙS$,f‡=Hf4Ò¥ŒëAÿ#²1Û(¹ÓÙfîY(ÐÚ/[³¦E*Âô¥AÿY²]RSj— Ú/[³¦E*Âô¥Á•ÿl²J›(¹€¥Ú/[²4€¥Ú/[AF²`q®! ršE HÐ(4R¥ôÚ/[-²Ó€¥Ú/[£a?ð²Ó€¥Ú/[³¦E*Âô¥³¦E*Âô¥AÿU:Ú/[³¦E*Âô¥AÿJ²E˜&ŒAÿJ²EÜ¥Œ AÿE²–E² Ñ`Ú/[³¦E*Âô¥B€+Õÿc€  ËFG²"&ã ǪŒ ²N™5Ó°¥²€¥²Ôê# ORW€¥æ¿²—àB[²;N™‹€¥5Ú/[Œ<²¦`P^•*ùøÚ/rÍO¡OBÆbÙ²¾it\ ~™5W‡=H俲4€¥Ú/U²¦E*Âô§° ÀBÌÕÿcAà#<§BÄ›à#<_BÄ›±aC±BGBðBÉBE‹ÿÿÉÿÉÿcC°‹ÿÿÁ• ÆA E B ÉC~Å B›ÉCûÅ  Jà!¸å¿°Ù/0AÉÙ#¸­°à/œéÿAÿL²N™5Ó°¥ŒDAÿGªŒ;AÿL²"&㪌-AÿV²×SY:jÙ€¥æ¿²—åŒAÿQ²ØfîM€› 濲—å°ÉÀ €gIÿÏ/—Ú/[²¥ô¥É€ QÉ?׌;É×Qÿ-ÿO ×CSPtT–ŒÿæOÉÿÍO¡OCMbIo V²ÁrzHêÜæ¿²Áø¥Œ oÚ/U°Ù/0éÿAÿD›AÿD›AÿMFÆCð›±±äjn"‰(eXd%H¨`À&mÉ:i*圥*c.M€eªšMÈQ*&*@-;aSeS!@a´j)S$;-Šjô`"•zî1¹Ó$\É*F^cÒ‘`â¦Ç„§tp³*c.M€:uk Ä(˜f€[Nd¿EH²”å7xNºeÓ0 æ#*\²˜:h(5@Z^€cÒ‘Ø*"&]IÓ-;-Ñ(¸`} æ#*\ñ(³€¥; ÓêÓWY[*`e´iÙØtdù‹ŽPDÙ:ep©Eœ¥æaÈ‘.Ì¥&eÓ…$cUV*ISä¥&eÓŠw*M*$¼Å—)PÓ$"•åÈ]ÑÅÈ×î ¥ ]FeE``4Û( P,æIY*ø“GÀÁ(¦E˜]Pï)F”T^*`HX(5@Pï)€¥†`Sj$5Ñ(5@F”T) e·SL4d*Á(¦‹¥`q®! ;N™ÀlÑ9 ä`ˆÄ84×*à"‰( RàSYWY¦E*Âô¥fî) f€VîO Æ%7+àR`b’+-:ltdµ@fî) f€VîO ØfîM…|LRJe®M€N™€Àfî) f€VîO Ôê#%|LRJe®M€N™Ó€¥1Û(¹—*c -d\e¦by qÙ4tL¼9]Çk*R`eª‡=HäÁsSBtr`9]Çk*Áø¥LÒ¨¥"ê›*]H]FåE%Xfôø¥]RÓºl"•ø¥ ÑÄ¥VîÏ%VîO%[4Æ^æø¥frotz-2.44/src/test/gntests.inf0000644000175000017500000002421712527042452013474 00000000000000 Global val; [ main; print "^This is a collection of the six test programs which came attached to the Z-Spec 0.99 file (spec.tex.) No changes have been made, except for the idiot menu system glued to the front.^"; while (1) { new_line; print "1: Fonts; 2: Accents; 3: InputCodes, 4: Colours, 5: Header, 6: TimedInput, 0: Exit^"; @read_char 1 val; switch (val) { '1': Main1(); '2': Main2(); '3': Main3(); '4': Main4(); '5': Main5(); '6': Main6(); '0': quit; } } ]; ! ------------------------------------------------------------------------- ! Fonts.inf ! ------------------------------------------------------------------------- [ Main1; ShowFonts(); ]; [ ShowFonts x; @erase_window -1; @split_window 10; FontAt(1,0,0); FontAt(2,1,0); FontAt(3,0,1); FontAt(4,1,1); @read_char 1 -> x; ]; [ FontAt font bx by i j x y; @set_window 1; y=1+5*by; x=2+34*bx; @set_cursor y x; print "Font ", font; @set_font font j; if (j==0) print " unavailable"; for (i=32:i<127:i++) { y=i/32+1+5*by; x=i%32+2+34*bx; @set_cursor y x; if (j==0) print "."; else @print_char i; } @set_font 1 j; @set_window 0; ]; ! ------------------------------------------------------------------------- ! Accents.inf ! ------------------------------------------------------------------------- [ Main2; ShowAccents(1); ShowAccents(4); ]; [ ShowAccents font x; @set_font font x; if (x==0) print_ret "Font ", font, " unavailable."; @erase_window -1; print "Accented characters test in font ", font, "^^"; print "Decimal code: character name plain ASCII equivalent^^"; print "155: @@155 a-umlaut ae^"; print "156: @@156 o-unlaut oe^"; print "157: @@157 u-umlaut ue^"; print "158: @@158 A-umlaut Ae^"; print "159: @@159 O-umlaut Oe^"; print "160: @@160 U-umlaut Ue^"; print "161: @@161 sz-ligature ss^"; print "162: @@162 quotation mark >> or ~^"; print "163: @@163 quotation mark << or ~^"; print "164: @@164 e-umlaut e^"; print "165: @@165 i-umlaut i^"; print "166: @@166 y-umlaut y^"; print "167: @@167 E-umlaut E^"; print "168: @@168 I-umlaut I^"; print "169: @@169 a-acute a^"; print "170: @@170 e-acute e^"; print "171: @@171 i-acute i^"; print "172: @@172 o-acute o^"; print "173: @@173 u-acute u^"; print "174: @@174 y-acute y^"; print "175: @@175 A-acute A^"; print "176: @@176 E-acute E^"; print "177: @@177 I-acute I^"; print "178: @@178 O-acute O^"; print "179: @@179 U-acute U^"; print "180: @@180 Y-acute Y^"; print "181: @@181 a-grave a^"; print "182: @@182 e-grave e^"; print "183: @@183 i-grave i^"; print "184: @@184 o-grave o^"; print "185: @@185 u-grave u^"; print "186: @@186 A-grave A^"; print "187: @@187 E-grave E^"; print "188: @@188 I-grave I^"; print "189: @@189 O-grave O^"; print "190: @@190 U-grave U^"; print "191: @@191 a-circumflex a^"; print "192: @@192 e-circumflex e^"; print "193: @@193 i-circumflex i^"; print "194: @@194 o-circumflex o^"; print "195: @@195 u-circumflex u^"; print "196: @@196 A-circumflex A^"; print "197: @@197 E-circumflex E^"; print "198: @@198 I-circumflex I^"; print "199: @@199 O-circumflex O^"; print "200: @@200 U-circumflex U^"; print "201: @@201 a-ring a^"; print "202: @@202 A-ring A^"; print "203: @@203 o-slash o^"; print "204: @@204 O-slash O^"; print "205: @@205 a-tilde a^"; print "206: @@206 n-tilde n^"; print "207: @@207 o-tilde o^"; print "208: @@208 A-tilde A^"; print "209: @@209 N-tilde N^"; print "210: @@210 O-tilde O^"; print "211: @@211 ae-ligature ae^"; print "212: @@212 AE-ligature AE^"; print "213: @@213 c-cedilla c^"; print "214: @@214 C-cedilla C^"; print "215: @@215 Icelandic thorn th^"; print "216: @@216 Icelandic eth th^"; print "217: @@217 Icelandic Thorn Th^"; print "218: @@218 Icelandic Eth Th^"; print "219: @@219 pound symbol L^"; print "220: @@220 oe-ligature oe^"; print "221: @@221 OE-ligature OE^"; print "222: @@222 upside-down !^"; print "223: @@223 upside-down ?^"; print "^Please press SPACE.^"; @read_char 1 -> x; ]; ! ------------------------------------------------------------------------- ! Inputcodes.inf ! ------------------------------------------------------------------------- [ Main3; InputCodes(); ]; [ InputCodes k; print "Keyboard input code testing^"; print "(Press keys to see how they respond, and press SPACE to finish.)^^"; for (::) { @read_char 1 -> k; print k, " "; switch(k) { ' ': return; 8: print "delete"; 13: print "return"; 27: print "escape"; 32 to 126: print "character '", (char) k, "'"; 129: print "cursor up"; 130: print "cursor down"; 131: print "cursor left"; 132: print "cursor right"; 133 to 144: print "function key f", k-132; 145 to 154: print "keypad ", k-145; 155 to 251: print "accented character '", (char) k, "'"; 252: print "menu click"; 253: print "mouse double-click"; 254: print "mouse click (single or double)"; default: print "error: code ", k, " should not have been returned"; } new_line; } ]; ! ------------------------------------------------------------------------- ! Colours.inf ! ------------------------------------------------------------------------- [ Main4; Colours(); ]; [ Colours fg bg; print "Colour display testing^"; if ((1->0)&1 == 0) "Fine: the interpreter says colours are unavailable."; print "The interpreter says colours are available. Let's see...^^"; for (fg=2:fg<10:fg++) { for (bg=2:bg<10:bg++) { if (fg ~= bg) { @set_colour fg bg; print (colourname) fg, " on ", (colourname) bg; @set_colour 1 1; new_line; } } } new_line; for (fg=2:fg<10:fg++) { for (bg=2:bg<10:bg++) { @set_colour fg bg; print "#"; } @set_colour 1 1; new_line; } print "^(Default colours.) Press SPACE to clear.^"; @read_char 1 -> fg; ]; [ Colourname x; switch(x) { 2: print "black"; 3: print "red"; 4: print "green"; 5: print "yellow"; 6: print "blue"; 7: print "magenta"; 8: print "cyan"; 9: print "white"; } ]; ! ------------------------------------------------------------------------- ! Header.inf ! ------------------------------------------------------------------------- [ Main5; Header(); ]; [ Header flag x y f; print "Interpreter declarations:^^"; flag=1->0; print "(In Flags 1...)^"; #IFV3; print "Status line unavailable?", (status) flag&16; print "Screen splitting available?", (status) flag&32; print "Default font has variable pitch?", (status) flag&64; #IFNOT; print "Colours available?", (status) flag&1; print "Boldface available?", (status) flag&4; print "Italic available?", (status) flag&8; print "Fixed-pitch font available?", (status) flag&16; print "Timed keyboard input available?", (status) flag&128; #ENDIF; #IFV5; print "^(In Flags 2. The following four questions have meaningful answers only if bits 3, 4, 5 and 7 of Flags 2 were set in advance: to do this, alter the game file by setting byte 16 to 184 and then run it again.)^"; flag=$10->1; print "Pictures available?", (status) flag&8; print "UNDO available?", (status) flag&16; print "Mouse available?", (status) flag&32; print "Sound effects available?", (status) flag&128; #ENDIF; #IFV5; print "^Interpreter (machine) number ", $1e->0, " version ", (char) $1f->0, "^"; print "^Screen height: "; x = $20->0; if (x==255) print "infinite^"; else print x, " lines^"; print "Screen width: "; x = $21->0; print x, " fixed-pitch font characters^"; print "Screen height in units: ", $24-->0, "^"; print "Screen width in units: ", $22-->0, "^"; print "Font height in units: ", $26->0, "^"; print "Font width (of a '0') in units: ", $27->0, "^^"; if ((1->0)&1 ~= 0) { print "Default background colour: ", (colourname2) $2c->0, "^"; print "Default foreground colour: ", (colourname2) $2d->0, "^^"; } for (f=1:f<5:f++) { @set_font f x; @set_font 1 y; print "Font ", f, " available?", (status) x; } #ENDIF; x=$32-->0; print "^Standard specification claimed by the interpreter: ", x/256, ".", x%256, "^"; #IFV5; print "^^(Press SPACE to clear.)^"; @read_char 1 -> x; #ENDIF; ]; [ Status f; if (f==0) " no"; " yes"; ]; [ Colourname2 x; switch(x) { 2: print "black"; 3: print "red"; 4: print "green"; 5: print "yellow"; 6: print "blue"; 7: print "magenta"; 8: print "cyan"; 9: print "white"; } ]; ! ------------------------------------------------------------------------- ! Timedinput.inf ! ------------------------------------------------------------------------- [ Main6; Timings(); ]; Global counter; [ Timings x; print "Testing timed input^^"; print "If you press no keys, five messages should appear, one second \ apart. If you do press a key, the test should finish at once.^^"; counter=0; @read_char 1 10 Interrupt x; print "^Test complete.^^"; print "Now the same test for 1/10th of a second (though probably not all \ interpreters will be fast enough to make the interval quite that \ brief).^^"; counter=0; @read_char 1 1 Interrupt -> x; print "^Test complete.^^"; print "^Please press SPACE.^"; @read_char 1 -> x; ]; [ Interrupt; print " message number ", ++counter, "^"; if (counter<5) rfalse; rtrue; ]; frotz-2.44/src/test/gntests.z50000644000175000017500000001600012527042452013245 00000000000000ÌÍÂàÀ@970311Bª›å6.11€ ¸ÂÎÚ›B‡=Hä¥Bôk.ÍEB]Ó°¥BHœž ¢¤¥¦§±¶»ÀÅÊÏÔÙÞãèíò÷ü $).38=BGLQV[`ejoty~ƒˆ’—.," à?5ÿº²äe®``F*#.R`Q`eªt+·Q—X9 I@9 ) f€eªŸ„bª  ²%D :*¾bª ²e]E|N€!¦MŠ` jê*`HÉ(³]!Ud RàeªÉ:™JO@cØeR‘iI4-( ^“d²”倻²%t.“g;ª È!Sg;« ÓWY%X`…t"‘SW`³­ ª*\³® .IIÓWY`t+®ä§ö-ÿAÿ1HŒ9Aÿ2HªŒ/Aÿ3HŒ%Aÿ4HùŒAÿ5HRŒAÿ6HYŒAÿ0CºŒÿc°°í?ÿÿê ùŒùŒùŒùŒö°ë646"4ﯲtÏ æ¿¾¿ K²Sf:&ž*  B76W Tt6"X TtﯠH²–EŒå¿•ŒÿȾë°Ú­Ú­°¾¿ U²tÏ æ¿³Sf:&*–Eí?ÿÿ²È!SeI æ#*_eXdL Ry€¥æ¿²圥²*!Ò "‰(½4×*àfI@±Ó† ŽÀ*Ú;fESd§”å²%4­ ›šJ&k Ê”å²%4® œšN&k Š”å²%4¯ šJ&k J”å²%4° ž¼jQYEœ¥²%4± ŸP¼jQYQEœ¥²%8¨  h¼jQYiEœ¥²%8© ¡|¼EÌ:]@”å²%8ª ¢j™.R`H×@Áx¦ÀRà%œ¥²%8« £j™.R`H×@Áp¦€Rà%œ¥²%8¬ ¤ šJ&k Eœ¥²%8­ ¥šJ&k Åœ¥²%8® ¦šJ&k Åœ¥²%8¯ §(¼jQY¨§²%8° ¨8¼jQY¸§²%8± ©†#Y(˜§²%<¨ ª †#Y(¨§²%<© «†#Y(¸§²%<ª ¬†#Y(Ч²%<« ­†#Y(è§²%<¬ ®†#Y(ø§²%<­ ¯¼e@†”å²%<® °(¼e@Š”å²%<¯ ±8¼e@Ž”å²%<° ²P¼e@””å²%<± ³h¼e@š”å²%@¨ ´x¼e@ž”å²%@© µŒ\Û(˜§²%@ª ¶ Œ\Û(¨§²%@« ·Œ\Û(¸§²%@¬ ¸Œ\Û(Ч²%@­ ¹Œ\Û(è§²%@® º¼2æm@†”å²%@¯ »(¼2æm@Š”å²%@° ¼8¼2æm@Ž”å²%@± ½P¼2æm@””å²%D¨ ¾h¼2æm@š”å²%D© ¿ˆ:èjKE]”å²%Dª À ˆ:èjKE] ”å²%D« Áˆ:èjKE]”å²%D¬ Âˆ:èjKE]”å²%D­ Ãˆ:èjKE]”å²%D® Ä¼!×#R.*tÅœ¥²%D¯ Å(¼!×#R.*tEœ¥²%D° Æ8¼!×#R.*tÅœ¥²%D± ÇP¼!×#R.*t…œ¥²E ¨ Èh¼!×#R.*tEœ¥²E © É—:l”å²E ª Ê¼]Ó0Åœ¥²E « Ë˜DØ4Ч²E ¬ ÌP¼b&a ””å²E ­ Í™:)(˜§²E ® Î™:)(̧²E ¯ Ï™:)(Ч²E ° Ð¼eÑ%@†”å²E ± ÑL¼eÑ%@“”å²E$¨ ÒP¼eÑ%@””å²E$© Ó(¼EÌ:]@Ê”å²E$ª ÔŠ‘9†gW(Ĩ§²E$« Õˆ).F&”å²E$¬ Ö ¼!I:1œ¥²E$­ ×9 DÓ%È-Ró´§²E$® Ø9 DÓ%ÈY4e¥œ¥²E$¯ Ù9 DÓ%È™6—L-”å²E$° Ú9 DÓ%ÈŠe ™´§²E$± ÛSS$zGR ‘”å²E(¨ Ü(¼EÌ:]@QEœ¥²E(© ÝPŠ‘9†gW(PŠ”å²E(ª ÞW%Eq4r`…œ¥²E(« ßW%Eq4r`¥œ¥²äV* ·+˜¤ˆEȧö°’°² xôéÓWY%@eXeÓ°§²ÄVêcA^`P)@6œ-+À]XV“$³Ó$]X``•Ä Š4nMØ4²圧ö濲€¥-ÿAÿ C°AÿJ²%Q«*Œ/Aÿ J²]YêóŒ#AÿJ²+šªŒBÿ ØCÿ~Ô²!¦\ÈeW€¸å¿²—ŒýAÿL²#Wb—ƒUŒïAÿ‚N²#Wb—4òeŒßAÿƒN²#Wb—*¯%ŒÏAÿ„N²#Wb—î±¹Œ¿Bÿ…ÛCÿײ/S#.R`A^eU„濌¢Bÿ‘×CÿšÓ²A^TÉ€¥U‘濌‰Bÿ›ÞCÿûÚ²*y) !¦\ÈeW€¸å¿²—ŒiAÿüN²IShEÈÀ¥ŒYAÿýT²Jša@&š*ˆEÈÀ¥ŒCAÿþ\²Jša@"."Ø:lE@Rà&š*—åŒ%²*÷RåtQ*€¥æ¿² SQ$S 4Û()SêgWÍI»Œþ³°û°²Fš\ ;DÞ*c.M…œ¥I '³nMEt5@:y*õ]Y*à`Þ`R4jø×(LÛÑñ¨²²-(O*^·+*\ØFš_êÛÑñ(²EY (²EH§”å B . B !aØ{Ú/9²“€¥Ú/9»•ŒÿÞ•ŒÿÑ» B ] B M{²–啌ÿó»•Œÿã²åx‰)fj9Fš_H¿•]X``•Ä Š4(×Eœ¥ö°-ÿAÿJ²&¢ŒSAÿH²ÝIŒIAÿJ²2êªeŒ=AÿJ²yQÆœŒ1AÿJ²:¨¥Œ%AÿL²HÌ*y˜¥ŒAÿJ²#ÆÌ¥Œ Aÿ G²q®åE°T°²ÓeWVêeW*"&\Ù:“`½圥²Ä:`q˜©EH²圥²Fš_f:&*–¥IÚ/<²ôE+ ÛÑñ¨µIÚ/<²Ù. lÎDÇEEÔ¥IÚ/<²nuI•;(4 RyÛÑñ¨µIÚ/<².II xôéÓWYÛÑñ¨µI€Ú/<²åxŽL.&3EH-( R1SŽM€.š\iXeÔO4Û((Ó:l/QÓcŠ_Rqx,;8«`…L4M àQ`q˜ªŠ]@aYÓÉlÓ!EtP P5Ø`9*àeª†I@-Ñ(x+9:lþe@%8P$°€i-*`_SÙÌÓEü§²®#:]XÛÑñ¨µIÚ/<²DL‰€f:&*–¥IÚ/<²Tk ÛÑñ¨µI Ú/<²jiK-Hgf:&*–¥I€Ú/<²ä:y*õ]Y*àÒ :jàORW€¥濲j_Ò`忲”å²äa)Sª9d½€¥ AÿN²:k:neEœ¥Œ 濲.MX”å²]JL994½€¥!濲nuI•;(4 Ry æ#*_œ¥²]JL )Ì7 :`jngô$濲”å²]JL994LMÙ`½€¥"濲”å²tO 5N1¹ÓS;8— &濲”å²tO qÉe Ô,¸`¿ÓS;8— '濲圥I €F²*,ÚG ÈA—SS$R4jåô,Ú/@²”å²*,ÚG .—)—SS$R4jåô-Ú/@²圥 B'¾¿¾²tÏ æ¿²ÛÑñ¨µÚ/<•ŒÿØ2²äc&M&] bª!Ë9eÔLDÎIIþ-(O*^·+*\½€¥×濲–EØ濲”å²å¾·+˜¤ˆ@f€"*åH¿”åö° G³Ð¥³«-ÿAÿJ²&¢ŒSAÿH²ÝIŒIAÿJ²2êªeŒ=AÿJ²yQÆœŒ1AÿJ²:¨¥Œ%AÿL²HÌ*y˜¥ŒAÿJ²#ÆÌ¥Œ Aÿ G²q®åE°[°²*c.M€eÒ) :uk%œ§²ËÔh]X`P+Ø`-Û(+Š`6šE µ(×`Rj "“$T×d²9`zš4·+ÀA^`eª*c a´j)nMØ4dM Eœ§ öS ¤²äeXdRUEY(²圥²tp5@`Ò(+t\$º%#-‹ÀaHRi¾e´i·Pæ>tdF :y*õ]Y*øŽF @,Ød Nš1 f€HÐ(5@:y*û [Ne@e¦d]Ê,¿Eœ§ öS¤²äeXdRUEY(²圥²äV* ·+˜¤ˆEȧö°²+ŠzHêÜ•è¿濲”åBÀ°ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥LÒ¨¥"ê›*]H]FåE%Xfôø¥]RÓºl"•ø¥ ÑÄ¥VîÏ%VîO%[4Æ^æø¥frotz-2.44/src/test/etude/0000755000175000017500000000000012527042452012467 500000000000000frotz-2.44/src/test/etude/etude.z50000644000175000017500000004100012527042452013770 00000000000000ôõìàêP970325B¶6.11€ ¸ÂÎÚ›B‡=Hä¥Bôk.ÍEB]Ó°¥BH!&+05:?DINSX]bglqv{€…Š”™ž£¨­²·¼ÁÆËÐÕÚßäéîóøý å æ ç è é ê ì í ï ðE"%)-0369<?BEHKNQTWZ]`cfilorux{~‚†ŠŽ’–šž¢¦©¬¯²µ¸»¾ÁÄÇËÎÑÓÕ×ÙÝàäçê Åì Iñõ´÷Sú¼Û ‡   e ÿ Ý.," à? Hÿº22 F B²Ôjà:y*õ]Y*à&Š`S "&:@f€.‘FœÓxù9D×j_R`Q`eªŸ„bª ²圥Œ;²Ôjà:y*õ]Y*à"&:X4tF4p+naÔÌæ¿²–E濲‹-(|¼)H§”åI €,²ÓeWVêeWÒ`4ÙF—) e]d8˜ÛÑñ(²”åŒ-²ÓeWVêeWÒ`4ÙF—) e]d8˜“„dlÎDÇEEȧI €4²ÓeWVêeWÒ`4ÙRU¦aß) ÇR)àe]d8˜ÛÑñ(²”åŒ5²ÓeWVêeWÒ`4ÙRU¦aß) ÇR)àe]d8˜“„dlÎDÇEEȧI €8²ÓeWVêeWÒ`4ÙÙ. z—S%WEÓ)%|+¹Žf:&*Eœ¥Œ9²ÓeWVêeWÒ`4ÙÙ. z—S%WEÓ)%|+¹ŽdP™ÛÑñ(²”åI €0²ÓeWVêeWÒ`4ÙnuIœ994+¹Žf:&*Eœ¥Œ1²ÓeWVêeWÒ`4ÙnuIœ994+¹ŽdP™ÛÑñ(²”åI  €.²ÓeWVêeWÒ`4ÙjiK-HgÄ\ŠÛÑñ(²”åŒ/²ÓeWVêeWÒ`4ÙjiK-HgÄ\Š“„dlÎDÇEEȧI€ €,²ÓeWVêeWÒ`4Ù.IIÓWYŽf:&*Eœ¥Œ-²ÓeWVêeWÒ`4Ù.IIÓWYŽdP™ÛÑñ(²”å»I €*²ÓeWVêeWÒ`4Ù¹jiP¹Žf:&*Eœ¥Œ+²ÓeWVêeWÒ`4Ù¹jiP¹ŽdP™ÛÑñ(²”å°I €/²ÓeWVêeWÒ`4ÙF—) e]d8˜ÛÑñ(²”å Œ0²ÓeWVêeWÒ`4ÙF—) e]d8˜“„dlÎDÇEEȧ  € ²ä:`eªh×(*4p³Ôh6šE áJŒ%²ä9`; pØ`:`eªh×(*4p³ÔhSQ$©E²j_. Ñ]Õ+Q`ÈA—SS$R4\zFe :l-(R:J`5F%W`¿Ó$ Rî~“dÑôsQ`.—)—SS$R4\{- ;LF àH×C:`S€eªI@"‘RåH¿”åÿ(ÿÿá[ÿ»²ð—$2àчDI€œ´§ B , B O{²åÜ•Œÿñ²— Ï/À­²”啌ÿÓÿÉ/ÿýÿÿá[ÿ°I €4²ÓeWVêeWÒ`4ÙRU¦aß) ÇR)àe]d8˜ÛÑñ(²”åŒ5²ÓeWVêeWÒ`4ÙRU¦aß) ÇR)àe]d8˜“„dlÎDÇEEȧI €8²ÓeWVêeWÒ`4ÙÙ. z—S%WEÓ)%|+¹Žf:&*Eœ¥Œ9²ÓeWVêeWÒ`4ÙÙ. z—S%WEÓ)%|+¹ŽdP™ÛÑñ(²”åI €0²ÓeWVêeWÒ`4ÙnuIœ994+¹Žf:&*Eœ¥Œ1²ÓeWVêeWÒ`4ÙnuIœ994+¹ŽdP™ÛÑñ(²”å»ñ²*c Q`*U4Ø;ê$xôE%|+¹–Eñ²åH²SRò .“d²Eȧñ²*c Q`;&EȾRàji*ñ:j$¿*w%È¥ñ²åH²SRò .“d²Eȧñ²*c Q`][*ø(¼mÉ*€e]ä²ñ²åH²SRò .“d²Eȧÿ(ÿÿá[ÿ²*c Q`-Ý)%sŽ'-*w ÚaÓ0etO Qk%L5È4+8À5F%Wîd¿–EÿÉ/ÿýÿÿá[ÿ²åH²SRò .“d²Eȧñ²*c Q`-Ý)%sŽ'-*w ÚaÓ0d¦+%[*w%[z*°%ü²ñ»²äNœŠŽF fþHîMÓ0gÑ+HeªŸ„bª QXtd*Ú:êÓeWVêeW`P ;DÞHîMIz*`³-(:j`*4pÀN™¦m@1-(gÑ+eªxÀeªx P²圥ññ²*c Q`‘$³Ù. +¹–Eñ»ññ²*c Q`‘$³êmWa@e]ä²ñ»ññ²*c Q`;&EÈ`][*ø(+¹–Eñ»ñññ²*c Q`‘$³Ù. ³êmWa@e]ä²ñ»²äeªtF4qÓ0]@1·:y) qÙ4etO Qk Ù4ÙØ`eªª*\; ]RÓ`+%L mS-SL4gz*ôHÓ c&eR*y`!\²圥ÿ(ÿÿá[ÿñ²*c Q`-Ý)%LR)*w%È¥ñ»ñ²*c Q`-Ý)%LdÑ9e]ä²ñ»ñ²*c Q`-Ý)%L+j_ *w%È¥ñ»ññ²*c Q`-Ý)%LR)`;&EÈ*w%È¥ñ»ññ²*c Q`-Ý)%LR)`][*ø(+¹–Eñ»ññ²*c Q`-Ý)%LdÑ9L+j_ *w%È¥ñ»ñññ²*c Q`-Ý)%LR)`;&EÈ`][*ø(+¹–Eñ»ÿÉ/ÿýÿÿá[ÿ²ä*i‹z*`+Eœ¥°­²¦‡ æ¿aL²¾Rü§Œ ²¾D\—„\³ SQ$¨æ¿²…ü§ °²-;eXgaÌMIZG.V. Ù:“`%Û;ReLM J‰j4•*æeÔOH1-+ •*æeÔOêV´aI4ê2j$²¾-(|¼)HªW^“*šb>{eªx]@jx9“)%;-;;"—]HeIÓ¨ED±E|§”å²À@jNM€eªOjO.R`e¦d ;naÔLG†{^šM84p×'}WPztdS†]8j0Ù;jÓ-Ó;>åLM Ä%‡à<ئ`5@`Ò(9“؆@-+ OjO.Rx *@f€@"’J“ÒRl];:lˆD ¦e+J®EW`²™5@Ó.ˆR@:y*õ]Y*øÑb€.‘Fœ-+ OjO.Rx@ÄY-+Àêtd h×y)Iþ-( M +Á,dÓ$×'He´a@Rqx*Ú:ê- ĺå|¦D+¾Å%å|=Át³t\F ÀiÑDR*ô‡E|§”å   vù õ ñA  ÍOÿûvùõ óÿ¿ÍOÿó vùõ öÿ¿ÍOÿóÍOÿûvù õ ùA  wù õ ü  ÍOÿûwùõ þÿþÍOÿó wùõÿþÍOÿóÍOÿûwù õ  xù õ  ÍOÿûxù õÍOÿó xùõ ÿýÍOÿóÍOÿûxùõ ÿý K²”å ÎŒ²”å Ú°²-;%ØV&{1-(! O*$4×*ø¾*hQ.M€lÑiX©¥4P(ªe|²žS@qÑD j4Óbª# zš\O*^·+*\¸` ;DÞ4FA@cW(5^ÕUF\R÷)GÅH§”å²t\5@]HRé``jQYØ

i¸Áp¦…`¿@-(|¼)`Þ`4Ù-(4×*à>Áx¸Ø%@%8ª`i¸Áp¦…``$®eH6œ+j\³ŽMt^@ÅH© ËR1SŽM€eªŸ„bª  ²E|RU:*``¦>Áx¸Ø©Å,³Ó$`¦<Áp¸Ø©Å(²™5@"“!ScXØ- eªŸ„bª  ²@iŽMt^@ÅH© ê—Rl`iŸ„bª  ²%DM DÙ*àê^ê#%H§”å ^B€tI Ïÿ(ÿÿá[ÿI ÅñI Åñ ÏÔcÝÏ/Ô­X F»Œ²€•ŒÿÝI ÑÿÉ/ÿýÿÿá[ÿI Åñ»»²>U@ 9Ždx¨EH¯àf€]U(Ù-;EØdL.-j]Sd+¹z*`Rà%H¹4S$5Ø*c%ȧ²•eÔOt ½t^FD¦`%tR)Ál(½Ù. ¦`etR)Ù. ¦`…t ;ª$¼qÉe¥;­ -Ý) ‘$¦`Åt ;ª$dÑ9;¯ -Ý) ‘$dÑ9ȧ²ä*y`¦‡Àö»A.C°»B0ÍC7ÉU0ŒÍOÿÿŒþ¢° A/²"“fôD4×*àfñ„4¸¾|¼HÈ5Ó(a*EY(¸—å ŒA /²"“fôD4×*àfñ„H¸¾|¼HÈ5Ó(bjr.MEà¿ ŒíA1²"“fôD4×*àfñ…[ß’ :j¸+ªü¥ Œ¹B '²"“fôD4×*àfñ—…T@忲—ÍOÿÿŒA_²"“fôD4×*à **eEà¥ÍOÿÿŒnA€Y²ji)nMI æ#*Ü¥ÍOÿÿŒSB€\²Ä`ˆÄ84×*à—忲—Œ5B›£ -ÿAÿL²#Wb—ƒUŒŒAÿ‚N²#Wb—4òeŒ|AÿƒN²#Wb—*¯%ŒlAÿ„N²#Wb—î±¹Œ\Á•ÿ…†‡×Á•ÿˆ‰ŠÐÁ•ÿ‹ŒÉÁ•ÿŽW²/S#.R`A^€¥U„濌+Á•ÿ‘’“ÔÁ•ÿ”•–ÍÁ•ÿ—˜™ÆAÿšR²A^TÉ øU‘濌BàUU›TÏ/Ô­ŒxBüY²ji)nMI æ#*Ü¥ÍOÿÿŒ]BÿD -ÿAÿüN²IShEÈÀ¥Œ+AÿýT²Jša@&š*ˆEÈÀ¥ŒAÿþQ²Jša@aÓ2*ˆEÈÀ¥ŒÍOÿÿ²ji)nMI æ#*Ü¥AF-ÿÁÿÿÿV²¾a´j)“„d!Ü¿Œ&Aÿ!²¾a´j)“„d!\L j1‘:jÓWY—åŒ A\-ÿÁÿÿÿS²¾a´j)“„d!Ü¿°²-;eXg:uk Q`*y) Æ#:1x³ÑD¿ æ#*_H*y*à:j‹*w%;ÙŽF @ôAS4reLM zšŽF aJÀEØd,4Ù æ#*_zš\O*^·+*\6š1¹Ôhzª$²—*JHê\4Ù-(O*^·+*\`jµS $P):!@+j_Ù5Ó0PSŠ\¼ Ø(²圥²®d*y*àÀ;8*+4êgWLP5@HÎL*zEœ¥€j²ä*y)$:uk%>€¥ÕPâšâšÍOš 俻К C° b%4Ð/š²"‰(¦‡¥æ¿²— ù'Ø»•ŒÿÚŒÿ–°²-;eXg:uk Q`*y) Æ#:1x³ÑD¿ æ#*_HgÕ(4×*øÁlS@qÑD)@q¦d4×*øÔjà:y*õ]Y*àe´idS@gÕ)%H5Ù¹EdP+:^`f€eªF:`ISh²圥€D²È!SeIÓWYÁøˆ ÃA.T²å™+nMØ5IEœ¥°²èQ*Áô¥æ¿²— ù'Ø»Œÿ¼°I€ €E ²Ôjà:y*õ]Y*à"&:X¾À;8ª*\;%|4ÙÙ‰„(˜V´_ eÒ) :uk%H§”åŒF ²Ôjà:y*õ]Y*à"&:X¾À;8ª*\;%|4ÙÙ‰„(˜“„djµRù.IIÓWYEœ§²*`zšê1Ó-;eXd³ØeW;`6šE µ(×Ù-(*‹“(*àaHRi@Ë-+Àµ(×Ù-(*‹“( mWx*`aHRi`³Ôjà:y*õ]Y*à;kM€eªÓ"—]Hd:NM€\Ù(X) À`R)ú0L|Ž¥H9``*y:ê.M@Q`eSØeW;`VªøÑDdM [*þ*L)M8`zš\O*^·+*\`S VôUWGÀ.:a®M€;8ú-j\²圥‚X²®dd² f€]Yjó4-(ÓJOEL\OÀS-*àA^4ê1Ó-(+@®dOÀA^4R eª*c q®E@; ;_SMÓ0²圥².II x¦‡Àˆ ÃA.C°» ÍOÿ£öS »Áÿ£²-(:NM€:y*÷j¹zM:“†`S ÑEIÙÑD²Š;-*àzš\O*^·+*\ QXtdjµRù.IIÓWY`Ràzš*^NLÙ) eª*c KRê“()M *&W $²¾—Ôjà:y*õ]Y*à4Ø-(gS…di€iÔh*ò:feI-(+ê.—(*`aHRi` DÕaIE|§”å -²Ôjà:y*õ]Y*à"&:X4tdjµRù.IIÓWY–@ ÎŒ(²Ôjà:y*õ]Y*à"&:X4V´_ eÒ) :uk%È ÚŒ¾ „²Ôjà:y*õ]Y*à ÑGeª.IÓ0O*^úW /S#.R`qÙ4P]šISgÈ G²-;;"—]Hd³]!Ud4ÙÔjà:y*õ]Y*à"&:XtdPjµRù.IIÓWYÙÑD²€¥ ÚŒ ÎŒ8²Ôjà:y*õ]Y*à ÑGeª.IÓ0O*^úW /S#.R`qÙ4L]šISd²€¥ Ú»Œý¨°-²Á¨•A F» ±I€ €E ²Ôjà:y*õ]Y*à"&:X¾À;8ª*\;%|4ÙÙ‰„(˜V´_ eÒ) :uk%H§”åŒF ²Ôjà:y*õ]Y*à"&:X¾À;8ª*\;%|4ÙÙ‰„(˜“„djµRù.IIÓWYEœ§²*`zšê1Ó-;eXd³Ôh6šE @ñ(P O*\.M@Q`:uk :`eªXhÑfa®ReH+j_Àe·)@aHRi`³ÀEÓ(,+¹ŽF µ(×@Ä9`eª.MXÕUF\d5@\Ù(,M@+j_Àe®_> "“'LSWÓeWVêeWØX:l-(M^ê# eÒ:læe@ ÚaIþÓ‘$i€:`ä8•E|§”å²ÓeW_UghÑGÀêˆ#W:l“!@UW "“$¦`+j_Àe®] :y*÷j¹·:y`.M@Q`e]d³Ó$5@S-*àg”¦m@N€mØ8ñ( -j#%Hy*à(È4:j`zš\Nºd6šE @]I\ÜLP4ÙÔh`"“eÓi@gÕ:lÓ$ %Ù:lÙEœ§²®dd² f€]Yjó4-(ÓJOEL\OÀS-*àA^4ê1Ó-(+@>U@%H¹“ÀEÓ(xg E`f€c4T5@eXd²圥².II]Ó0¦‡Àˆ ÃA.C°»²ê1ÓMÓ0+EH²圥ÍOÿ£ €½².II]Ó0¦‡ÀÕPâšâšÍOš ä” W»Ðš 6²Ôhk>UIÀ&NEÓ(²¾>U@%H¹4R e®`+E|§”åŒ5²Ôhk>UI€¹ КbT4Ð/šå¿•Œÿæ²%H§”åКAOКA.EŒŒÿC²*c eWIÓ*$²”åÁÿ£²-(:NM€:y*÷j¹zM:“†`S ÑEIÙÑD²Š;-*àzš\O*^·+*\ QXtdjµRù.IIÓWY`Ràzš*^NLÙ) eª*c KRê“()M *&W $²¾—Ôjà:y*õ]Y*à4Ø-(gS…di€iÔh*ò:feI-(+ê.—(*`aHRi` DÕaIE|§”å -²Ôjà:y*õ]Y*à"&:X4tdjµRù.IIÓWY–@ ÎŒ(²Ôjà:y*õ]Y*à"&:X4V´_ eÒ) :uk%È ÚŒ¾ „²Ôjà:y*õ]Y*à ÑGeª.IÓ0O*^úW /S#.R`qÙ4P]šISgÈ G²-;;"—]Hd³]!Ud4ÙÔjà:y*õ]Y*à"&:XtdPjµRù.IIÓWYÙÑD²€¥ ÚŒ ÎŒ8²Ôjà:y*õ]Y*à ÑGeª.IÓ0O*^úW /S#.R`qÙ4L]šISd²€¥ Ú°-•A. ²å[[*þ-]J "“'H²EH¦ ¥™:J$˜fîM…>€¥±²-;eXgqªeª\SWÓeWVêeWV´_8ê:l¦M*$Nºdx5@0Ò( :*@Ä.—]UEEL/*\zM:“ x` ; :`êz“$~—@³-(RRi·RUdVªøŽe e]dFê>Ó± ¼€q¦e[*àe]dS@4É>UIê.—(S@:y*÷j¹) ; qÙ45@/S#.R`A^E|§”å²-(RRi·RUd*4p6šE µ(׎e eª”] $1Û*edFê>naÇE@y*à;%Hzš SQ$(*4>U@J—(4×*ø`Rà2€È@M ).d5@1Û*`!¦\ÈeWØËÔh  gÕ) eªHSWaQ,²圥²ËÔh)@eª”] $1Û*edqÈ(³Ôjà:y*õ]Y*à;VîO.M€eªŽmS æ#*_L5È4d6šE3¾e¦d¸`5@]XV“aÇ:.gÀQ`eª†I@-Ñ(²圧²·*4%>Œ;jÌ¥ÕPâšâšâšGâšiâšvâšeâšnÍOš ä¿Ðš \²Ôhk>UIÀ&NEÓ(²”åŒ3²Ôhk>UI€¹ КbT4Ð/šå¿•Œÿæ²%ȧ°²-;eXgeªÓeWVêeWÇ:.gÀf€&€aÓ2*Ó$j9:±(gS&…dRRi`²圥I È Œ  €>²Ôjà:y*õ]Y*à"&:X¾À;8ª*\;%|4ÙÙ‰„(˜V´_ jiP²圥Œ?²Ôjà:y*õ]Y*à"&:X¾À;8ª*\;%|4ÙÙ‰„(˜“„djµRùS&…H§”å  ²KQ.M€-×c J›(²Eȧ¾ ÿ-ÿÁÿÿÿ@²m@,ÎEI¼€:y*õ]Y*à"&:X- ; &Š`S cUV—dgS&…d²”å ÍOÿÿŒŒAÿX²m@,ÎEIEœ¥  ŒrAÿT²m@cH!J%IEœ¥Œ\Aÿ(²S&€cH!J%I¾ji9 -×c J›(¿Eœ¥  Œ1²m@0Û(NS“êcQdQ*€¥æ¿²¼€D\—„\²”å   ‚:²äaÒj&eÓ0)M J›(²Eȧ¾ ÿ-ÿÁÿÿÿ@²m@,ÎEI¼€:y*õ]Y*à"&:X- ; &Š`S cUV—dgS&…d²”å ÍOÿÿŒ×AÿX²m@,ÎEIEœ¥  Œ½AÿT²m@cH!J%IEœ¥Œ§Aÿs²S&€cH!J%I¾ji9 aHRiTmE|²”å²ä5Ù¹EdP—d5Ø*c%L\OÀS-*àA^47x "“$gS&…d²¾ÓFOÀ:y*õ]Y*ø`+ª#Y:l-()M :M4 qÑD+:^`f€+¦#1x5@`Ò(QÓd`5@-×c Rj@Ë-;Qjø`iÔh)@e®`+ŠÀaHRi.I@Ô\RêåLSWÓeWVêeW“GÀcUV—gaÓ2*’SjS&…H¿”å²ZG.V*S&…>€¥ˆ ûA.K  Œ²äaHRiS&…H²Eœ¥¾ ÿ-ÿAÿR²S&€,ÎEIEœ¥Œ+²S&€0Û(NS“êcQdQ*€¥æ¿²¼€D\—„\²”å  Œ1²m@0Û(NS“êcQdQ*€¥æ¿²¼€D\—„\²”å   €³²ä5Ù¹EdP—d5Ø*c%L\OÀS-*àA^47xgS&…d²”å²M‘(šM4Áøˆ ûA.M ÍOÿþŒa²ä-×c jiP²Eȧ¾ ÿ-ÿAÿX²S&€,ÎEIEœ¥  Œ1²S&€0Û(NS“êcQdQ*€¥æ¿²¼€D\—„\²”å  »-ÿÁÿÿþT²*c Ó!QEIEœ¥Œ©ÁÿÿÿÊ €o²Ôjà:y*õ]Y*à"&:X4V´_ :M4%Lk eª¦jÚM4•"‰(+:^j$p©`:i9eÓ04Ù-(O*^·+*\ QXtdjµRù¹jiP¹–@ ÚŒV²Ôjà:y*õ]Y*à"&:X4tdjµRù¹jiP¹`i-(@`Û(¶jiPU%@]Yjó) …$³^ô—.M€e®`²€¥ ÎŒÚAÿ² €7²Ôjà:y*õ]Y*à"&:X4V´_ :M4%Lk ; %ÉL¸dRð–@ ÚŒv²Ôjà:y*õ]Y*à"&:X4tdjµRù¹jiP¹`iÓf# :M4 ,ÎEI@´q[*åLd 9 N™êgWLp©wR@eª¦jÚM4•"‰(³9 Ù SQ$²€¥ ÚŒ%Aÿt €3²Ôjà:y*õ]Y*à"&:X4V´_ :M4%LM ; &Š`²€¥ ÎŒ<²Ôjà:y*õ]Y*à"&:X4tdjµRù¹jiP¹`YÓf# ; &Š`OÜÅÈ ÚŒ®Aÿ© €O²Ôjà:y*õ]Y*à"&:X4V´_ :M4%LM ; &Š`²ŽL `; cUV—gKQeÕE@:M4%È ÎŒX²Ôjà:y*õ]Y*à"&:X4tdjµRù¹jiP¹`YÓf# ; &Š`OÜÅH:`,Èd³ÙV´_8ZG.V*¹jiP¹–@ Ú°²-;eXg9`zšL(É*w q®! ;%ØV&yIÒII8Ù*>ê.—(5@Vô2æHiÙ`²¾ÙØtdEF\5Y5W-;;"›*ê$x5@åp˜UH@´q[*åLRJ†IX¼€:hGI:lI@Rî1Ó Ó.ˆR@0Ò+…p ;DÞÒV—dÓdF˜:l*w i-*`:R).*GÀ[Nd²圧²®dd² f€]Yjó4-(ÓJOEL\OÀS-*àA^4·:yÀEÓ(,+¹Ó$5SÚ;%H9`eª.M@Q`e]d QXtdVªåL\VªøÓ$naª`R€[N"xP(É`zš\O*^·+*\:1¦m@fôhñ(;--S †IXEœ§²SM„e]d¦‡Àˆ ÃA.R²äeXd—eIEœ¥°²å™5ØØÀ-Ó EÓ(,+¹@”Q'yEȧº» Å» M²”Q'yEȧºÏ`W Ä €v²•eÔOty®d*y*àÀ;8*+4.b±Àe®`*z圥²Hzª]Ô$¿ ]; *^¤+:%Eœ¥ b'²€T濲— VTÏ/`­²”啌ÿØ ²å>€¥âšMâšÍOš 俻К bU4Ð/šA G•ŒÿëbS4Ð/šA.EŒ÷a€4Ð/šB0Ð4Ð/šC9H Œÿ b:4Ð/šB0€+4Ð/šC9Ý4Ð/šU0V t•ŒÿÅBÇcV²±(Ø( O*\fGJwR@ æ€æ¿²`Rà%H¹4];%L\ ; SeWþÙaQ,P ;DÞ-(*z‹•eÔOȧŒþmVÏ/`  ²- R¹:“Øtd+ :UER*y)%ȧŒþA¯Œþ<°ö«²*^¤+:%Et|¼HÈ5Ó(:y*õ]Y*à]*è; ܧ²þ†M7+€±S0:`Ê^\Ù4¦+(REIH¿”å²êEFá@Éÿ濲ºú:9Že Ó.—È<ÿå¿ÿ=ÿå¿ÿ>ÿå¿ÿ?ÿå¿ÿ»²*c8J±8Ó!@qÙ4|¼F!®M@ié`&ˆjJO H±%ȧ»²dP™Ete®`^Œ\Ò4+N™*c +j_Ù5Ó0L5@åp˜UH@¦cM€1-(+`L5Ø·Q—@&Š`L” 3F\ÓeJ- zš\O*^·+*\`|¼)qJ±8Ód²圥²t\4ÙFg*\³-;Vô2æHÀ4Û(; `Ld²Ž4L¸d5Ó@d QX`YÔh6šE N™ØcR(d`5@tjy‹†F åp’ :jMX`²Ž,S@-Ó$RJe®M€c7l(³ )-(|¼)L5H@ uØeÓ0O*^·+*_L5H@:kQH¸`O*^·+*_LM eªL*iJF: 9`zš(eÑDRkk $²”å°²Ó-()ŽNnM…LeWTŠgI(rîg*L²™4Ù†`** ‰ä‹%p©Eœ§²êEFa@Et§È!SeI”k eXd½†%*$Ñ;>4.b±À!¦\ÈeW`LR)`ji*ñ:j`inuIœ994gÑ+ȧ²È!SeI”k eXd½“S*$4Ù¦Å>Ó$<Áp]ELL `eXeI^ê#1x²”岎mS™+¹*c%t;%ceª†I@-Ñ(¸`Pàf€VîO eªŽmS*w KRêÙF8¦(É@nuI*c f€&€e®`²”å².II˜fîM€eXd½ˆ4Ó1I*c f€&€™4eÓmØ8ñ(¹Ó$gnaÇEEdO*^úW8¾RqxM@:y*÷j¹Ó-]JÈgFF>·:y`+¹Eü§²àj¨RNM€*c j:]X¥œ¥².c Q`eWIÓ.M€!¦\ÈeW`§tO8äc7(Ò«jc.M…œ¥²à"ê%Ù`½”å².f* ôL-W^…œ¥²)Î$½Œ\ÍEL``¤$‰`i-(+‹-(^œ¤§°-;)‹Ôjà:y*õ]Y*àµ(×`P)¦m@Ré:l4)ȧ-;)‹Ôjà:y*õ]Y*àµ(×`P)¦m@„\”d0²”唥”¥ñ™ꤥ—©SÊF4ð¥ñéEF1SäÅše»*%,*€­%,*¼•¥…$«¦@•¥…$«¦@…´¥%,h´¥%,hð­…$«º€­…$«º¼•¥%,%€­%,%¼•¥…$«¦ •¥…$«¦ …´¥¼jQY¥˜›P¼jQY¥˜œh¼jQY¥˜ÅsRDÚd½Äø¥…sRDÚd½Äü¥EsRDÚd½Å€¥cår.0Ùj꥘¡Áx¦ÅrÚS*`½ň¥Áp¦…rÚS*`½ÅŒ¥(¼jQY¥˜¤8¼jQY¥˜¥x¼jQY¥˜¦EsRDÚd½Åœ¥ÅsRDÚd½Å ¥¼eEt¦•%(¼eEt¦•E8¼eEt¦•eP¼eEt¦•…h¼eEt¦•¥x¼eEt¦•ÅÅpÈk*¥˜¯EpÈk*¥˜°ÅpÈk*¥˜±…pÈk*¥˜²EpÈk*¥˜³ÅpÈk*¥˜´¼2æmEt¦–¥(¼2æmEt¦–Å8¼2æmEt¦–åP¼2æmEt¦—h¼2æmEt¦—%Åq—j¥˜ºEq—j¥˜»Åq—j¥˜¼…q—j¥˜½Eq—j¥˜¾¼!×#R.*t½Åü¥(¼!×#R.*t½Æ€¥8¼!×#R.*t½Æ„¥P¼!×#R.*t½ƈ¥h¼!×#R.*t½ÆŒ¥Åq]Iq+¥t¦˜…Eq]Iq+¥t¦˜¥Åq]Iq+¥t¦˜Å…q]Iq+¥t¦˜åEq]Iq+¥t¦™¼]Ó0½Ƥ¥ÅrîM…t¦™EP¼b&a¥t¦™e…s ¥˜Ì¼eÑ%Et¦™¥L¼eÑ%Et¦™ÅP¼eÑ%Et¦™åÅs.E*¥˜Ðes.E*¥˜Ñ…s.E*¥˜ÒEr.0Ùj꥘ÓÄ(¼EÌ:]Et¦š… ¼!I:1½ÆÔ¥q %ÑDÅt¦šÅe´^et¦šå+-¥˜Ø-Ró¥˜ÙY4½Æè¥VšM%sHôD½Æì¥QEr.0Ùj꥘܄(¼EÌ:]Et¦›¥:{*ø(¼…t¦›Å:{*ø(¼¥t¦›åj_Òeê!Sd4Ó1X4™*õYé*ª*\ DÌ`LÑ{à¥z*$«¹F—) e]ä¥ZG.V. Ù:“`%Û;ReL*F:iªåÈ!SeI æ#*\k5ë%M‘(¼A^Ó×YzF%r.M@:uë%.IIM‘(¼A^Ó×Y.IIzF%r.M@:uë%·(¼F†%Ó0,NºdºjS&€ ÕîEÙø¥·:y:lê.—(iÙeÓ°¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥ÁsSBtr`9]Çk*Áø¥LÒ¨¥"ê›*]H]FåE%Xfôø¥]RÓºl"•ø¥ ÑÄ¥VîÏ%VîO%[4Æ^æø¥frotz-2.44/src/test/etude/README0000644000175000017500000000127512527042452013274 00000000000000This archive contains the TerpEtude game file (etude.z5). It also contains all the Inform 6 source code for TerpEtude: etude.inf accents.inc accentin.inc color.inc division.inc exittext.inc givenin.inc graham.inf header.inc styles.inc timedch.inc timedstr.inc undo.inc I strongly recommend that TerpEtude only be distributed in this format -- that is, with game file and source code together. A programmer who wants to test his interpreter can't really know what he's doing unless he sees exactly what the tests are. I've also thrown in gntests.z5 and gntests.inf; this program comprises the six test programs that Graham appended to the Z-Spec TeX document. (Taken from the 0.99 spec.tex file.) frotz-2.44/src/test/etude/etude.inf0000644000175000017500000001004712527042452014215 00000000000000Switches xv5; Release 2; Global errflag; Constant SectionOk = "This aspect of your interpreter appears to behave according to spec.^"; Constant SectionBad = "This aspect of your interpreter appears to behave WRONG.^"; Include "header.inc"; Include "color.inc"; Include "styles.inc"; Include "division.inc"; Include "accents.inc"; Include "accentin.inc"; Include "timedch.inc"; Include "timedstr.inc"; Include "givenin.inc"; Include "undo.inc"; Include "exittext.inc"; Array optionlist table [; "Version" Version; "Recent changes to TerpEtude" History; "Header flags analysis" TestHeader; "Styled text" TestStyles; "Colored text" TestColor; "Multiplication, division, remainder" TestDiv; "Accented character output" TestAccents; "Single-key input" TestAccentsCharIn; "Full-line input" TestAccentsIn; "Timed single-key input" TestTimedChar; "Timed full-line input" TestTimedString; "Pre-loading of input line" TestGivenInput; "Undo capability" TestUndo; "Printing before quitting" TestExitText; ]; [ main; new_line; Version(); new_line; mainloop(); print "Goodbye.^"; quit; ]; Constant INBUFSIZE 80; Array inbuf -> INBUFSIZE; [ mainloop printmenu inbufvar numopts ix cx len val; numopts = optionlist-->0 / 2; printmenu = 1; while (1) { if (printmenu) { print "Options: (hit Enter by itself to display this menu)^"; print " . (period): Exit TerpEtude^"; for (ix=0: ix(ix*2+1), "^"; } printmenu = 0; } print "^> "; inbuf->0 = (INBUFSIZE-3); inbuf->1 = 0; inbufvar = inbuf; ix = 0; @aread inbufvar ix; new_line; len = inbuf->1; cx = 0; while (cx < len && inbuf->(2+cx) == ' ') cx++; if (cx < len && inbuf->(2+cx) == '.') break; if (cx == len || inbuf->(2+cx) < '0' || inbuf->(2+cx) > '9') { printmenu = 1; continue; } val = 0; while (cx < len && inbuf->(2+cx) >= '0' && inbuf->(2+cx) <= '9') { val = val * 10 + (inbuf->(2+cx) - '0'); cx++; } if (val < 1 || val > numopts) { print "Please enter a value from 1 to ", numopts, ", or ~.~ to exit, or hit Enter by itself to display the menu of options.^"; continue; } ix = optionlist-->(val*2); if (ix==0) { print "That option is not yet implemented.^"; continue; } indirect(ix); } ]; [ GetKey ix; @read_char 1 ix; return ix; ]; [ Version; print "TerpEtude: A Z-machine Interpreter Exerciser^"; print "By Andrew Plotkin (erkyrath@@64netcom.com)^"; print "Release ", (0-->1) & $03ff; print " / built with Inform v"; inversion; new_line; print "Tests compliance with Z-Machine Standards Document 0.99.^"; new_line; print "NOTE: This program does not test everything in the Z-Spec. Passing all the tests in this program does NOT guarantee that your interpreter is Z-Spec-compliant.^^"; print "For that matter, this program may have mistakes in it. I don't think it does, but you should not assume it is the Fount of All Z-Machineness. If you find something strange, check the Z-Spec, check existing interpreters, check Infocom's interpreters, and then send me mail if you're still confused.^"; ]; [ History; print "In the beginning, TerpEtude was written. That was release DRAFT-1.^^"; print " Release 2:^Accented-out test: Added ability to display characters in bold, underline, and fixed-width styles.^"; print "Accented-out test: Noted that >> and << are, in fact, tested correctly.^"; print "Given-text test: It's the game file's job to print the given text before it calls @@64read. Fixed test to do this.^"; print "Timed-string test: Changed test to do both ~invisible~ and ~visible~ interrupts (only one interrupt in three actually prints text.)^"; print "^ Upcoming Test Features:^"; print "List of terminating characters^Fonts^Stream 3 nesting^"; print "^ Credits:^"; print "Title: Jon Ferro^"; print "Spec Aid: Graham, SJ, PDD, and the rest of the crowd^"; ]; frotz-2.44/src/test/etude/exittext.inc0000644000175000017500000000134712527042452014765 00000000000000 [ TestExitText ix; print "This tests if you can read text which is displayed immediately before the program quits. (It is not clear whether this is covered by the Z-Spec. However, some games -- including some original Infocom games -- display important closing text and then immediately quit.)^^"; print "Hit ~.~ to return to the main menu, or any other key to print a line of text and then quit. If the line of text does not appear, or appears and vanishes too quickly to read, your interpreter will have trouble with those games.^^"; print "ClosingText> "; ix = GetKey(); if (ix == '.') { print "^Test aborted.^"; return; } print "^^This is a final line of text. Goodbye.^"; quit; ]; frotz-2.44/src/test/etude/division.inc0000644000175000017500000000333112527042452014726 00000000000000 [ TestDivLine str answer correct; print (string) str, " = ", answer; if (answer == correct) { print " (ok)^"; } else { print " (ERROR, should be ", correct, "!)^"; errflag = 1; } ]; [ TestDiv x y z; print "This tests signed multiplication, division, and modulo operations. All these operations are supposed to be signed. (The Z-Spec 0.2 erroneously says they are unsigned; this is corrected in 0.99.)^^"; print "I am assuming the convention that division always rounds towards zero (not towards negative infinity), and (A % B) always has the same sign as A. These conventions seem to be common among existing C/C++ compilers. The Infocom interpreters also follow these conventions. (But they are not guaranteed by the C and C++ standards. Those only require that (A/B)*B + (A%B) == A, for all A and all nonzero B.)^^"; errflag = 0; x = 13; y = 5; z = x * y; TestDivLine("13 * 5", z, 65); x = 13; y = -5; z = x * y; TestDivLine("13 * -5", z, -65); x = -13; y = 5; z = x * y; TestDivLine("-13 * 5", z, -65); x = -13; y = -5; z = x * y; TestDivLine("-13 * -5", z, 65); x = 13; y = 5; z = x / y; TestDivLine("13 / 5", z, 2); x = 13; y = -5; z = x / y; TestDivLine("13 / -5", z, -2); x = -13; y = 5; z = x / y; TestDivLine("-13 / 5", z, -2); x = -13; y = -5; z = x / y; TestDivLine("-13 / -5", z, 2); x = 13; y = 5; z = x % y; TestDivLine("13 % 5", z, 3); x = 13; y = -5; z = x % y; TestDivLine("13 % -5", z, 3); x = -13; y = 5; z = x % y; TestDivLine("-13 % 5", z, -3); x = -13; y = -5; z = x % y; TestDivLine("-13 % -5", z, -3); if (errflag == 0) { print "^", (string)SectionOk; } else { print "^", (string)SectionBad; } ]; frotz-2.44/src/test/etude/color.inc0000644000175000017500000000160012527042452014215 00000000000000 Array colortab --> "" "" "Black" "Red" "Green" "Yellow" "Blue" "Magenta" "Cyan" "White"; [ TestColor val fg bg claim; val = $0->1; if (val & 1) { print "Interpreter claims that colored text IS available.^"; claim = 1; } else { print "Interpreter claims that colored text IS NOT available.^"; claim = 0; } if (claim) print "^In the square below, you should see"; else print "^If it was, in the square below, you would see"; print " vertical stripes of background color (matching the column headers) and horizontal rows of foreground color (that is, all # marks in a row the same color.)^"; font off; new_line; print "Bk Rd Gr Yl Bl Mg Cy Wh^"; for (fg=2:fg<10:fg++) { for (bg=2:bg<10:bg++) { @set_colour fg bg; print "## "; } @set_colour 1 1; print ": ", (string)colortab-->fg, "^"; } font on; ]; frotz-2.44/src/test/etude/timedstr.inc0000644000175000017500000000550712527042452014744 00000000000000 [ TestTimedString claim inbufvar ix; ix = $0->1; if (ix & 128) { claim = 1; print "Your interpreter claims (by its header bit) that it DOES support timed input.^^"; } else { claim = 0; print "Your interpreter claims (by its header bit) that it DOES NOT support timed input.^^"; } print "When you begin this test, you should be able to enter a line of input in the usual fashion. Every three seconds, a line of text will appear. (If the lines appear at the rate of one every thirty seconds, your interpreter is using the incorrect timing rate caused by an old bug in ZIP.)^^"; print "Interrupts actually are occuring once per second; every third interrupt prints a line of text, and the other two have no visible effect. After each line, your input should be redrawn so that you can continue typing and editing it.^^"; print "Hit ~.~ to return to the main menu, or any other key to begin the test. Type ~.~ on a line by itself to stop the test.^^"; print "TimedString> "; ix = GetKey(); if (ix == '.') return; new_line; print "Beginning test...^^"; timedcharflag = -93; timedcharcounter = 0; while (1) { print "TimedString> "; inbuf->0 = (INBUFSIZE-3); inbuf->1 = 0; inbufvar = inbuf; ix = 0; @aread inbufvar 0 10 TimedStringSplot ix; new_line; if (inbuf->1 == 0) print "You just typed a blank line. (Type ~.~ to stop this test.)^^"; else { print "You just typed ~"; for (ix=0: ix1: ix++) print (char)(inbuf->(2+ix)); print "~.^^"; } if (inbuf->1 == 1 && inbuf->2 == '.') break; } print "Test terminated.^"; if (timedcharflag == -93) { print "The timing interrupt function was not called at all. Either your interpreter does not support timed input, or you terminated the test before one second elapsed. (Or your interpreter has the ~slow~ bug and you terminated the test before ten seconds elapsed.)^^"; if (claim == 0) print "Your interpreter claims to not support timed input. ", (string)SectionOk; else print "Your interpreter claims to support timed input. ", (string)SectionBad; } else { if (timedcharflag == 0) { print "Your interpreter calls the timing interrupt function with no arguments. "; if (claim == 0) print "This is correct, except that your interpreter claims not to support timed input at all. ", (string)SectionBad; else print (string)SectionOk; } else { print "Your interpreter calls the timing interrupt function with an argument. ", (string)SectionBad; } } ]; [ TimedStringSplot arg; timedcharflag = arg; timedcharcounter++; if (timedcharcounter == 3) { timedcharcounter = 0; print "^[Every three seconds....]^TimedString> "; } return 0; ]; frotz-2.44/src/test/etude/header.inc0000644000175000017500000000312512527042452014333 00000000000000 [ TestHeader val val2; val = $32->0; val2 = $32->1; if (val == 0 && val2 == 0) { print "Your interpreter does not claim to follow any particular version of the Z-Spec.^^"; } else { print "Your interpreter claims to follow revision ", val, ".", val2, " of the Z-Spec.^^"; } val = $0->1; if (val & 1) { print "Interpreter claims that colored text IS available.^"; } else { print "Interpreter claims that colored text IS NOT available.^"; } if (val & 4) { print "Interpreter claims that emphasized (bold) text IS available.^"; } else { print "Interpreter claims that emphasized (bold) text IS NOT available.^"; } if (val & 8) { print "Interpreter claims that italic (or underlined) text IS available.^"; } else { print "Interpreter claims that italic (or underlined) text IS NOT available.^"; } if (val & 16) { print "Interpreter claims that fixed-width text IS available.^"; } else { print "Interpreter claims that fixed-width text IS NOT available.^"; } if (val & 32) { print "Interpreter claims that sound effects ARE available.^"; } else { print "Interpreter claims that sound effects ARE NOT available.^"; } if (val & 128) { print "Interpreter claims that timed input IS available.^"; } else { print "Interpreter claims that timed input IS NOT available.^"; } new_line; val = $10->1; if (val & 16) { print "Interpreter claims that ~undo~ IS available.^"; } else { print "Interpreter claims that ~undo~ IS NOT available.^"; } ]; frotz-2.44/src/test/etude/styles.inc0000644000175000017500000000526612527042452014436 00000000000000 [ TestStyles val; val = $0->1; if (val & 4) { print "Interpreter claims that emphasized (bold) text IS available.^"; } else { print "Interpreter claims that emphasized (bold) text IS NOT available.^"; } if (val & 8) { print "Interpreter claims that italic (or underlined) text IS available.^"; } else { print "Interpreter claims that italic (or underlined) text IS NOT available.^"; } if (val & 16) { print "Interpreter claims that fixed-width text IS available.^"; } else { print "Interpreter claims that fixed-width text IS NOT available.^"; } new_line; style bold; print "Test of emphasized (bold) text."; style roman; print "^...normal font...^"; style underline; print "Test of italic (or underlined) text."; style roman; print "^...normal font...^"; style reverse; print "Test of reverse-video text."; style roman; print "^...normal font...^"; font off; print "Test of fixed-width text (using ~font off~, which sets a header bit)."; font on; print "^...normal font...^"; @set_text_style 8; print "Test of fixed-width text (using ~@@64set_text_style 8~)."; @set_text_style 0; new_line; print "^Now we will try combining styles. The Z-Spec does not require interpreters to display combined styles, so the lines below may not have all the styles they say they do.^^"; style bold; style underline; print "Test of bold, italic text."; style roman; new_line; style bold; style reverse; print "Test of bold, reverse text."; style roman; new_line; style underline; style reverse; print "Test of italic, reverse text."; style roman; new_line; style bold; style underline; style reverse; print "Test of bold, italic, reverse text."; style roman; new_line; print "^The following are all printed with ~font off~ (that is, the header bit remains set, even though ~style roman~ statements occur.)^"; font off; style bold; print "Test of fixed, bold text."; style roman; new_line; style underline; print "Test of fixed, italic text."; style roman; new_line; style reverse; print "Test of fixed, reverse text."; style roman; new_line; style bold; style underline; print "Test of fixed, bold, italic text."; style roman; new_line; style bold; style reverse; print "Test of fixed, bold, reverse text."; style roman; new_line; style underline; style reverse; print "Test of fixed, italic, reverse text."; style roman; new_line; style bold; style underline; style reverse; print "Test of fixed, bold, italic, reverse text."; style roman; new_line; font on; print "^End of styles test.^"; ]; frotz-2.44/src/test/etude/givenin.inc0000644000175000017500000000222112527042452014536 00000000000000 [ TestGivenInput ix inbufvar; print "This tests whether your interpreter supports being handed input by the game file. (For example, after a function key is hit in Beyond Zork, the command prompt appears with text already in place -- whatever text you had typed before you interrupted it with the function key.)^^"; print "The command prompt below should appear with the word ~Given~ already visible after it. You should be able to type more characters, or go back and edit the given character as if you had typed them yourself.^^"; print "If you see the word ~Given~ twice, your interpreter is printing the given characters, which it shouldn't (that's the responsibility of the game file.)^^"; print "Preload> Given"; inbuf->0 = (INBUFSIZE-3); inbuf->1 = 5; inbuf->2 = 'G'; inbuf->3 = 'i'; inbuf->4 = 'v'; inbuf->5 = 'e'; inbuf->6 = 'n'; inbufvar = inbuf; ix = 0; @aread inbufvar ix; if (inbuf->1 == 0) print "You just typed a blank line.^"; else { print "You just typed ~"; for (ix=0: ix1: ix++) print (char)(inbuf->(2+ix)); print "~.^"; } ]; frotz-2.44/src/test/etude/accentin.inc0000644000175000017500000000607212527042452014673 00000000000000 ! Prints out the name of a character. This also prints a warning ! if the character should not be accepted by a particular input ! method (given in restr.) ! restr = 1 for by-line, 2 for by-char, 0 for no warning [ DescChar ch restr res; res = 0; ! 0 for ok everywhere, -1 for never ok, 2 for ok only ! in read_char if (ch == 8) { print "control character 'ctrl-H' (z-machine 'delete')"; res = 2; } else if (ch == 13) { print "control character 'ctrl-M' (z-machine 'newline')"; res = 2; } else if (ch == 27) { print "control character 'ctrl-[' (z-machine 'escape')"; res = 2; } else if (ch < 32) { print "control character 'ctrl-", (char)(ch+64), "'"; res = -1; } else if (ch == 127) { print "control character 'delete'"; res = -1; } else if (ch == 128) { print "undefined character"; res = -1; } else if (ch < 128) { print "ASCII character '", (char)ch, "'"; } else if (ch < 155) { res = 2; switch (ch) { 129: print "cursor up"; 130: print "cursor down"; 131: print "cursor left"; 132: print "cursor right"; 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144: print "function key ", ch-132; 145, 146, 147, 148, 149, 150, 151, 152, 153, 154: print "keypad key ", ch-145; } } else if (ch < 224) { print (string)AccentList-->(ch - 155 + 1); } else if (ch < 252) { print "undefined character"; res = -1; } else if (ch < 255) { res = 2; switch (ch) { 252: print "menu click"; 253: print "mouse double-click"; 254: print "mouse single-click"; } } else { res = -1; print "undefined character"; } if (restr == 1) { switch (res) { -1: print " (should NOT occur)"; 2: print " (should NOT occur in full-line input)"; } } else if (restr == 2) { switch (res) { -1: print " (should NOT occur)"; } } ]; [ TestAccentsIn inbufvar ix ch len; print "This tests input of accented (actually, all) characters. Enter a line of text; it will be broken down, and you will see a list of what characters your interpreter thought you typed. Remember that the interpreter is supposed to reduce everything to lower-case.^^"; print "Hit Enter by itself to return to the main menu.^"; while (1) { print "^AccentedInput> "; inbuf->0 = (INBUFSIZE-3); inbuf->1 = 0; inbufvar = inbuf; ix = 0; @aread inbufvar ix; new_line; len = inbuf->1; if (len == 0) return; for (ix=0: ix(2+ix); print "code=", ch, ": "; DescChar(ch, 1); new_line; } } ]; [ TestAccentsCharIn ch; print "This tests input of accented (actually, all) characters. Type characters; you will see what characters your interpreter thought you typed. Hit ~.~ to return to the main menu.^^"; while (1) { print "AccentedInput> "; ch = GetKey(); if (ch == '.') { print "^^Test finished.^"; return; } print "^code=", ch, ": "; DescChar(ch, 2); new_line; } ]; frotz-2.44/src/test/etude/accents.inc0000644000175000017500000000674712527042452014540 00000000000000 ! This table is in order and complete from code 155 to 223 inclusive. Array AccentList table [; "a-umlaut:@:a"; "o-umlaut:@:o"; "u-umlaut:@:u"; "A-umlaut:@:A"; "O-umlaut:@:O"; "U-umlaut:@:U"; "sz-ligature:@ss"; ">>-quotes:@@162"; "<<-quotes:@@163"; "e-umlaut:@:e"; "i-umlaut:@:i"; "y-umlaut:@:y"; "E-umlaut:@:E"; "I-umlaut:@:I"; "a-acute:@'a"; "e-acute:@'e"; "i-acute:@'i"; "o-acute:@'o"; "u-acute:@'u"; "y-acute:@'y"; "A-acute:@'A"; "E-acute:@'E"; "I-acute:@'I"; "O-acute:@'O"; "U-acute:@'U"; "Y-acute:@'Y"; "a-grave:@`a"; "e-grave:@`e"; "i-grave:@`i"; "o-grave:@`o"; "u-grave:@`u"; "A-grave:@`A"; "E-grave:@`E"; "I-grave:@`I"; "O-grave:@`O"; "U-grave:@`U"; "a-circumflex:@^a"; "e-circumflex:@^e"; "i-circumflex:@^i"; "o-circumflex:@^o"; "u-circumflex:@^u"; "A-circumflex:@^A"; "E-circumflex:@^E"; "I-circumflex:@^I"; "O-circumflex:@^O"; "U-circumflex:@^U"; "a-ring:@oa"; "A-ring:@oA"; "o-slash:@/o"; "O-slash:@/O"; "a-tilde:@~a"; "n-tilde:@~n"; "o-tilde:@~o"; "A-tilde:@~A"; "N-tilde:@~N"; "O-tilde:@~O"; "ae-ligature:@ae"; "AE-ligature:@AE"; "c-cedilla:@cc"; "C-cedilla:@cC"; "thorn:@th"; "eth:@et"; "Thorn:@Th"; "Eth:@Et"; "pound-symbol:@LL"; "oe-ligature:@oe"; "OE-ligature:@OE"; "inverse-!:@!!"; "inverse-?:@??"; ]; [ TestAccents ix opt; print "This displays all the accented characters (encoding values 155 to 223). You will have to inspect your interpreter's display to make sure they appear correctly.^^"; print "For the record, an umlaut is two dots; an acute accent is the one that slants up to the right; a grave accent is the one that slants down to the right; a circumflex is a pointy hat; a tilde is a squiggly hat; a ring is a ring; a cedilla is the little hook that hangs down below the C. Thorn looks like a capital D whose vertical bar extends both up and down, and Eth looks like a D with a little cross-stroke.^^"; print "NOTE: Inform 6.11 contradicts the Z-Spec 0.99 document, on the subject of the European angle-quotes (the ones that look like '>>' and '<<'). The Z-Spec says that the character '>>' is code 162, and '<<' is 163. However, Inform 6.11 (following the Z-Spec 0.2) compiles '@@64>>' as 163, and '@@64<<' as 162. The concensus is that the Z-Spec 0.2 and Inform 6.11 are wrong, and Z-Spec 0.99 and later are correct.^^"; !ix = '@>>'; !print "(This version of TerpEtude was compiled with Inform "; !inversion; !print ", which compiles '@@64>>' as ", ix, ", which your interpreter ! displays as '", (char)ix, "'. Got it?)^^"; opt = 0; while (1) { if (opt >= 0) { if (opt & 4) font off; if (opt & 2) style underline; if (opt & 1) style bold; for (ix=1: ix <= AccentList-->0: ix++) { print (string) AccentList-->ix; if (ix % 4 == 0) new_line; else print " "; } if (opt & 4) font on; if (opt & 3) style roman; new_line; new_line; } print "Type a digit (0..7) to repeat this list in a different text style, or ~.~ to end this test.^"; print "Options: 0: normal; 1: bold; 2: italic; 3: bold italic; 4: fixed-width; 5: fixed bold; 6: fixed italic; 7: fixed bold italic.^"; print "^Accents> "; @read_char 1 ix; new_line; if (ix == '.') return; new_line; if (ix >= '0' && ix <= '7') opt = ix - '0'; else opt = -1; } ]; frotz-2.44/src/test/etude/undo.inc0000644000175000017500000001014512527042452014050 00000000000000 [ TestUndo claim ix cont result; print "This tests the interpreter's ability to do single and multiple ~undo~ commands.^^"; ! snarf bit 4 of the low (second) word of $10-11. ix = $10->1; if (ix & $10) claim = 1; else claim = 0; if (claim) print "Your interpreter claims (by its header bit) that it DOES support undo.^^"; else print "Your interpreter claims (by its header bit) that it DOES NOT support undo.^^"; cont = 1; result = 0; ! result codes: 0 for total failure of the undo ! facility. -1 if the interpreter claims (via save_undo returning ! -1) that it doesn't support undo. 1 if it supports single undo. 2 ! if it supports multiple undo. -2 if the user aborted before ! testing. print "Simulating first move...^"; @save_undo ix; switch (ix) { -1: print "Save failed -- interpreter claims that it does not support ~undo~.^"; cont = 0; result = -1; 0: print "Save failed.^"; cont = 0; result = 0; 1: print "Save succeeded.^"; 2: print "Undo succeeded (undid first move).^"; cont = 0; result = 2; default: print "Save gave unknown result code ", ix, " -- ERROR.^"; cont = 0; result = 0; } if (cont) { print "^Simulating second move...^"; @save_undo ix; switch (ix) { -1: print "Save failed -- interpreter claims that it does not support ~undo~.^"; cont = 0; result = -1; 0: print "Save failed.^"; cont = 0; result = 0; 1: print "Save succeeded.^"; 2: print "Undo succeeded (undid second move).^"; print "^Hit ~.~ to abort this test, or any other key to try a second ~undo~. (In many interpreters, executing the second ~undo~ will return to exactly the same point as the first one. If this occurs, and you see this message a second time (or more), your interpreter only supports single-move undo.)^"; print "MultipleUndo> "; ix = GetKey(); new_line; if (ix == '.') { cont = 0; result = 1; break; } print "^Second undo...^"; @restore_undo ix; switch (ix) { 0: print "Undo failed.^"; default: print "Undo gave unknown result code ", ix, " -- ERROR.^"; } result = 0; cont = 0; default: print "Save gave unknown result code ", ix, " -- ERROR.^"; cont = 0; result = 0; } } if (cont) { print "^Hit ~.~ to abort this test, or any other key to try ~undo~.^"; print "SingleUndo> "; ix = GetKey(); new_line; if (ix == '.') { cont = 0; result = -2; } else { print "^First undo...^"; @restore_undo ix; switch (ix) { 0: print "Undo failed.^"; cont = 0; result = 0; default: print "Undo gave unknown result code ", ix, " -- ERROR.^"; cont = 0; result = 0; } } } new_line; switch (result) { -2: print "Test cancelled.^"; -1: if (claim) print "Your interpreter claims to support ~undo~, but the @@64save_undo opcode returned -1, indicating that the interpreter does not support ~undo~. ", (string)SectionBad; else print "Your interpreter claims to not support ~undo~, and the @@64save_undo opcode returned -1, corroborating this. ", (string)SectionOk; 0: if (claim) print "Your interpreter claims to support ~undo~, but it didn't work. ", (string)SectionBad; else print "Your interpreter claims to not support ~undo~, and in fact ~undo~ failed. However, it did not return -1 from the @@64save_undo opcode, which it should. ", (string)SectionBad; 1: if (claim) print "Your interpreter claims to support ~undo~, and it does. ", (string)SectionOk; else print "Your interpreter claims to not support ~undo~, but in fact it does anyway. ", (string)SectionBad; 2: if (claim) print "Your interpreter claims to support ~undo~, and it does. In fact, it supports multiple ~undo~. ", (string)SectionOk; else print "Your interpreter claims to not support ~undo~, but in fact it does anyway. In fact, it supports multiple ~undo~. ", (string)SectionBad; } ]; frotz-2.44/src/test/etude/timedch.inc0000644000175000017500000000430612527042452014522 00000000000000 Global timedcharflag; Global timedcharcounter; [ TestTimedChar claim ix; ix = $0->1; if (ix & 128) { claim = 1; print "Your interpreter claims (by its header bit) that it DOES support timed input.^^"; } else { claim = 0; print "Your interpreter claims (by its header bit) that it DOES NOT support timed input.^^"; } print "When you begin this test, asterisks should appear at the rate of one per second. If they appear at the rate of one every ten seconds, your interpreter is using the incorrect timing rate caused by an old bug in ZIP. If an entire line of ten asterisks appears all at once every ten seconds, your interpreter is not properly flushing its buffer.^^"; while (1) { print "Hit ~.~ to return to the main menu, or any other key to begin the test. Hit any key to stop the test while it is running.^^"; print "TimedKey> "; ix = GetKey(); if (ix == '.') return; new_line; timedcharcounter = 0; timedcharflag = -93; @read_char 1 10 TimedCharSplot ix; new_line; if (timedcharflag == -93) { print "The timing interrupt function was not called at all. Either your interpreter does not support timed input, or you terminated the test before one second elapsed. (Or your interpreter has the ~slow~ bug and you terminated the test before ten seconds elapsed.)^^"; if (claim == 0) print "Your interpreter claims to not support timed input. ", (string)SectionOk; else print "Your interpreter claims to support timed input. ", (string)SectionBad; } else { if (timedcharflag == 0) { print "Your interpreter calls the timing interrupt function with no arguments. "; if (claim == 0) print "This is correct, except that your interpreter claims not to support timed input at all. ", (string)SectionBad; else print (string)SectionOk; } else { print "Your interpreter calls the timing interrupt function with an argument. ", (string)SectionBad; } } new_line; } ]; [ TimedCharSplot arg; timedcharflag = arg; print "* "; timedcharcounter++; if (timedcharcounter == 10) { new_line; timedcharcounter = 0; } return 0; ]; frotz-2.44/src/test/etude/Makefile0000644000175000017500000000051312527042452014046 00000000000000 etude.z5: etude.inf header.inc styles.inc color.inc \ accents.inc accentin.inc division.inc timedch.inc \ timedstr.inc givenin.inc undo.inc exittext.inc inform etude.inf etude.z5 gntests.z5: gntests.inf inform gntests.inf gntests.z5 go: etude.z5 xzip etude.z5 fgo: etude.z5 frotz etude.z5 jgo: etude.z5 jzip etude.z5 frotz-2.44/src/test/strictz.inf0000644000175000017500000001404612527042452013506 00000000000000! Strict Z test, written to test frotz_zstrict_patch ! _ ! Torbjorn Andersson, 981127 Property prop1; Property prop2; Attribute attr1; ! 0 Attribute attr2; ! 1 ! Note: obj1's property list will have prop2 before prop1. Object obj1 with prop1 1, prop2 2, has attr1 attr2; ! These will be set later anyway Object -> obj2 with prop1; Object -> obj3; [ _jin o1 o2 expected; @jin o1 o2 ?L1; print "@@64jin ", o1, " ", o2, " => FALSE "; if (expected) "(incorrect)"; "(correct)"; .L1; print "@@64jin ", o1, " ", o2, " => TRUE "; if (expected) "(correct)"; "(incorrect)"; ]; [ _get_child o expected result; @get_child o -> result ?L2; print "@@64get_child ", o, " => ", result; if (expected) " (incorrect)"; " (correct)"; .L2; print "@@64get_child ", o, " => ", result; if (expected) " (correct)"; " (incorrect)"; ]; [ _get_parent o expected result; @get_parent o -> result; print "@@64get_parent ", o, " => ", result; if (result) result = true; if (expected == result) " (correct)"; " (incorrect)"; ]; [ _get_sibling o expected result; @get_sibling o -> result ?L3; print "@@64get_sibling ", o, " => ", result; if (expected) " (incorrect)"; " (correct)"; .L3; print "@@64get_sibling ", o, " => ", result; if (expected) " (correct)"; " (incorrect)"; ]; [ _get_prop_addr o p expected result; @get_prop_addr o p -> result; print "@@64get_prop_addr ", o, " ", p, " => ", result; if (result) result = true; if (result == expected) " (correct)"; " (incorrect)"; ]; [ _get_prop o p expected result; @get_prop o p -> result; print "@@64get_prop ", o, " ", p, " => ", result; if (result) result = true; if (result == expected) " (correct)"; " (incorrect)"; ]; [ _clear_attr o a; @clear_attr o a; print_ret "@@64clear_attr ", o, " ", a; ]; [ _set_attr o a; @set_attr o a; print_ret "@@64set_attr ", o, " ", a; ]; [ _test_attr o a expected; @test_attr o a ?L4; print "@@64test_attr ", o, " ", a, " => FALSE "; if (expected) "(incorrect)"; "(correct)"; .L4; print "@@64test_attr ", o, " ", a, " => TRUE "; if (expected) "(correct)"; if (~~o) "(incorrect; shouldn't set attributes in object 0)"; "(incorrect)"; ]; [ _insert_object o1 o2; @insert_obj o1 o2; print_ret "@@64insert_obj ", o1, " ", o2; ]; [ _remove_obj o; @remove_obj o; print_ret "@@64remove_obj ", o; ]; [ _get_next_prop o p expected result; @get_next_prop o p -> result; print "@@64get_next_prop ", o, " ", p, " => ", result; if (result) result = true; if (result == expected) " (correct)"; " (incorrect)"; ]; Array buffer string 120; Array parse string 64; [ Main x; print "Strict Z Test^"; print "^According to the Z-Machine Standards Document, ~objects are numbered consecucutively from 1 upward, with object number 0 being used to mean ~nothing~ (though there is formally no such object).~ Hence, it seems reasonable that operations on object number 0 should either fail or, if that is not an option, do nothing. These tests are written with that assumption.^"; print "^Please note that whenever a test is flagged as ~correct~, that only means that an instruction returned zero or non-zero (or branched / didn't branch) when it was supposed to. Not that it necessarily returned the correct value. If no result is written the result was not tested, and the test was only included to test the stability of the interpreter.^"; print "^Would you like to make a transcript of the test results? (Y/N) "; for (::) { read buffer parse; if (parse-->1 == 'yes' or 'y//') { print "^"; @output_stream 2; break; } if (parse-->1 == 'no' or 'n//') { print "^"; break; } print "Transcript? (Y/N) "; } print "Testing @@64jin:^^"; _jin (obj1, obj2, false); _jin (obj2, obj1, true); _jin (0, obj1, false); _jin (0, 0, true); print "^Testing @@64get_child:^^"; _get_child (obj1, true); _get_child (obj3, false); _get_child (0, false); print "^Testing @@64get_parent:^^"; _get_parent (obj1, false); _get_parent (obj2, true); _get_parent (0, false); print "^Testing @@64get_sibling:^^"; _get_sibling (obj1, false); _get_sibling (obj2, true); _get_sibling (0, false); print "^Testing @@64get_prop_addr:^^"; _get_prop_addr (obj1, prop1, true); _get_prop_addr (obj3, prop1, false); _get_prop_addr (0, prop1, false); print "^Testing @@64get_prop:^^"; _get_prop (obj1, prop1, true); _get_prop (obj2, prop1, false); _get_prop (0, prop1, false); print "^Testing @@64clear_attr:^^"; _clear_attr (obj1, attr1); _clear_attr (obj1, attr2); _clear_attr (0, attr1); _clear_attr (0, attr2); print "^Testing @@64set_attr:^^"; _set_attr (obj1, attr1); _set_attr (obj1, attr2); _set_attr (0, attr1); print "^(Note: An attempt has been made to set attribute number ", attr1, " in object number 0. If the @@64test_attr test below indicates that this attribute has been set, the interpreter did not ignore the instruction as it should have done.)^"; print "^Testing @@64test_attr:^^"; _test_attr (obj1, attr1, true); _test_attr (obj1, attr2, true); _test_attr (obj2, attr1, false); _test_attr (obj2, attr2, false); _test_attr (0, attr1, false); _test_attr (0, attr2, false); print "^Testing @@64insert_obj:^^"; _insert_object (obj3, obj1); _insert_object (0, obj1); _insert_object (obj1, 0); _insert_object (0, 0); print "^Testing @@remove_obj:^^"; _remove_obj (obj3); _remove_obj (0); print "^Testing @@get_next_prop:^^"; _get_next_prop (obj1, prop2, true); _get_next_prop (obj2, prop1, false); _get_next_prop (0, prop1, false); print "^Test completed!^"; if ((0-->8) & 1) { print "^End of transcript^"; @output_stream -2; } print "^Press any key.^"; @read_char -> x; ]; frotz-2.44/Makefile0000644000175000017500000002071712527045477011214 00000000000000# Define your C compiler. I recommend gcc if you have it. # MacOS users should use "cc" even though it's really "gcc". # CC = gcc #CC = cc # Define your optimization flags. Most compilers understand -O and -O2, # Standard (note: Solaris on UltraSparc using gcc 2.8.x might not like this.) # OPTS = -O2 # Pentium with gcc 2.7.0 or better #OPTS = -O2 -fomit-frame-pointer -malign-functions=2 -malign-loops=2 \ #-malign-jumps=2 # Define where you want Frotz installed. Usually this is /usr/local PREFIX = /usr/local MAN_PREFIX = $(PREFIX) #MAN_PREFIX = /usr/local/share CONFIG_DIR = $(PREFIX)/etc #CONFIG_DIR = /etc # Define where you want Frotz to look for frotz.conf. # CONFIG_DIR = /usr/local/etc #CONFIG_DIR = /etc #CONFIG_DIR = /usr/pkg/etc #CONFIG_DIR = # Uncomment this if you want color support. Most, but not all curses # libraries that work with Frotz will support color. # COLOR_DEFS = -DCOLOR_SUPPORT # Uncomment this if you have an OSS soundcard driver and want classical # Infocom sound support. # #SOUND_DEFS = -DOSS_SOUND # Uncomment this too if you're running BSD of some sort and are using # the OSS sound driver. # #SOUND_LIB = -lossaudio # Define your sound device # This should probably be a command-line/config-file option. # #SOUND_DEV = /dev/dsp #SOUND_DEV = /dev/sound #SOUND_DEV = /dev/audio # If your vendor-supplied curses library won't work, uncomment the # location where ncurses.h is. # #INCL = -I/usr/local/include #INCL = -I/usr/pkg/include #INCL = -I/usr/freeware/include #INCL = -I/5usr/include #INCL = -I/path/to/ncurses.h # If your vendor-supplied curses library won't work, uncomment the # location where the ncurses library is. # #LIB = -L/usr/local/lib #LIB = -L/usr/pkg/lib #LIB = -L/usr/freeware/lib #LIB = -L/5usr/lib #LIB = -L/path/to/libncurses.so # One of these must always be uncommented. If your vendor-supplied # curses library won't work, comment out the first option and uncomment # the second. # CURSES = -lcurses #CURSES = -lncurses # Uncomment this if your need to use ncurses instead of the # vendor-supplied curses library. This just tells the compile process # which header to include, so don't worry if ncurses is all you have # (like on Linux). You'll be fine. # #CURSES_DEF = -DUSE_NCURSES_H # Uncomment this if you're compiling Unix Frotz on a machine that lacks # the memmove(3) system call. If you don't know what this means, leave it # alone. # #MEMMOVE_DEF = -DNO_MEMMOVE # Uncomment this if for some wacky reason you want to compile Unix Frotz # under Cygwin under Windoze. This sort of thing is not reccomended. # #EXTENSION = .exe SDLINC = `sdl-config --cflags` FTCFLAGS = `freetype-config --cflags` FTLIBS = `freetype-config --libs` SDL_DEFS = $(SDLINC) $(FTCFLAGS) $(FTLIBS) SDL_LIBS = -ljpeg -lpng -lz -lSDL -lSDL_mixer ##################################################### # Nothing under this line should need to be changed. ##################################################### SRCDIR = src VERSION = 2.44 NAME = frotz BINNAME = $(NAME) DISTFILES = bugtest DISTNAME = $(BINNAME)-$(VERSION) distdir = $(DISTNAME) COMMON_DIR = $(SRCDIR)/common COMMON_TARGET = $(SRCDIR)/frotz_common.a COMMON_OBJECT = $(COMMON_DIR)/buffer.o \ $(COMMON_DIR)/err.o \ $(COMMON_DIR)/fastmem.o \ $(COMMON_DIR)/files.o \ $(COMMON_DIR)/hotkey.o \ $(COMMON_DIR)/input.o \ $(COMMON_DIR)/main.o \ $(COMMON_DIR)/math.o \ $(COMMON_DIR)/object.o \ $(COMMON_DIR)/process.o \ $(COMMON_DIR)/quetzal.o \ $(COMMON_DIR)/random.o \ $(COMMON_DIR)/redirect.o \ $(COMMON_DIR)/screen.o \ $(COMMON_DIR)/sound.o \ $(COMMON_DIR)/stream.o \ $(COMMON_DIR)/table.o \ $(COMMON_DIR)/text.o \ $(COMMON_DIR)/variable.o CURSES_DIR = $(SRCDIR)/curses CURSES_TARGET = $(SRCDIR)/frotz_curses.a CURSES_OBJECT = $(CURSES_DIR)/ux_init.o \ $(CURSES_DIR)/ux_input.o \ $(CURSES_DIR)/ux_pic.o \ $(CURSES_DIR)/ux_screen.o \ $(CURSES_DIR)/ux_text.o \ $(CURSES_DIR)/ux_blorb.o \ $(CURSES_DIR)/ux_audio_none.o \ $(CURSES_DIR)/ux_audio_oss.o DUMB_DIR = $(SRCDIR)/dumb DUMB_TARGET = $(SRCDIR)/frotz_dumb.a DUMB_OBJECT = $(DUMB_DIR)/dumb_init.o \ $(DUMB_DIR)/dumb_input.o \ $(DUMB_DIR)/dumb_output.o \ $(DUMB_DIR)/dumb_pic.o SDL_DIR = $(SRCDIR)/sdl SDL_TARGET = $(SRCDIR)/frotz_sdl.a SDL_OBJECT = $(SDL_DIR)/sf_aiffwav.o \ $(SDL_DIR)/sf_deffont.o \ $(SDL_DIR)/sf_font3.o \ $(SDL_DIR)/sf_fonts.o \ $(SDL_DIR)/sf_ftype.o \ $(SDL_DIR)/sf_images.o \ $(SDL_DIR)/sf_msg_en.o \ $(SDL_DIR)/sf_osfdlg.o \ $(SDL_DIR)/sf_resample.o \ $(SDL_DIR)/sf_resource.o \ $(SDL_DIR)/sf_sig.o \ $(SDL_DIR)/sf_sound.o \ $(SDL_DIR)/sf_util.o \ $(SDL_DIR)/sf_video.o # Blorb file handling # BLORB_DIR = $(SRCDIR)/blorb BLORB_TARGET = $(SRCDIR)/blorblib.a BLORB_OBJECT = $(BLORB_DIR)/blorblib.o TARGETS = $(COMMON_TARGET) $(CURSES_TARGET) $(BLORB_TARGET) OPT_DEFS = -DCONFIG_DIR="\"$(CONFIG_DIR)\"" $(CURSES_DEF) \ -DVERSION="\"$(VERSION)\"" CURSES_DEFS = $(OPT_DEFS) $(COLOR_DEFS) $(SOUND_DEFS) $(SOUNDCARD) \ $(MEMMOVE_DEF) FLAGS = $(OPTS) $(CURSES_DEFS) $(INCL) $(NAME): $(NAME)-curses curses: $(NAME)-curses $(NAME)-curses: $(COMMON_TARGET) $(CURSES_TARGET) $(BLORB_TARGET) $(CC) -o $(BINNAME)$(EXTENSION) $(TARGETS) $(LIB) $(CURSES) $(SOUND_LIB) dumb: $(NAME)-dumb d$(NAME): $(NAME)-dumb $(NAME)-dumb: $(COMMON_TARGET) $(DUMB_TARGET) $(CC) -o d$(BINNAME)$(EXTENSION) $(COMMON_TARGET) $(DUMB_TARGET) $(LIB) sdl: $(NAME)-sdl s$(NAME): $(NAME)-sdl $(NAME)-sdl: $(COMMON_TARGET) $(SDL_TARGET) $(BLORB_TARGET) $(CC) -o s$(BINNAME) $(COMMON_TARGET) $(SDL_TARGET) $(BLORB_TARGET) $(SDL_LIBS) all: $(NAME) d$(NAME) .SUFFIXES: .SUFFIXES: .c .o .h $(COMMON_OBJECT): %.o: %.c $(CC) $(OPTS) $(COMMON_DEFS) -o $@ -c $< $(BLORB_OBJECT): %.o: %.c $(CC) $(OPTS) -o $@ -c $< $(DUMB_OBJECT): %.o: %.c $(CC) $(OPTS) -o $@ -c $< $(CURSES_OBJECT): %.o: %.c $(CC) $(OPTS) $(CURSES_DEFS) -o $@ -c $< $(SDL_OBJECT): %.o: %.c $(CC) $(OPTS) $(SDL_DEFS) -o $@ -c $< # If you're going to make this target manually, you'd better know which # config target to make first. # common_lib: $(COMMON_TARGET) $(COMMON_TARGET): $(COMMON_OBJECT) @echo @echo "Archiving common code..." ar rc $(COMMON_TARGET) $(COMMON_OBJECT) ranlib $(COMMON_TARGET) @echo curses_lib: config_curses $(CURSES_TARGET) $(CURSES_TARGET): $(CURSES_OBJECT) @echo @echo "Archiving curses interface code..." ar rc $(CURSES_TARGET) $(CURSES_OBJECT) ranlib $(CURSES_TARGET) @echo dumb_lib: $(DUMB_TARGET) $(DUMB_TARGET): $(DUMB_OBJECT) @echo @echo "Archiving dumb interface code..." ar rc $(DUMB_TARGET) $(DUMB_OBJECT) ranlib $(DUMB_TARGET) @echo sdl_lib: $(SDL_TARGET) $(SDL_TARGET): $(SDL_OBJECT) @echo @echo "Archiving SDL interface code..." ar rc $(SDL_TARGET) $(SDL_OBJECT) ranlib $(SDL_TARGET) @echo blorb_lib: $(BLORB_TARGET) $(BLORB_TARGET): $(BLORB_OBJECT) @echo @echo "Archiving Blorb file handling code..." ar rc $(BLORB_TARGET) $(BLORB_OBJECT) ranlib $(BLORB_TARGET) @echo soundcard.h: @if [ ! -f $(SRCDIR)/soundcard.h ] ; then \ sh $(SRCDIR)/misc/findsound.sh $(SRCDIR); \ fi install: $(NAME) strip $(BINNAME)$(EXTENSION) install -d $(PREFIX)/bin install -d $(MAN_PREFIX)/man/man6 install -c -m 755 $(BINNAME)$(EXTENSION) $(PREFIX)/bin install -c -m 644 doc/$(NAME).6 $(MAN_PREFIX)/man/man6 uninstall: rm -f $(PREFIX)/bin/$(NAME) rm -f $(MAN_PREFIX)/man/man6/$(NAME).6 deinstall: uninstall install_dumb: d$(NAME) strip d$(BINNAME)$(EXTENSION) install -d $(PREFIX)/bin install -d $(MAN_PREFIX)/man/man6 install -c -m 755 d$(BINNAME)$(EXTENSION) $(PREFIX)/bin install -c -m 644 doc/d$(NAME).6 $(MAN_PREFIX)/man/man6 uninstall_dumb: rm -f $(PREFIX)/bin/d$(NAME) rm -f $(MAN_PREFIX)/man/man6/d$(NAME).6 deinstall_dumb: uninstall_dumb distro: dist dist: distclean mkdir $(distdir) @for file in `ls`; do \ if test $$file != $(distdir); then \ cp -Rp $$file $(distdir)/$$file; \ fi; \ done find $(distdir) -type l -exec rm -f {} \; tar chof $(distdir).tar $(distdir) gzip -f --best $(distdir).tar rm -rf $(distdir) @echo @echo "$(distdir).tar.gz created" @echo clean: rm -f $(SRCDIR)/*.h $(SRCDIR)/*.a find . -name *.o -exec rm -f {} \; find . -name *.O -exec rm -f {} \; distclean: clean rm -f $(BINNAME)$(EXTENSION) d$(BINNAME)$(EXTENSION) s$(BINNAME) rm -f *.EXE *.BAK *.LIB rm -f *.exe *.bak *.lib rm -f *core $(SRCDIR)/*core -rm -rf $(distdir) -rm -f $(distdir).tar $(distdir).tar.gz realclean: distclean clobber: distclean help: @echo @echo "Targets:" @echo " frotz" @echo " dfrotz" @echo " install" @echo " uninstall" @echo " clean" @echo " distclean" @echo frotz-2.44/HOW_TO_PLAY0000644000175000017500000002021312527042452011340 00000000000000========================================================== ---------------------------------------------------------- || How To Play Interactive Fiction With Frotz || ---------------------------------------------------------- ========================================================== In the late 1970s, a group of students at the Massachusetts Institute of Technology (MIT) formed a company called Infocom to produce a genre of computer games called "Interactive Fiction", that is, a game in which you're "playing" a novel or short story. Infocom wrote their games to run on an imaginary computer of their own design called the "Z-Machine" and so the games are said to be written in "Z-code". From their first game ("Zork I") to their last ("Shogun"), the Z-machine went through six versions. The most common versions were "Standard" (version 3) and "Advanced" (version 5). Infocom's heyday lasted through the late 1970s and all of the 1980s. In the early to mid 1990s the Z-machine was reverse-engineered and a new language called "Inform" was created to allow people to once again write programs for the Z-machine. The Z-code games written nowadays were written using this Inform language. These games are usually in the form of single files. The extensions .z3 and .z5 mean that a file is a "Z-code" game in version 3 or version 5. These files are not compressed, but are ordinary binary files and should be downloaded in 'binary' mode. The canonical repository of freeware games which can be played on Frotz is at the Interactive Fiction Archive at http://www.ifarchive.org and its mirrors. The following is borrowed from the first few pages of the manual to Lost Treasures of Infocom I: ========================================= Communicating with Interactive Fiction || ========================================= With Interactive Fiction, you type your commands in plain English each time you see the prompt which looks like this: > Most of the sentences that the stories understand are imperative sentences. See the examples below. When you have finished typing your input, press the ENTER (or RETURN) key. The story will then respond, telling you whether your request is possible at this point in the story, and what happened as a result. The story recognizes your words by their first six letters, and all subsequent letters are ignored. Therefore, CANDLE, CANDLEs, and CANDLEstick would all be treated as the same word. Most stories don't care about capitalization, so you can just type in all-lowercase if you like. To move around, just type the direction you want to go. Directions can be abbreviated: NORTH to N, SOUTH to S, EAST to E, WEST to W, NORTHEAST to NE, NORTHWEST to NW, SOUTHEAST to SE, SOUTHWEST to SW, UP to U, and DOWN to D. IN and OUT will also work in certain places. There are many differnet kinds of sentences used in Interactive Fiction. Here are some examples: > WALK TO THE NORTH > WEST > NE > DOWN > TAKE THE BIRDCAGE > READ ABOUT DIMWIT FLATHEAD > LOOK UP MEGABOZ IN THE ENCYCLOPEDIA > LIE DOWN IN THE PINK SOFA > EXAMINE THE SHINY COIN > PUT THE RUSTY KEY IN THE CARDBOARD BOX > SHOW MY BOW TIE TO THE BOUNCER > HIT THE CRAWLING CRAB WITH THE GIANT NUTCRACKER > ASK THE COWARDLY KING ABOUT THE CROWN JEWELS You can use multiple objects with certain verbs if you separate them by the word "AND" or by a comma. Here are some examples: > TAKE THE BOOK AND THE FROG > DROP THE JAR OF PEANUT BUTTER, THE SPOON, AND THE LEMMING FOOD > PUT THE EGG AND THE PENCIL IN THE CABINET You can include several inputs on one line if you separate them by the word "THEN" or by a period. Each input will be handled in order, as though you had typed them individually at seperate prompts. For example, you could type all of the following at once, before pressing the ENTER (or RETURN) key: > TURN ON THE LIGHT. TAKE THE BOOK THEN READ ABOUT THE JESTER IN THE BOOK If the story doesn't understand one of the sentences on your input line, or if an unusual event occurs, it will ignore the rest of your input line. The words "IT" and "ALL" can be very useful. For example: > EXAMINE THE APPLE. TAKE IT. EAT IT > CLOSE THE HEAVY METAL DOOR. LOCK IT > PICK UP THE GREEN BOOT. SMELL IT. PUT IT ON. > TAKE ALL > TAKE ALL THE TOOLS > DROP ALL THE TOOLS EXCEPT THE WRENCH AND MINIATURE HAMMER > TAKE ALL FROM THE CARTON > GIVE ALL BUT THE RUBY SLIPPERS TO THE WICKED WITCH The word "ALL" refers to every visible object except those inside something else. If there were an apple on the ground and an orange inside a cabinet, "TAKE ALL" would take the apple but not the orange. There are three kinds of questions you can ask: "WHERE IS (something)", "WHAT IS (something)", and "WHO IS (someone)". For example: > WHO IS LORD DIMWIT? > WHAT IS A GRUE? > WHERE IS EVERYBODY? When you meet intelligent creatures, you can talk to them by typing their name, then a comma, then whatever you want to say to them. Here are some examples: > JESTER, HELLO > GUSTAR WOOMAX, TELL ME ABOUT THE COCONUT > UNCLE OTTO, GIVE ME YOUR WALLET > HORSE, WHERE IS YOUR SADDLE? > BOY, RUN HOME THEN CALL THE POLICE > MIGHTY WIZARD, TAKE THIS POISONED APPLE. EAT IT Notice that in the last two examples, you are giving the characters more than one command on the same input line. Keep in mind, however, that many creatures don't care for idle chatter; your actions will speak louder than your words. ================= Basic Commands || ================= BRIEF - This command causes the game to fully describe a location only the first time you enter it. On subsequent visits, only the name of the location and any objects present will be described. Most adventures will begin in "BRIEF" mode and remain in "BRIEF" mode unless you use the "VERBOSE" or "SUPERBRIEF" commands. DIAGNOSE - This will give you a report of your physical condition. Not all games support this command. INVENTORY - This will give you a list of what you are carrying and wearing. Usually you can abbreviate "INVENTORY" to "I". LOOK - This will give you a full description of your location. You can abbreviate "LOOK" to 'L'. QUIT - This lets you stop Frotz gracefully. If you want to save your position before quitting, you must use the "SAVE" command. RESTORE - This restores a previously saved position. RESTART - This stops the game and restarts it from the beginning. SAVE - This saves a "snapshot" of your current position. You can return to a saved position in the future by using the "RESTORE" command. SCRIPT - This command tells Frotz to make a transcript of the story and save it to a file. Transcripts can be used to aid your memory, prepare maps, prepare walkthroughs, make something to brag about, and so on. SCORE - This command will show your current score and often a ranking based on that score. SUPERBRIEF - This one causes the game to display only the name of a place you enter, even if you've never been there before. In this mode, not even objects present are described. Of course, you can get a full description of your location and the object present by typing "LOOK". In "SUPERBRIEF" mode, the blank line between turns is eliminated. This mode is meant for players who are already familiar with the geography. TIME - This gives your the current time in the story. Some games don't have a concept of time and therefore don't have this command. UNSCRIPT - Stops Frotz from making a transcript. VERBOSE - This causes the game to give a complete description of each location and the objects in it every time you enter a location, even if you've been there before. VERSION - Shows you the release number and serial number of the story file. WAIT - Causes time in the story to pass. Since nothing happens until you type a sentence and press RETURN, you could leave your machine, take a nap, then return to the story to find that nothing has changed. So, to make time pass in the story, you type "WAIT". For example, if you meet a wizard, you might "WAIT" to see if he will say anything. If you're aboard a flying carpet, you might "WAIT" to see where it goes. There are few exceptions, most notable Infocom's "Border Zone", in which the game is played in real-time (take too long deciding what to do and Bad Things happen). This command can often be abbreviated to "Z".