frotz-2.43/AUTHORS 644 1750 24 2070 7556675333 6752 Original 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.43/BUGS 644 1750 24 6740 7556653363 6374 ============= 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.43/COPYING 644 1750 24 43076 7136455260 6755 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave, Cambridge, MA 02139, 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 Library 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 Appendix: 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) 19yy 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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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 Library General Public License instead of this License. frotz-2.43/ChangeLog 644 1750 24 16011 7556701742 7466 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.43/DUMB 644 1750 24 3333 7556701450 6345 The 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.43/HOW_TO_PLAY 644 1750 24 20221 7556675617 7534 ========================================================== ---------------------------------------------------------- || 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". frotz-2.43/INSTALL 644 1750 24 21447 7557110751 6751 =========================================================================== --------------------------------------------------------------------------- | 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. ==================== Precompiled Frotz || ==================== NetBSD: It's in the pkgsrc tree! FreeBSD: It's in the ports tree! MacOS X: It's at http://www.ifarchive.org/indexes/if-archiveXinfocomXinterpretersXfrotz.html Debian: Debian isn't so good at keeping an up-to-date version of Frotz around, so check http://packages.debian.org/testing/games/frotz.html to make sure before you use apt-get or whatever. See http://www.cs.csubak.edu/~dgriffi/proj/frotz/prepackaged.html for up-to-date information on precompiled Frotz packages ======================= 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.43/Makefile 644 1750 24 15505 7557110460 7353 # 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 ##################################################### # Nothing under this line should need to be changed. ##################################################### SRCDIR = src VERSION = 2.43 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_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 TARGETS = $(COMMON_TARGET) $(CURSES_TARGET) OPT_DEFS = -DCONFIG_DIR="\"$(CONFIG_DIR)\"" $(CURSES_DEF) \ -DVERSION="\"$(VERSION)\"" -DSOUND_DEV="\"$(SOUND_DEV)\"" COMP_DEFS = $(OPT_DEFS) $(COLOR_DEFS) $(SOUND_DEFS) $(SOUNDCARD) \ $(MEMMOVE_DEF) FLAGS = $(OPTS) $(COMP_DEFS) $(INCL) $(NAME): $(NAME)-curses $(NAME)-curses: soundcard.h $(COMMON_TARGET) $(CURSES_TARGET) $(CC) -o $(BINNAME)$(EXTENSION) $(TARGETS) $(LIB) $(CURSES) \ $(SOUND_LIB) all: $(NAME) d$(NAME) dumb: $(NAME)-dumb d$(NAME): $(NAME)-dumb $(NAME)-dumb: $(COMMON_TARGET) $(DUMB_TARGET) $(CC) -o d$(BINNAME)$(EXTENSION) $(COMMON_TARGET) \ $(DUMB_TARGET) $(LIB) .SUFFIXES: .SUFFIXES: .c .o .h .c.o: $(CC) $(FLAGS) $(CFLAGS) -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 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 rm -f $(COMMON_DIR)/*.o $(CURSES_DIR)/*.o $(DUMB_DIR)/*.o distclean: clean rm -f $(BINNAME)$(EXTENSION) d$(BINNAME)$(EXTENSION) 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.43/PACKAGING 644 1750 24 1670 7517557642 7115 I 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.43/PORTING 644 1750 24 11433 7556711002 6752 Frotz 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 (if known): ------------------------------------ WinFrotz Frotz for machines running Microsoft Windows. http://www.d.kinder.btinternet.co.uk AmigaFrotz Frotz for the Amiga series of computers. (no webpage) FrotzCE Frotz for PocketPC machines running WindowsCE. http://www.pyram-id.demon.co.uk/FrotzCE.html (Based on 2.32) DOS Good ol' DOS (no webpage) Kwest Frotz ported to KDE. http://users.pandora.be/peter.bienstman/kwest/ EbmFrotz Frotz ported to Franklin's eBookman. http://www.twpo.com.au/cwarrens/ebm Nobody maintains these ports: ----------------------------- Pilot Frotz Frotz for Palm Pilot machines. http://www.geocities.com/SiliconValley/Way/2367/ (Link has been dead for several months) 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. Wireless phones: They might have enough memory to handle Frotz, but I have severe doubts about how usable any sort of Interactive Fiction can be on such a device. Phones were not designed to be used as general-purpose terminals. 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 [mailto:dgriffi@cs.csubak.edu] Sent: Thursday, August 08, 2002 6:43 PM To: prof@ludix.com 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.43/README 644 1750 24 5151 7556677161 6566 FROTZ V2.43 - 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. - Default use of the Quetzal file format. Command line option to use the old format. There are several differences between the old-style save format at Quetzal such that converting from old-style and Quetzal is difficult if not impossible. This also means you can't restore an old-style save and then save your game in Quetzal. - Optional speech synthesis and recognition through FLITE and Sphinx. - 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 information on the speech synthesis and recognition capabilities of Frotz, see the file "SPEECH". 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 dgriffi@cs.csubak.edu. 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 Unix Frotz is available at the Unix Frotz homepage at http://www.cs.csubak.edu/~dgriffi/proj/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. http://www.ifarchive.org/ (USA, Pittsburgh, PA) http://mirror.ifarchive.org/ (USA) ftp://ftp.ifarchive.com/if-archive/ (USA, Los Angeles, CA) ftp://ftp.guetech.org/if-archive/ (USA, Bremerton, WA) ftp://ftp.plover.net/if-archive/ (USA, Chicago, IL) ftp://ftp.funet.fi/pub/misc/if-archive/ (Finland) http://www.planetmirror.com/pub/if-archive/ (Australia) ftp://ftp.planetmirror.com/pub/if-archive/ (Australia) frotz-2.43/README.1st 644 1750 24 1530 7556710231 7253 What 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). COPYING Full text of the GNU General Public License. ChangeLog Summary of changes from one version to another. DUMB Description of the dumb-interface version of Frotz. HOW_TO_PLAY How to play Interactive Fiction. INSTALL How to install Frotz. Makefile The Makefile (the INSTALL file tells you about this). 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.43/SPEECH 644 1750 24 5454 7556674234 6604 =============================================== ----------------------------------------------- | 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.43/TODO 644 1750 24 2357 7557105565 6376 Rework 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. 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). An X11 graphical interface using GTK widgets (thus allowing V6 full support). Cocoa support for OSX. frotz-2.43/doc/frotz.6 644 1750 24 32520 7556711166 7720 .\" -*- nroff -*- .TH FROTZ 6 2.43 .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. .P This port supports old-style sound effects through the OSS sound driver. .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 \-Q No Quetzal. By default, Frotz uses the new Quetzal save format when you save your game. If for some reason you want to save and restore using the old Frotz format, use this flag. .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 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 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 .br syntax. .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 quetzal \ \ on\ |\ off .br Use Quetzal save format. Default is on. If for some reason you want to save or restore using the old Frotz format, set this to "off". .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. .PP Latest information on Unix Frotz is here: .br http://www.cs.csubak.edu/~dgriffi/proj/frotz/ .PP The latest release of Unix Frotz is here: .br ftp://ftp.ifarchive.org/if-archive/infocom/interpreters/frotz/ .PP See this website for a list of mirrors: .br http://www.ifarchive.org .PP See this website for more information on Infocom past, present, and future; and where to get new Z-Machine games and the old ones by Infocom: .br http://www.csd.uwo.ca/Infocom/ .PP Frotz for other platforms: .br http://www.geocities.com/SiliconValley/Heights/3222/frotz.html .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.csd.uwo.ca/Infocom/faq.html .br http://www.cs.csubak.edu/~dgriffi/proj/frotz/HOW_TO_PLAY .br http://www.ifarchive.org .SH BUGS This program has no bugs. no bugs. no bugs. no *WHAP* thank you. .SH AUTHORS .B Frotz was written by Stefan Jokisch in 1995-7. .br The Unix port was done by Galen Hazelwood. .br Currently the Unix port is 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.43/doc/frotz.conf-big 644 1750 24 4471 7176610053 11212 # 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 # Use Quetzal save format (default "yes") quetzal yes # 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.43/doc/frotz.conf-small 644 1750 24 720 7136157614 11537 # 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.43/doc/dfrotz.6 644 1750 24 21443 7556711200 10053 .\" -*- nroff -*- .TH FROTZ 6 2.43 .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. No sound-effect or 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 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 \-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 No Quetzal. By default, Frotz uses the new Quetzal save format when you save your game. If for some reason you want to save and restore using the old Frotz format, use this flag. .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. .PP Latest information on Unix Frotz is here: .br http://www.cs.csubak.edu/~dgriffi/proj/frotz/ .PP The latest release of Unix Frotz is here: .br ftp://ftp.ifarchive.org/if-archive/infocom/interpreters/frotz/ .PP See this website for a list of mirrors: .br http://www.ifarchive.org .PP See this website for more information on Infocom past, present, and future; and where to get new Z-Machine games and the old ones by Infocom: .br http://www.csd.uwo.ca/Infocom/ .PP Frotz for other platforms (very old and out of date): .br http://www.geocities.com/SiliconValley/Heights/3222/frotz.html .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.csd.uwo.ca/Infocom/faq.html .br http://www.cs.csubak.edu/~dgriffi/proj/frotz/HOW_TO_PLAY .br http://www.ifarchive.org .SH BUGS This program has no bugs. no bugs. no bugs. no *WHAP* thank you. .br Well, if you insist, you can look at http://www.cs.csubak.edu/~dgriffi/proj/frotz/BUGS or see the BUGS file in the Unix Frotz tarball .SH AUTHORS .B Frotz was written by Stefan Jokisch in 1995-7. .br The Unix port was done by Galen Hazelwood. .br Currently the Unix port is 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.43/frotz.lsm 644 1750 24 1246 7556650654 7566 Begin4 Title: Unix Frotz Version: 2.43 Entered-date: 2002-10-27 Description: An interpreter for all Infocom games and other Z-machine games. Compiles with standard 1.0 of Graham Nelson's specification. Keywords: infocom, frotz, text-adventure, zork Author: dgriffi@cs.csubak.edu (David Griffith) Maintained-by: dgriffi@cs.csubak.edu (David Griffith) Primary-site: ftp://ftp.ifarchive.org/if-archive/infocom/interpreters/frotz/frotz-2.43.tar.gz Alternate-site: http://www.cs.csubak.edu/~dgriffi/proj/frotz/ Original-site: Platforms: All reasonably POSIX-compliant flavors of Unix. Use ncurses if vendor-supplied curses won't work. Copying-policy: GPL End frotz-2.43/src/dumb/dumb_pic.c 644 1750 24 10116 7556642367 11367 /* 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.43/src/dumb/dumb_init.c 644 1750 24 15751 7556660457 11571 /* 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 -Q use old-style save format\n\ -A watch attribute testing \t -R xxx do runtime setting \\xxx\n\ -h # screen height \t before starting (can be used repeatedly)\n\ -i ignore fatal errors \t -s # random number seed value\n\ -I # interpreter number \t -S # transscript width\n\ -o watch object movement \t -t set Tandy bit\n\ -O watch object locating \t -u # slots for multiple undo\n\ -p plain ASCII output only \t -w # screen width\n\ -P alter piracy opcode \t -x expand abbreviations g/x/z" /* static char usage[] = "\ \n\ FROTZ V2.32 - interpreter for all Infocom games. Complies with standard\n\ 1.0 of Graham Nelson's specification. Written by Stefan Jokisch in 1995-7.\n\ \n\ DUMB-FROTZ V2.32R1 - port for all platforms. Somewhat complies with standard\n\ 9899 of ISO's specification. Written by Alembic Petrofsky in 1997-8.\n\ \n\ Syntax: frotz [options] story-file [graphics-file]\n\ \n\ -a watch attribute setting\n\ -A watch attribute testing\n\ -h # screen height\n\ -i ignore runtime errors\n\ -I # interpreter number to report to game\n\ -o watch object movement\n\ -O watch object locating\n\ -p alter piracy opcode\n\ -P transliterate latin1 to plain ASCII\n\ -R xxx do runtime setting \\xxx before starting\n\ (this option can be used multiple times)\n\ -s # random number seed value\n\ -S # transscript width\n\ -t set Tandy bit\n\ -u # slots for multiple undo\n\ -w # screen width\n\ -x expand abbreviations g/x/z\n\ \n\ While running, enter \"\\help\" to list the runtime escape sequences.\n\ "; */ /* 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 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; /* Parse the options */ do { c = zgetopt(argc, argv, "aAh:iI:oOpPQs: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 '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 'Q': f_setup.save_quetzal = 0; 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); exit(1); } /* if (((argc - zoptind) != 1) && ((argc - zoptind) != 2)) { puts(usage); exit(1); } */ story_name = argv[zoptind++]; if (zoptind < argc) graphics_filename = argv[zoptind++]; } 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_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; } /* Sorry, but dumb frotz is too dumb to care about searching paths. */ return NULL; } 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.save_quetzal = 1; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; } frotz-2.43/src/dumb/dumb_frotz.h 644 1750 24 2314 7556640120 11727 /* 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; /* 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.43/src/dumb/dumb_output.c 644 1750 24 34777 7556646134 12173 /* 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); } 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 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); } 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); } } 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); memset(screen_changes, 0, screen_cells); } frotz-2.43/src/dumb/dumb_input.c 644 1750 24 31546 7556642352 11757 /* 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; static bool do_more_prompts = TRUE; 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 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]); 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; 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: "); 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 */ } frotz-2.43/src/curses/getopt.h 644 1750 24 10611 7137102744 11452 /* 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.43/src/curses/ux_audio_none.c 644 1750 24 4541 7520105537 12763 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/curses/ux_audio_oss.c 644 1750 24 21460 7556702315 12655 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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(story_name) + 10); if (! filename) return; basename = strrchr(story_name, '/'); if (basename) basename++; else basename = story_name; dotpos = strrchr(basename, '.'); namelen = (dotpos ? dotpos - basename : strlen(basename)); if (namelen > 6) namelen = 6; sprintf(filename, "%.*ssound/%.*s%02d.snd", basename - story_name, 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.43/src/curses/ux_file.c 644 1750 24 5506 7206574121 11564 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 (option_zcode_path != NULL) { if ((fp = pathopen(name, option_zcode_path, mode, buf)) != NULL) { strncpy(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(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.43/src/curses/ux_frotz.h 644 1750 24 4264 7520366250 12017 /* * ux_frotz.h * * Unix interface, declarations, definitions, and defaults * */ #include "../common/frotz.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 BLUE_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; /*** Functions specific to the Unix port of Frotz ***/ bool unix_init_pictures(void); /* ux_pic */ int getconfig(char *); int geterrmode(char *); int getcolor(char *); int getbool(char *); FILE *pathopen(const char *, const char *, const char *, char *); void sig_winch_handler(int); void redraw(void); #ifdef NO_MEMMOVE void *memmove(void *, void *); #endif frotz-2.43/src/curses/ux_init.c 644 1750 24 57313 7557105435 11642 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #define __UNIX_PORT_FILE #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 #include #include "ux_frotz.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 -r # right margin\n\ -d disable color \t -q quiet (disable sound effects)\n\ -e enable sound \t -Q use old-style save format\n\ -f # foreground color \t -s # random number seed value\n\ -F Force color mode \t -S # transscript width\n\ -h # screen height \t -t set Tandy bit\n\ -i ignore fatal errors \t -u # slots for multiple undo\n\ -l # left margin \t -w # screen width\n\ -o watch object movement \t -x expand abbreviations g/x/z" 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 *home; char configfile[FILENAME_MAX + 1]; if ((getuid() == 0) || (geteuid() == 0)) { printf("I won't run as root!\n"); exit(1); } if ((home = getenv("HOME")) == 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, sig_winch_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:oOpPQqr: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 = atoi(optarg); 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 = atoi(optarg); 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 'Q': f_setup.save_quetzal = 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 */ story_name = malloc(FILENAME_MAX + 1); strcpy(story_name, argv[optind]); /* Strip path off the story file name */ p = (char *)story_name; for (i = 0; story_name[i] != 0; i++) if (story_name[i] == '/') p = (char *)story_name + i + 1; for (i = 0; p[i] != '\0'; i++) semi_stripped_story_name[i] = p[i]; semi_stripped_story_name[i] = '\0'; 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); /* Don't forget the extensions */ strcat (script_name, ".scr"); strcat (command_name, ".rec"); strcat (save_name, ".sav"); strcat (auxilary_name, ".aux"); }/* 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);*/ u_setup.curses_active = 1; /* Let os_fatal know curses is running */ initscr(); /* Set up curses */ 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 { /* 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); }/* 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); os_display_string((zchar *) "[Hit any key to exit.]"); 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 (option_zcode_path != NULL) { if ((fp = pathopen(name, option_zcode_path, mode, buf)) != NULL) { strncpy(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(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() */ /* * 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, "quetzal") == 0) { f_setup.save_quetzal = 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) { option_zcode_path = malloc(strlen(value) * sizeof(char) + 1); strncpy(option_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() */ /* * sig_winch_handler * * Called whenever Frotz recieves a SIGWINCH signal to make curses * cleanly resize the window. * */ void sig_winch_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. */ /* signal(sig, SIG_DFL); signal(sig, SIG_IGN); signal(SIGWINCH, sig_winch_handler); */ } 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.save_quetzal = 1; f_setup.sound = 1; f_setup.err_report_mode = ERR_DEFAULT_REPORT_MODE; 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; } frotz-2.43/src/curses/ux_input.c 644 1750 24 47323 7517535621 12036 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 - 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) { 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.43/src/curses/ux_pic.c 644 1750 24 22721 7520366673 11450 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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(story_name) + 10)) == NULL) return FALSE; basename = strrchr(story_name, '/'); if (basename) basename++; else basename = story_name; dotpos = strrchr(basename, '.'); namelen = (dotpos ? dotpos - basename : strlen(basename)); sprintf(filename, "%.*sgraphics/%.*s.mg1", basename - story_name, 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.43/src/curses/ux_screen.c 644 1750 24 6742 7520366137 12134 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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. * */ void os_erase_area (int top, int left, int bottom, int right) { 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); else if (units < 0) os_erase_area(top + 1, left + 1, top - units, right + 1); }/* os_scroll_area */ frotz-2.43/src/curses/ux_setup.h 644 1750 24 757 7520366614 12002 typedef 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 */ int interpreter; /* see frotz.h */ } u_setup_t; extern u_setup_t u_setup; frotz-2.43/src/curses/ux_text.c 644 1750 24 20240 7520366522 11644 /* * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/misc/findsound.sh 755 1750 24 4017 7556704141 11745 #!/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.43/src/misc/config.sh 755 1750 24 13560 7556637467 11264 #!/bin/sh TARGET=$1 OUTPUTDIR=$2 CONFIG_H="$OUTPUTDIR/config.h" LIBFILE="$OUTPUTDIR/FROTZ_LIBS" echo "Checking..." touch $CONFIG_H exit 0 cat << EOF > $CONFIG_H /* * This file is automatically generated by config.sh, which is run from * the Makefile found in the Frotz source distribution. * */ EOF OSNAME=`uname` #echo "#define __${OSNAME}__" >> $CONFIG_H if [ $TARGET = "dumb" ] ; then echo "Ah, so you're building for the dumb interface..." echo "That greatly simplifies things." echo echo "/* Nothing more is needed to build for a dumb interface. */" >> $CONFIG_H exit 0 fi echo "/* Make sure this is uncommented if you want to disable audio support. */" >> $CONFIG_H echo "/* #define NO_SOUND */" >> $CONFIG_H echo >> $CONFIG_H if [ $OSNAME = "Linux" ] ; then echo "#define USE_NCURSES" >> $CONFIG_H echo >> $CONFIG_H echo "/* Comment this out to disable color support. */" >> $CONFIG_H echo "#define COLOR_SUPPORT" >> $CONFIG_H echo >> $CONFIG_H echo "/* Comment this out to disable OSS audio support. */" >> $CONFIG_H echo "#define OSS_SOUND" >> $CONFIG_H echo "-lncurses" > $LIBFILE exit 0 fi if [ $OSNAME = "NetBSD" ] ; then RELEASE=`uname -r` VER="$OSNAME-$RELEASE" echo $VER echo "/* Uncomment this if you want to use ncurses instead of regular curses." >> $CONFIG_H case "$RELEASE" in 1.6*|1.7*) echo "/* #define USE_NCURSES */" >> $CONFIG_H ; echo "Great! You can use NetBSD's curses library." ;; *) echo "#define USE_NCURSES" >> $CONFIG_H ; echo "Use of ncurses required" ; echo "Let's see where ncurses.h is hiding..." ; echo " /usr/pkg/include/ncurses.h" echo " /usr/local/include/ncurses.h" echo " /usr/local/pkg/include/ncurses.h" if [ -r /usr/pkg/include/ncurses.h ] ; then NCURSES_HEADER=/usr/pkg/include/ncurses.h elif [ -r /usr/local/include/ncurses.h ] ; then NCURSES_HEADER=/usr/local/include/ncurses.h elif [ -r /usr/local/pkg/include/ncurses.h ] ; then NCURSES_HEADER=/usr/local/pkg/include/ncurses.h fi if [ -z $NCURSES_HEADER ] ; then echo echo "Sorry, couldn't find ncurses.h anywhere." echo "Frotz requires ncurses or curses from NetBSD 1.6 or greater." echo "Install ncurses and try again." echo else echo "/* Found $NCURSES_HEADER */" >> $CONFIG_H echo " --Found $NCURSES_HEADER" fi ;; esac echo >> $CONFIG_H echo "/* Comment this out to disable color support. */" >> $CONFIG_H echo "#define COLOR_SUPPORT" >> $CONFIG_H echo >> $CONFIG_H echo "/* Sound device file */" >> $CONFIG_H echo "#ifndef SOUND_DEV" >> $CONFIG_H echo "#define SOUND_DEV \"/dev/sound\"" >> $CONFIG_H echo "#endif" >> $CONFIG_H echo >> $CONFIG_H echo "/* Comment this out to disable OSS audio support. */" >> $CONFIG_H echo "#define OSS_SOUND" >> $CONFIG_H echo >> $CONFIG_H echo "#include " >> $CONFIG_H if [ $NCURSES_HEADER ] ; then echo "-lncurses -lossaudio" > $LIBFILE else echo "-lcurses -lossaudio" > $LIBFILE fi exit 0 fi # Untested if [ $OSNAME = "FreeBSD" ] ; then RELEASE=`uname -r` VER="$OSNAME-$RELEASE" echo $VER echo "/* Uncomment this if you want to use ncurses instead of regular curses." >> $CONFIG_H case "$RELEASE" in *) echo "#define USE_NCURSES" >> $CONFIG_H ; echo "Use of ncurses required" ; echo "Let's see where ncurses.h is hiding..." ; echo " /usr/include/ncurses.h" echo " /usr/local/include/ncurses.h" if [ -r /usr/include/ncurses.h ] ; then NCURSES_HEADER=/usr/include/ncurses.h elif [ -r /usr/local/include/ncurses.h ] ; then NCURSES_HEADER=/usr/local/include/ncurses.h fi if [ -z $NCURSES_HEADER ] ; then echo echo "Sorry, couldn't find ncurses.h anywhere." echo "Frotz requires ncurses on this OS." echo "Install ncurses and try again." echo exit 1 else echo "/* Found $NCURSES_HEADER */" >> $CONFIG_H echo " --Found $NCURSES_HEADER" fi ;; esac echo >> $CONFIG_H echo "/* Comment this out to disable color support. */" >> $CONFIG_H echo "#define COLOR_SUPPORT" >> $CONFIG_H echo >> $CONFIG_H echo "/* Sound device file */" >> $CONFIG_H echo "#ifndef SOUND_DEV" >> $CONFIG_H echo "#define SOUND_DEV /dev/dsp" >> $CONFIG_H echo "#endif" >> $CONFIG_H echo >> $CONFIG_H echo "/* Comment this out to disable OSS audio support. */" >> $CONFIG_H echo "#define OSS_SOUND" >> $CONFIG_H if [ $NCURSES_HEADER ] ; then echo "-lncurses -lossaudio" > $LIBFILE else echo "-lcurses -lossaudio" > $LIBFILE fi exit 0 fi # Untested if [ $OSNAME = "OpenBSD" ] ; then RELEASE=`uname -r` VER="$OSNAME-$RELEASE" echo $VER echo "/* Uncomment this if you want to use ncurses instead of regular curses." >> $CONFIG_H case "$RELEASE" in *) echo "#define USE_NCURSES" >> $CONFIG_H ; echo "Use of ncurses required" ; echo "Let's see where ncurses.h is hiding..." ; echo " /usr/pkg/include/ncurses.h" echo " /usr/local/include/ncurses.h" if [ -r /usr/include/ncurses.h ] ; then NCURSES_HEADER=/usr/pkg/include/ncurses.h elif [ -r /usr/local/include/ncurses.h ] ; then NCURSES_HEADER=/usr/local/include/ncurses.h fi if [ -z $NCURSES_HEADER ] ; then echo echo "Sorry, couldn't find ncurses.h anywhere." echo "Frotz requires ncurses on this OS." echo "Install ncurses and try again." echo exit 1 else echo "/* Found $NCURSES_HEADER */" >> $CONFIG_H echo " --Found $NCURSES_HEADER" fi ;; esac echo >> $CONFIG_H echo "/* Comment this out to disable color support. */" >> $CONFIG_H echo "#define COLOR_SUPPORT" >> $CONFIG_H echo >> $CONFIG_H echo "/* Sound device file */" >> $CONFIG_H echo "#ifndef SOUND_DEV" >> $CONFIG_H echo "#define SOUND_DEV /dev/dsp" >> $CONFIG_H echo "#endif" >> $CONFIG_H echo >> $CONFIG_H echo "/* Comment this out to disable OSS audio support. */" >> $CONFIG_H echo "#define OSS_SOUND" >> $CONFIG_H if [ $NCURSES_HEADER ] ; then echo "-lncurses -lossaudio" > $LIBFILE else echo "-lcurses -lossaudio" > $LIBFILE fi exit 0 fi frotz-2.43/src/common/buffer.c 644 1750 24 5761 7556411016 11372 /* buffer.c - Text buffering and word wrapping * 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" 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.43/src/common/err.c 644 1750 24 7773 7517407034 10720 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #include "frotz.h" /* 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.43/src/common/fastmem.c 644 1750 24 54436 7517405154 11603 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 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_path_open(story_name, "rb")) == 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 */ fseek (story_fp, 0, SEEK_END); story_size = ftell (story_fp); fseek (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) { fseek (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, auxilary_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 (auxilary_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, save_name, FILE_RESTORE) == 0) goto finished; strcpy (save_name, new_name); /* Open game file */ if ((gfp = fopen (new_name, "rb")) == NULL) goto finished; if (f_setup.save_quetzal) { success = restore_quetzal (gfp, story_fp); } else { /* Load game file */ release = (unsigned) fgetc (gfp) << 8; release |= fgetc (gfp); (void) fgetc (gfp); (void) fgetc (gfp); /* Check the release number */ if (release == h_release) { pc = (long) fgetc (gfp) << 16; pc |= (unsigned) fgetc (gfp) << 8; pc |= fgetc (gfp); SET_PC (pc) sp = stack + (fgetc (gfp) << 8); sp += fgetc (gfp); fp = stack + (fgetc (gfp) << 8); fp += fgetc (gfp); for (i = (int) (sp - stack); i < STACK_SIZE; i++) { stack[i] = (unsigned) fgetc (gfp) << 8; stack[i] |= fgetc (gfp); } fseek (story_fp, 0, SEEK_SET); for (addr = 0; addr < h_dynamic_size; addr++) { int skip = fgetc (gfp); for (i = 0; i < skip; i++) zmp[addr++] = fgetc (story_fp); zmp[addr] = fgetc (gfp); (void) fgetc (story_fp); } /* Check for errors */ if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size) success = -1; else /* Success */ success = 2; } else print_string ("Invalid save file\n"); } 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) { 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 (curr_undo->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 (auxilary_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, save_name, FILE_SAVE) == 0) goto finished; strcpy (save_name, new_name); /* Open game file */ if ((gfp = fopen (new_name, "wb")) == NULL) goto finished; if (f_setup.save_quetzal) { success = save_quetzal (gfp, story_fp); } else { /* Write game file */ fputc ((int) hi (h_release), gfp); fputc ((int) lo (h_release), gfp); fputc ((int) hi (h_checksum), gfp); fputc ((int) lo (h_checksum), gfp); GET_PC (pc) fputc ((int) (pc >> 16) & 0xff, gfp); fputc ((int) (pc >> 8) & 0xff, gfp); fputc ((int) (pc) & 0xff, gfp); nsp = (int) (sp - stack); nfp = (int) (fp - stack); fputc ((int) hi (nsp), gfp); fputc ((int) lo (nsp), gfp); fputc ((int) hi (nfp), gfp); fputc ((int) lo (nfp), gfp); for (i = nsp; i < STACK_SIZE; i++) { fputc ((int) hi (stack[i]), gfp); fputc ((int) lo (stack[i]), gfp); } fseek (story_fp, 0, SEEK_SET); for (addr = 0, skip = 0; addr < h_dynamic_size; addr++) if (zmp[addr] != fgetc (story_fp) || skip == 255 || addr + 1 == h_dynamic_size) { fputc (skip, gfp); fputc (zmp[addr], gfp); skip = 0; } else skip++; } /* 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; 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; GET_PC (p->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 */ fseek (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.43/src/common/files.c 644 1750 24 22300 7523741545 11236 /* files.c - Transscription, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 transscript file. 'AMFV' makes this more complicated as it * turns transscription on/off several times to exclude some text from * the transscription file. This wasn't a problem for the original V4 * interpreters which always sent transscription to the printer, but it * means a problem to modern interpreters that offer to open a new file * every time transscription is turned on. Our solution is to append to * the old transscription 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, script_name, FILE_SCRIPT)) goto done; strcpy (script_name, new_name); } /* Opening in "at" mode doesn't work for script_erase_input... */ if ((sfp = fopen (script_name, "r+t")) != NULL || (sfp = fopen (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 transscription. * */ 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 transscript 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 transscript 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 transscript 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 transscript 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 transscript 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 transscript 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, command_name, FILE_RECORD)) { strcpy (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, command_name, FILE_PLAYBACK)) { strcpy (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.43/src/common/frotz.h 644 1750 24 41047 7556674314 11323 /* * 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. */ /* #include "../config.h" */ #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 char zbyte; typedef unsigned short zword; enum story { BEYOND_ZORK, SHERLOCK, ZORK_ZERO, SHOGUN, ARTHUR, JOURNEY, LURKING_HORROR, UNKNOWN }; typedef unsigned char zchar; /*** 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 #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 transscription 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 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 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 /* A bunch of x86 assembly code previously appeared here. */ #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; /*** Various data ***/ extern char *story_name; 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 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; /*** Blorb stuff ***/ /* bb_err_t blorb_err; bb_map_t *blorb_map; */ /*** 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 /*** 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); /*** 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); void os_fatal (const char *); void os_finish_with_sample (int); 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); int os_string_width (const zchar *); void os_init_setup (void); int os_speech_output(const zchar *); #include "setup.h" frotz-2.43/src/common/getopt.c 644 1750 24 2123 7174321112 11401 /* * 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.43/src/common/hotkey.c 644 1750 24 11621 7517402376 11442 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/common/input.c 644 1750 24 14560 7516643514 11303 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 */ 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; /* 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.43/src/common/main.c 644 1750 24 7601 7517405060 11037 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ /* * This is an interpreter for Infocom V1 to V6 games. It also supports * the recently defined V7 and V8 games. Please report bugs to * * s.jokisch@avu.de * */ #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; /* User options */ /* int option_attribute_assignment = 0; int option_attribute_testing = 0; int option_context_lines = 0; int option_object_locating = 0; int option_object_movement = 0; int option_left_margin = 0; int option_right_margin = 0; int option_ignore_errors = 0; int option_piracy = 0; int option_undo_slots = MAX_UNDO_SLOTS; int option_expand_abbreviations = 0; int option_script_cols = 80; int option_save_quetzal = 1; */ 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.43/src/common/math.c 644 1750 24 10242 7133627400 11055 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/common/object.c 644 1750 24 43663 7521776014 11416 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/common/process.c 644 1750 24 32606 7517405350 11616 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 << (f_setup.save_quetzal ? 12 : 8))); 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); if (f_setup.save_quetzal) 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++ >> (f_setup.save_quetzal ? 12 : 8); 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 (f_setup.save_quetzal ? frame_count : (zword) (fp - stack)); }/* 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 (f_setup.save_quetzal) { 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]; } else { if (zargs[1] > STACK_SIZE) runtime_error (ERR_BAD_FRAME); fp = stack + zargs[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.43/src/common/quetzal.c 644 1750 24 37501 7136454174 11631 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 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) (((a)<<24) | ((b)<<16) | ((c)<<8) | (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> 16) & 0x7fff; } store ((zword) (result % zargs[0] + 1)); } }/* z_random */ frotz-2.43/src/common/redirect.c 644 1750 24 6352 7174321357 11723 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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; if (buffering && (short) xsize <= 0) xsize = get_max_width ((zword) (- (short) 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.43/src/common/screen.c 644 1750 24 102433 7517403230 11426 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 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; } wp[8], *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); 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); 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); 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); }/* 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); }/* 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 */ frotz-2.43/src/common/setup.h 644 1750 24 3032 7556626715 11271 /* * 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 save_quetzal; /* done */ int sound; /* done */ int err_report_mode; /* done */ } f_setup_t; extern f_setup_t f_setup; typedef struct zcode_header_struct { zbyte h_version; zbyte h_config; zword h_release; zword h_resident_size; zword h_start_pc; zword h_dictionary; zword h_objects; zword h_globals; zword h_dynamic_size; zword h_flags; zbyte h_serial[6]; zword h_abbreviations; zword h_file_size; zword h_checksum; zbyte h_interpreter_number; zbyte h_interpreter_version; zbyte h_screen_rows; zbyte h_screen_cols; zword h_screen_width; zword h_screen_height; zbyte h_font_height; zbyte h_font_width; zword h_functions_offset; zword h_strings_offset; zbyte h_default_background; zbyte h_default_foreground; zword h_terminating_keys; zword h_line_width; zbyte h_standard_high; zbyte h_standard_low; zword h_alphabet; zword h_extension_table; zbyte h_user_name[8]; zword hx_table_size; zword hx_mouse_x; zword hx_mouse_y; zword hx_unicode_table; } z_header_t; frotz-2.43/src/common/sound.c 644 1750 24 10063 7516651007 11262 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 (number); break; case EFFECT_FINISH_WITH: os_finish_with_sample (number); break; } locked = FALSE; } else os_beep (number); }/* z_sound_effect */ frotz-2.43/src/common/stream.c 644 1750 24 16273 7133627420 11433 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 transscript 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 transscript 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.43/src/common/table.c 644 1750 24 7545 7133627420 11211 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/common/text.c 644 1750 24 52224 7541260622 11120 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/common/variable.c 644 1750 24 12320 7133627422 11714 /* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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.43/src/test/etude/accentin.inc 644 1750 24 6100 6311276324 13014 ! 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.43/src/test/etude/accents.inc 644 1750 24 7004 6315507440 12654 ! 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.43/src/test/etude/color.inc 644 1750 24 1610 6311034337 12343 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.43/src/test/etude/division.inc 644 1750 24 3342 6311026546 13060 [ 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.43/src/test/etude/etude.inf 644 1750 24 10112 6315742263 12363 Switches 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.43/src/test/etude/etude.z5 644 1750 24 41000 6315742272 12145 P970325B6.11 B=HBk.EB]ӰBH!&+05:?DINSX]bglqv{ E"%)-0369<?BEHKNQTWZ]`cfilorux{~  IS    e  .," ? H22FBj:y*]Y*&`S "&:@f.Fx9Dj_R`Q`eb 圥;j:y*]Y*"&:X4tF4p+naE-(|)HI,eWVeW`4F) e]d8(-eWVeW`4F) e]d8dlDEEȧI4eWVeW`4RUa) R)e]d8(5eWVeW`4RUa) R)e]d8dlDEEȧI8eWVeW`4. zS%WE)%|+f:&*E9eWVeW`4. zS%WE)%|+dP(I0eWVeW`4nuI994+f:&*E1eWVeW`4nuI994+dP(I .eWVeW`4jiK-Hg\(/eWVeW`4jiK-Hg\dlDEEȧI,eWVeW`4.IIWYf:&*E-eWVeW`4.IIWYdP(I*eWVeW`4jiPf:&*E+eWVeW`4jiPdP(I/eWVeW`4F) e]d8( 0eWVeW`4F) e]d8dlDEEȧ  :`eh(*4ph6E J%9`; p`:`eh(*4phSQ$Ej_. ]+Q`ASS$R4\zFe :l-(R:J`5F%W`$ R~dsQ`.)SS$R4\{- ;LF HC:`SeI@"RH([$2DI B , B O{//[I4eWVeW`4RUa) R)e]d8(5eWVeW`4RUa) R)e]d8dlDEEȧI8eWVeW`4. zS%WE)%|+f:&*E9eWVeW`4. zS%WE)%|+dP(I0eWVeW`4nuI994+f:&*E1eWVeW`4nuI994+dP(*c Q`*U4;$xE%|+EHSR .dEȧ*c Q`;&ERji*:j$*w%ȥHSR .dEȧ*c Q`][*(m*e]HSR .dEȧ([*c Q`-)%s'-*w a0etO Qk%L54+85F%WdE/[HSR .dEȧ*c Q`-)%s'-*w a0d+%[*w%[z*%NF fHM0g+Heb QXtd*:eWVeW`P ;DHMIz*`-(:j`*4pNm@1-(g+exex P圥*c Q`$. +E*c Q`$mWa@e]*c Q`;&E`][*(+E*c Q`$. mWa@e]etF4q0]@1:y) q4etO Qk 4`e*\; ]R`+%L mS-SL4gz*H c&eR*y`!\圥([*c Q`-)%LR)*w%ȥ*c Q`-)%Ld9e]*c Q`-)%L+j_ *w%ȥ*c Q`-)%LR)`;&E*w%ȥ*c Q`-)%LR)`][*(+E*c Q`-)%Ld9L+j_ *w%ȥ*c Q`-)%LR)`;&E`][*(+E/[*iz*`+EaLR D\\ SQ$ -;eXgaMIZG.V. :`%;ReLM Jj4*eOH1-+ *eOVaI42j$-(|)HW^*b>{ex]@jx9)%;-;;"]HeIEDE|@jNMeOjO.R`ed ;naLG{^M84p'}WPztdS]8j0;j-;>LM %<`5@`(9@-+ OjO.Rx *@f@"JRl];:lD e+JEW`5@.R@:y*]Y*b.F-+ OjO.Rx@Y-+td hy)I-( M +,d$'Hea@Rqx*:- |D+%|=tt\F iDR*E|   v  A  Ov O v OOv  A  w    Ow O wOOw   x   Ox O x OOx K Ό ڰ-;%V&{1-(! O*$4**hQ.MliX4P(e|S@qD j4b# z\O*^+*\` ;D4FA@cW(5^UF\R)GHt\5@]HR``jQY

ip`@-(|)``4-(4*>x%@%8`ip``$eH6+j\Mt^@H R1SMeb  E|RU:*``>x,$`<p(5@"!ScX- eb  @iMt^@H Rl`ib  %DM D*^#%H ^BtI([II c/XFI/[I>U@ 9dxEHf]U(-;EdL.-j]Sd+z*`R%H4S$5*c%ȧeOt t^FD`%tR)l(. `etR). `t ;$qe;-) $`t ;$d9;-) $d9ȧ*y`A.CB0C7U0O A/"fD4*f4|H5(a*EY( A /"fD4*fH|H5(bjr.ME A1"fD4*f[ :j+ B '"fD4*fT@OA_"fD4* **eEOnAYji)nMI #*ܥOSB\`84*5B -AL#WbUAN#Wb4e|AN#Wb*%lAN#Wb\W/S#.R`A^U+ARA^T UBUUT/xBYji)nMI #*ܥO]BD -ANIShE+ATJa@&*EAQJa@a2*EOji)nMI #*ܥAF-Vaj)d!ܿ&A!aj)d!\L j1:jWY A\-Saj)d!ܿ-;eXg:uk Q`*y) #:1xD #*_H*y*:j*w%;F @AS4reLM zF aJEd,4 #*_z\O*^+*\61hz$*JH\4-(O*^+*\`jS $P):!@+j_50PS\ (圥d*y*;8*+4gWLP5@HL*zEj*y)$:uk%>PO C b%4/"('ڌ-;eXg:uk Q`*y) #:1xD #*_Hg(4*lS@qD)@qd4*j:y*]Y*eidS@g)%H5EdP+:^`feF:`ISh圥D!SeIWY A.T+nM5IEQ*'IE j:y*]Y*"&:X;8*\;%|4(V_ e) :uk%HF j:y*]Y*"&:X;8*\;%|4(djR.IIWYE*`z1-;eXdeW;`6E (-(*(*aHRi@-+(-(*( mWx*`aHRi`j:y*]Y*;kMe"]Hd:NM\(X) `R)0L|H9``*y:.M@Q`eSeW;`VDdM [**L)M8`z\O*^+*\`S VUWG.:aM;8-j\圥Xdd f]Yj4-(JOEL\OS-*A^41-(+@dOA^4Re*c qE@; ;_SM0圥.II x A.C OS -(:NM:y*jzM:`S EID;-*z\O*^+*\ QXtdjR.IIWY`Rz*^NL) e*c KR()M *&W $j:y*]Y*4-(gSdiih*:feI-(+.(*`aHRi` DaIE|-j:y*]Y*"&:X4tdjR.IIWY@ Ό(j:y*]Y*"&:X4V_ e) :uk% ڌj:y*]Y* Ge.I0O*^W /S#.R`q4P]ISgG-;;"]Hd]!Ud4j:y*]Y*"&:XtdPjR.IIWYD ڌ Ό8j:y*]Y* Ge.I0O*^W /S#.R`q4L]ISd ڻ-A F IE j:y*]Y*"&:X;8*\;%|4(V_ e) :uk%HF j:y*]Y*"&:X;8*\;%|4(djR.IIWYE*`z1-;eXdh6E @(P O*\.M@Q`:uk :`eXhfaReH+j_e)@aHRi`E(,+F (@9`e.MXUF\d5@\(,M@+j_e_> "'LSWeWVeWX:l-(M^# e:le@ aI$i:`8E|eW_UghG#W:l!@UW "$`+j_e] :y*j:y`.M@Q`e]d$5@S-*gm@Nm8( -j#%Hy*(4:j`z\Nd6E @]I\LP4h`"ei@g:l$ %:lEdd f]Yj4-(JOEL\OS-*A^41-(+@>U@%HE(xg E`fc4T5@eXd圥.II]0 A.C1M0+EH圥O .II]0PO  W6hk>UI&NE(>U@%H4Re`+E|5hk>UI bT4/%HAOA.EC*c eWI*$-(:NM:y*jzM:`S EID;-*z\O*^+*\ QXtdjR.IIWY`Rz*^NL) e*c KR()M *&W $j:y*]Y*4-(gSdiih*:feI-(+.(*`aHRi` DaIE|-j:y*]Y*"&:X4tdjR.IIWY@ Ό(j:y*]Y*"&:X4V_ e) :uk% ڌj:y*]Y* Ge.I0O*^W /S#.R`q4P]ISgG-;;"]Hd]!Ud4j:y*]Y*"&:XtdPjR.IIWYD ڌ Ό8j:y*]Y* Ge.I0O*^W /S#.R`q4L]ISd ڰ-A. [[*-]J "'HEH :J$fM>-;eXgqe\SWeWVeWV_8:lM*$Ndx5@0( :*@.]UEEL/*\zM: x` ; :`z$~@-(RRiRUdVee]dF> qe[*e]dS@4>UI.(S@:y*j) ; q45@/S#.R`A^E|-(RRiRUd*4p6E (ee] $1*edF>naE@y*;%Hz SQ$(*4>U@J(4*`R2@M ).d5@1*`!\eWh  g) eHSWaQ,圥h)@e] $1*edq(j:y*]Y*;VO.MemS #*_L54d6E3ed`5@]XVa:.gQ`eI@-(圧*4%>;j̥PGivenO \hk>UI&NE(3hk>UI bT4/%ȧ-;eXgeeWVeW:.gf&a2*$j9:(gS&dRRi`圥I  >j:y*]Y*"&:X;8*\;%|4(V_ jiP圥?j:y*]Y*"&:X;8*\;%|4(djRS&H  KQ.M-c J(Eȧ -@m@,EI:y*]Y*"&:X- ; &`S cUVdgS&d OAXm@,EIE  rATm@cH!J%IE\A(S&cH!J%Iji9 -c J(E  1m@0(NScQdQ*D\\  :aj&e0)M J(Eȧ -@m@,EI:y*]Y*"&:X- ; &`S cUVdgS&d OAXm@,EIE  ATm@cH!J%IEAsS&cH!J%Iji9 aHRiTmE|5EdPd5*c%L\OS-*A^47x "$gS&dFO:y*]Y*`+#Y:l-()M :M4 qD+:^`f+#1x5@`(Qd`5@-c Rj@-;Qj`ih)@e`+aHRi.I@\RLSWeWVeWGcUVga2*SjS&HZG.V*S&> A.K  aHRiS&HE -ARS&,EIE+S&0(NScQdQ*D\\  1m@0(NScQdQ*D\\  5EdPd5*c%L\OS-*A^47xgS&dM(M4 A.M Oa-c jiPEȧ -AXS&,EIE  1S&0(NScQdQ*D\\  -T*c !QEIEʠoj:y*]Y*"&:X4V_ :M4%Lk ejM4"(+:^j$p`:i9e04-(O*^+*\ QXtdjRjiP@ ڌVj:y*]Y*"&:X4tdjRjiP`i-(@`(jiPU%@]Yj) $^.Me` ΌA7j:y*]Y*"&:X4V_ :M4%Lk ; %LdR@ ڌvj:y*]Y*"&:X4tdjRjiP`if# :M4 ,EI@q[*Ld 9 NgWLpwR@ejM4"(9  SQ$ ڌ%At3j:y*]Y*"&:X4V_ :M4%LM ; &` Ό<j:y*]Y*"&:X4tdjRjiP`Yf# ; &`O ڌAOj:y*]Y*"&:X4V_ :M4%LM ; &`L `; cUVgKQeE@:M4% ΌXj:y*]Y*"&:X4tdjRjiP`Yf# ; &`OH:`,dV_8ZG.V*jiP@ ڰ-;eXg9`zL(*w q!;%V&yIII8*>.(5@V2Hi`tdEF\5Y5W-;;"*$x5@pUH@q[*LRJIX:hGI:lI@R1 .R@0+p ;DVddF:l*w i-*`:R).*G[Nd圧dd f]Yj4-(JOEL\OS-*A^4:yE(,+$5S;%H9`e.M@Q`e]d QXtdVL\V$na`R[N"xP(`z\O*^+*\:1m@fh(;--S IXESMe]d A.ReXdeIE5- E(,+@Q'yEȧ Ż MQ'yEȧ`W ĠveOtyd*y*;8*+4.be`*z圥Hz]$]; *^+:%E b'TVT/` >MO  bU4/A GbS4/A.Ea4/B04/C9H  b:4/B0+4/C94/U0V tBcV(( O*\fGJwR@ `R%H4];%L\ ; SeWaQ,P ;D-(*zeOȧmV/` - R:td+ :UER*y)%ȧA<*^+:%Et|H5(:y*]Y*]*; ܧM7+S0:`^\4+(REIHEF@ɏ:9e.<=>?*c8J8!@q4|F!M@i`&jJO H%ȧdPEte`^\4+N*c +j_50L5@pUH@cM1-(+`L5Q@&`L 3F\eJ- z\O*^+*\`|)qJ8d圥t\4Fg*\-;V2H4(; `Ld4Ld5@d QX`Yh6E NcR(d`5@tjyF p :jMX`,S@-$RJeMc7l( )-(|)L5H@ ue0O*^+*_L5H@:kQH`O*^+*_LM eL*iJF: 9`z(eDRkk $-()NnMLeWTgI(rg*L4`** %pEEFa@Et!SeIk eXd%*$;>4.b!\eW`LR)`ji*:j`inuI994g+ȧ!SeIk eXdS*$4>$<p]ELL `eXeI^#1xmS+*c%t;%ceI@-(`PfVO emS*w KRF8(@nuI*c f&e`.IIfMeXd41I*c f&4em8($gnaEEdO*^W8RqxM@:y*j-]JgFF>:y`+EjRNM*c j:]X.c Q`eWI.M!\eW`tO8c7(jc.M"%`.f*L-W^)$\EL``$`i-(+-(^-;)j:y*]Y*(`P)m@R:l4)ȧ-;)j:y*]Y*(`P)m@\d0唥ꤥSF4EF1Se*%,*%,*$@$@%,h%,h$$%,%%,%$$jQYPjQYhjQYsRDdsRDdEsRDdŀcr.0jxrS*`ňprS*`Ō(jQY8jQYxjQYEsRDdŜsRDdŠeEt%(eEtE8eEtePeEtheEtxeEtpk*Epk*pk*pk*Epk*pk*2mEt(2mEt82mEtP2mEth2mEt%qjEqjqjqjEqj!#R.*t(!#R.*tƀ8!#R.*tƄP!#R.*tƈh!#R.*tƌq]Iq+tEq]Iq+tq]Iq+tq]Iq+tEq]Iq+t]0ƤrMtEPb&ates e%EtLe%EtPe%Ets.E*es.E*s.E*Er.0j(E:]Et !I:1ԥq %Dte^et+--RY4VM%sHDQEr.0j(E:]Et:{*(t:{*(tj_e!Sd41X4*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,NdjS& E:y:l.(ieӰ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]FE%Xf]RӺl" ĥV%VO%[4^frotz-2.43/src/test/etude/exittext.inc 644 1750 24 1351 6311026572 13107 [ 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.43/src/test/etude/givenin.inc 644 1750 24 2241 6315510364 12670 [ 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.43/src/test/etude/header.inc 644 1750 24 3132 6311030355 12452 [ 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.43/src/test/etude/Makefile 644 1750 24 513 6313533741 12160 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.43/src/test/etude/README 644 1750 24 1276 6311300226 11413 This 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.43/src/test/etude/styles.inc 644 1750 24 5326 6311274635 12567 [ 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.43/src/test/etude/timedch.inc 644 1750 24 4313 6310026174 12644 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.43/src/test/etude/timedstr.inc 644 1750 24 5525 6315512243 13072 [ 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.43/src/test/etude/undo.inc 644 1750 24 10203 6311276147 12217 [ 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.43/src/test/crashme.z5 644 1750 24 111000 7302056723 11365  010521B$P6.21 B=HBk.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.," ?! #" 65/W/ȍ##+X4REQqCA bW?U*#+ B [!+#*#+ bR+#t #" R+#t  ]/ /C *5 B_!B+)B_!'T c C "BB _! '&(c&_.MHE[(@QR:lW^BEAN"&#黰A O#/#O#A![f) fVO 4ח$A" $(%7+c7:lE*A#T$c7:lE*A$Z$!P)E*B f) BN]F$ ^ re@E>-Bϲ>UBѲpU-H AO]Ӱ AJ&* \ހ//#I%L54 *y]U4E*BVCQCʲ]FrEg%@IRRX:l-Q>E*Qp(E]BJeXB CL-$@ BGk AU%9*^(E]%-AP:ed\ftӌAR4 R-䥌AJTתysAJ*)gAJ!%[ALzMܥMALaEӰ?A L!E7e1A LzM%#A J*)A NP)FԥA ZtS$,f=Hf4ҥA#1(fY(/#OE*AY]RSj /#OE*lJ(/#O4/#OAF`q!rE H(4R/#O-Ӏ/#Oa?Ӏ/#OE*E*AU$)/#OE*AJE&AJEܥ AEE `/#OE*B+c FG"&Ǫ N5Ӱ# ORWB[;N5/#O<`P^*/#fOOBbٲit\ ~5W=H俲4/#IE*BcA###Bě##!~BěaCBGBðBBEɏɏcCt##B!p6t##B!ot##B##BH E##B##BH '##B##BH  AE I!/#A!#/#ALN5ӰDAG;AL"&-AVSY:jـAQfM ɏgI//#OɏQɏ?׏;ɏ׏Q-OCSPtTOɏOOCMbIoVrzH o/#I/#ADADAMFCðh]@_SM0\6Jfd5F ]U(-(+L54 *j\) e`k5k%ȧ-;V2H *j\+\&Q*9 LP+-(Pc3+jrF!M@:y*]Y*HJd: Ge`:1-;*4RH&NL,S@ F z\O*^+*\5S.1GFU0qDg*J4;*HDH@q!aj)l9 rF!M@0(54Ry`5@`(Q*- qD(jeȧ]FeE``4( P,IY*G(E]P)FT^*`HX(5@P)`Sj$5(5@FT) eSL4d*(`q!;Nl9 `84*"( RSYWYE*f) fVO %7+R`b+-:ltd@f) fVO fM|LRJeMNf) fVO #%|LRJeMNӀ1(*c -d\eby q4tL9]k*R`e=HsSBtr`9]k*LҨ"*]H]FE%Xf]RӺl" ĥV%VO%[4^\'k!" -*fEfrotz-2.43/src/test/gntests.inf 644 1750 24 24217 6311027244 11640 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.43/src/test/gntests.z5 644 1750 24 16000 6311275307 11416 @970311B6.11 B=HBk.EB]ӰBH $).38=BGLQV[`ejoty~.," ?5e``F*#.R`Q`et+QX9 I@9 ) feb  %D :*b e]E|N!M` j*`H(]!Ud Re:JO@ceRiI4-( ^d%t.g;!Sg;WY%X`t"SW`*\.IIWY`t+-A1H9A2H/A3H%A4HA5HRA6HYA0Cc? 646"4t KSf:&*  B76W Tt6"X TtHEȾUt Sf:&*E?!SeI #*_eXdL Ry圥*! "(4*fI@ *;fESd%4J&k ʔ%4N&k %4J&k J%4jQYE%4PjQYQE%8hjQYiE%8|E:]@%8j.R`H@xR%%8j.R`H@pR%%8 J&k E%8J&k Ŝ%8J&k Ŝ%8(jQY%88jQY%8#Y(%< #Y(%<#Y(%<#Y(Ч%<#Y(觲%<#Y(%<e@%<(e@%<8e@%<Pe@%<he@%@xe@%@\(%@ \(%@\(%@\(Ч%@\(觲%@2m@%@(2m@%@82m@%@P2m@%Dh2m@%D:jKE]%D :jKE] %D:jKE]%D:jKE]%D:jKE]%D!#R.*tŜ%D(!#R.*tE%D8!#R.*tŜ%DP!#R.*tE h!#R.*tEE :lE ]0ŜE D4ЧE Pb&aE :)(E :)(̧E :)(ЧE e%@E Le%@E$Pe%@E$(E:]@ʔE$9gW(ĨE$).F&E$ !I:1E$9 D%-RE$9 D%Y4eE$9 D%6L-E$9 D%eE$SS$zGR E((E:]@QEE(P9gW(PE(W%Eq4r`E(W%Eq4r`V* +Eȧ xWY%@eXeӰVcA^`P)@6-+]XV$$]X`` 4nM4圧-A CAJ%Q*/A J]Y#AJ+B C~Բ!\eWAL#WbUAN#Wb4eAN#Wb*%AN#WbBCײ/S#.R`A^eUBCӲA^TɀUBCڲ*y) !\eWiANIShEYATJa@&*ECA\Ja@".":lE@R&*%*RtQ* SQ$S 4()SgWIF\ ;D*c.MI'nMEt5@:y*]Y*``R4j(L񨲲-(O*^+*\F_(EY (EH B . B !a{/9/9ޕѻ B ] B M{x)fj9F_H]X`` 4(E-AJ&SAHIIAJ2e=AJyQƜ1AJ:%ALH*yAJ#̥ A GqETeWVeW*"&\:`圥:`qEH圥F_f:&*I/<E+ I/<. lDEEԥI/<nuI;(4 RyI/<.II xWYI/<xL.&3EH-( R1SM.\iXeO4((:l/Qc_Rqx,;8`L4M Q`q]@aYl!EtP P5`9*eI@-(x+9:le@%8P$i-*`_SE#:]XI/<DLf:&*I/<Tk I /<jiK-Hgf:&*I/<:y*]Y* :jORWj_`a)S9d AN:k:neE .MX]JL994!nuI;(4 Ry #*_]JL )7 :`jng$]JL994LM`"tO 5N1S;8&tO qe,`S;8'圥IF*,G ASS$R4j,/@*,G .)SS$R4j-/@圥 B't /<2c&M&] b!9eLDII-(O*^+*\׏E؏+@f"*HGХ-AJ&SAHIIAJ2e=AJyQƜ1AJ:%ALH*yAJ#̥ A GqE[*c.Me) :uk%h]X`P+`-(+`6E (`Rj "$Td9`z4+A^`e*c aj)nM4dM E S eXdRUEY(圥tp5@`(+t\$%#-aHRieiP>tdF :y*]Y*F @,d N1fH(5@:y* [Ne@ed],E SeXdRUEY(圥V* +Eȧ+zHBsSBtr`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]FE%Xf]RӺl" ĥV%VO%[4^frotz-2.43/src/test/random.inf 644 1750 24 12715 7556672353 11454 ! 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.43/src/test/random.z5 644 1750 24 13000 7556672357 11226  @021026Beh6.21 B=HBk.EB]ӰBHUVWY[]^_`PPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPPc.," ?- (CbA("_D\\1jF Qk*/ aɨA)AP"_D\\1jF Q`]7 aɨ CP_Vl_&W_2OQg_+T(fTaEȥ_+X4;%ȥBBQ_P/-IwBNUbñ c+uA(J|J=oԕ?[΍܍B1C9U0 ]X`OA^41@ BP"BBQ_P'$ c(BBQ_P/TBBQ_PB+9 BP"BBQ_P'- SA Y_k OAqAQG?; N B2MEBBF_E/TBBF_EB+T2$USA IAqAQG?x  BFDBBF_E/c]BBF_E/_*e BFeUBBF_E/v-IwX4{ cu#_ BF"BBF_E'$Ώ1Um%|QE_jEE|b(EXiEV*  EHd*c%-A1HA2H AqC[(@QR:lW^BEAN"&A O /A![f) fVO 4חA" *(%7+c7:lE*A#T2c7:lE*A$Z:P)E*B f) BN]F$ ^ re@E>-Bϲ>UBѲpU-H AO]Ӱ AJ&* \ހ// %L54 *y]U4E*BVCQCʲ]FrEg%@IRRX:l-Q>E*Qp(E]BJeXB CL-$@ BGk AU%9*^(E]%-AP:ed\ftӌAR4 R-䥌AJTתysAJ*)gAJ!%[ALzMܥMALaEӰ?A L!E7e1A LzM%#A J*)A NP)FԥA ZtS$,f=Hf4ҥA#1(fY(/E*AY]RSj /E*lJ(/4/AF`q!rE H(4R/-Ӏ/a?Ӏ/E*E*AUB/E*AJE&AJEܥ AEE `/E*B+c FG"&Ǫ N5Ӱ# ORWB[;N5/<`P^*/&OOBbٲit\ ~5W=H俲4/ E*BcA#[Bě#BěaCBGBðBBEɏɏcC6t#Bo A E B C~ BC J!/A#/PALN5ӰDAG;AL"&-AVSY:jـAQfM ɏgI//ɏQɏ?׏;ɏ׏Q-OCSPtTOɏOOCMbIoVrzH o/ /ADADAMFCð5Q@&W'1`wR@ej_f9)E@Q`*9Drq.ISaLn*)nF*$;-3H-(1`SS!@Qk-()Ne\)y1Se-+&TO4(,+j\S8-(S9R@q]@exd@Te]@(+-`4,`~*mQ`,)-*(F`e`(`4DG{M `)y`; qD 1-()yJdF@: q(DG{MM]7 qD 1fe1JdF@"%0P5@D`,^Ex-(SS!X(S&F>M4HbTS-F(j(6E (*`eF8(SSeI$k :yP\ \4e`^\.b!2Uȧ19  g1+:`e O*\,5@-E ;]U]X*y) |ȧD54+9EX4-()y1-(*y*;]U]X*y) ȧ6FO&W$6iUWF &re:kRiRN|:+j_R`%H`&m]-4Lk 圥]FeE``4( P,IY*G(E]P)FT^*`HX(5@P)`Sj$5(5@FT) eSL4d*(`q!;Nl9 `84*"( RSYWYE*f) fVO %7+R`b+-:ltd@f) fVO fM|LRJeMNf) fVO #%|LRJeMNӀ1(*c -d\eby q4tL9]k*R`e=HsSBtr`9]k*LҨ"*]H]FE%Xf]RӺl" ĥV%VO%[4^"%frotz-2.43/src/test/README 644 1750 24 2565 7525112474 10325 These 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.43/src/test/strictz.inf 644 1750 24 14133 6627504247 11664 ! 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.43/src/test/strictz.z5 644 1750 24 10000 6627504255 11432  K981127B6.15  0>IB=HBk.EB]ӰB夿ED娿D嬿Hx@.," LNxyX?Zf8>D@ͳM^#%R)>h˳R)M^#%3d! >ͳ:hR)"]H俲d! >˳"]H俳:hR)dT*y> aK"]H俳:hR)5daEӰ>ͳ:hR)"]H俲daEӰ>˳"]H俳:hR)rdVT)> aK"]H俳:hR)qdV> aK"]H俳:hR)l(g7k d9j<*c%X>D@ͳM^#%R)*c%X>h˳R)G M^#%naWdPJ(PsdM]dV> aK"]H俳:hR)]d|eX䧍XO+ K,H7x"W%TxDL*c.ML圥eXe0@1Y5$圥eXe0@1YO%teXe0@1Y8:leXe0@1Y^%7eXe0@1Y^eXe0@"*Xft    eXe0@aYg7eXe0@eXd9\圥eXe0@:x*t;;;;eXe0]RSjtBBeXe01Y+^HHHeXdRUEY)%ЧIײ*i7x"W%?Vc~ xM^#%; SQ&ec aYfY+:`P)R:l4-(|F!M@i`&jJO%Lf=HgzH]IO #Hk.mQx ^Up$eP)zH\ )0aI4J`3S-:l 61e]@;.HGNcH4#%| M `; aJK]Fb(4*eOR`P)zH\ 6E )5Wf: RL,4tdLW.ReL PS-:l@-+ *c8(]eSeedcJ:EV* te@ed5S+j\*c ;.&1$`e^#%d- Rqx(`4c7i:gWMI^RN*RM ) @%Ld\!|5S`jS $PS edd) c]x+:^j$5@"]Hd:(,P+G ;rg*L5@]Xj9`S eXeI`i-(+`N>":%I4*c eEx,5@:y*]Y*ȧrE z.A@fH(7x"W Q`e*c ]Xj9`hxS*eRW 4*`H(P+ 9]k*zH=HdjG*H9`e+g7*c QS:i9eX- e`g78e@4*`aY`eeWVeW.$S 9R-(O_HeL`d6E 4( RjEsSBtr`9]k*LҨ"*]H]FE%Xf]RӺl" ĥV%VO%[4^VԩVԪ9ܩ9ܪfrotz-2.43/src/test/unicode.inf 644 1750 24 4417 7477700674 11602 ! 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.43/src/test/crashme.inf 644 1750 24 4567 7205637153 11572 ! 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.43/src/test/unicode.z5 644 1750 24 11000 7477700705 11361 |}tr020606B[ q6.21  HSR!" QasB=HBk.EB]ӰBHMNOQSUVWXHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH.," ?atp*c.MVO%[S9%ExEHÏnˍܐ7AC([`8$//[-AJ%Q*A J]YAL#WbUAN#Wb4eAN#Wb*%AN#WbW/S#.R`A^UoAUA^T UCANIShE3ATJa@&*EATJa@a2*E/Hɰ `([/U bTV t//[/ B Pt/ Iɾ H ɏ׏BFTIW//W//X-BC ;A H1A H'A HA HAH AE[(@QR:lW^BEAN"&A O/[ A![f) fVO 4חA" "(%7+c7:lE*A#T*c7:lE*A$Z2P)E*B f) BN]F$ ^ re@E>-Bϲ>UBѲpU-H AO]Ӱ AJ&* \ހ//U%L54 *y]U4E*BVCQCʲ]FrEg%@IRRX:l-Q>E*Qp(E]BJeXB CL-$@ BGk AU%9*^(E]%-AP:ed\ftӌAR4 R-䥌AJTתysAJ*)gAJ!%[ALzMܥMALaEӰ?A L!E7e1A LzM%#A J*)A NP)FԥA ZtS$,f=Hf4ҥA#1(fY(/[E*AY]RSj /[E*lJ(/[4/[AF`q!rE H(4R/[-Ӏ/[a?Ӏ/[E*E*AU:/[E*AJE&AJEܥ AEE `/[E*B+c FG"&Ǫ N5Ӱ# ORWB[;N5/[<`P^*/rOOBbٲit\ ~5W=H俲4/UE*BcA#<Bě#<_BěaCBGBðBBEɏɏcC A E B C~ BC J!/0A#/ALN5ӰDAG;AL"&-AVSY:jـAQfM ɏgI//[ɏQɏ?׏;ɏ׏Q-OCSPtTOɏOOCMbIoVrzH o/U/0ADADAMFCðjn"(eXd%H`&m:i*圥*c.MeMQ*&*@-;aSeS!@aj)S$;-j`"z1$\*F^c`DŽtp*c.M:uk (f[NdEH7xNe0 #*\:h(5@Z^c*"&]I-;-(`} #*\(; WY[*`eitdPD:epEa.̥&e$cUV*IS&ew*M*$)P$"]]FeE``4( P,IY*G(E]P)FT^*`HX(5@P)`Sj$5(5@FT) eSL4d*(`q!;Nl9 `84*"( RSYWYE*f) fVO %7+R`b+-:ltd@f) fVO fM|LRJeMNf) fVO #%|LRJeMNӀ1(*c -d\eby q4tL9]k*R`e=HsSBtr`9]k*LҨ"*]H]FE%Xf]RӺl" ĥV%VO%[4^