From cf94b9e473718dc4981a1a9789a37945cc14d362 Mon Sep 17 00:00:00 2001 From: David Griffith Date: Sat, 11 Nov 2000 04:46:09 -0800 Subject: [PATCH] Released frotz-2.40.tar.gz --- AUTHORS | 31 ++ BUGS | 70 ++++ COPYING | 339 +++++++++++++++++ ChangeLog | 75 ++++ HOW_TO_PLAY | 191 ++++++++++ INSTALL | 161 ++++++++ Makefile | 169 ++++++--- README | 72 ++++ Readme.unix | 318 ---------------- TODO | 54 +++ Todo | 12 - buffer.c | 27 +- err.c | 153 ++++++++ fastmem.c | 492 ++++++++++++++++-------- files.c | 28 +- findsound.sh | 59 +++ frotz.6 | 377 ++++++++++++++++-- frotz.conf-big | 97 +++++ frotz.conf-small | 16 + frotz.h | 193 +++++----- getopt.c | 19 +- getopt.h | 131 +++++++ hotkey.c | 21 +- input.c | 19 +- main.c | 51 +-- math.c | 23 +- object.c | 115 +++++- process.c | 80 ++-- quetzal.c | 562 +++++++++++++++++++++++++++ random.c | 23 +- readme.txt | 103 ----- redirect.c | 23 +- screen.c | 49 ++- sound.c | 33 +- stream.c | 19 +- table.c | 19 +- text.c | 32 +- ux_sample.c => ux_audio_none.c | 21 +- ux_audio_oss.c | 363 ++++++++++++++++++ ux_frotz.h | 90 ++++- ux_init.c | 627 ++++++++++++++++++++++++++---- ux_input.c | 672 ++++++++++++++++++++------------- ux_pic.c | 337 +++++++++++++++-- ux_screen.c | 98 +++-- ux_text.c | 73 ++-- variable.c | 19 +- 46 files changed, 5212 insertions(+), 1344 deletions(-) create mode 100644 AUTHORS create mode 100644 BUGS create mode 100644 COPYING create mode 100644 ChangeLog create mode 100644 HOW_TO_PLAY create mode 100644 INSTALL create mode 100644 README delete mode 100644 Readme.unix create mode 100644 TODO delete mode 100644 Todo create mode 100644 err.c create mode 100755 findsound.sh create mode 100644 frotz.conf-big create mode 100644 frotz.conf-small create mode 100644 getopt.h create mode 100644 quetzal.c delete mode 100644 readme.txt rename ux_sample.c => ux_audio_none.c (63%) create mode 100644 ux_audio_oss.c diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..72df555 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,31 @@ +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, +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. diff --git a/BUGS b/BUGS new file mode 100644 index 0000000..a212344 --- /dev/null +++ b/BUGS @@ -0,0 +1,70 @@ +===================== +Arrow Key Handling || +===================== + +Arrow key handling for V6 is a bit broken. In "Journey", there are five +columns along the bottom of the screen for choosing commands. The first +column (starting from the left) lists party commands. The second lists +party members with an arrow pointing to the right. The three remaining +columns list available commands which can be given to the characters. If +a command appears to the right of a character's name, you can key over to +that command and the character will do that activity. For example, "Cast" +appears to the right of Praxix's name. Key over to "Cast" and hit enter. +A list of spells to cast will appear. The problem is that you cannot key +over to a column on the right unless a valid command exists to the right +of the option currently in focus. + +To see this bug in action, start the game and enter the tavern. Notice +how you then have only "Exit" and "Game" as the available party commands. +Bergon has no available commands. Commands are available to Praxix, +Esher, and Tag; but you can't move the selection (cursor, I guess) +directly over to the right and Frotz beeps at you. Frotz should move pick +the next option above or below and to the right of the current option in +cases like this. + + +================== +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 +2.40. 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. + + +============================ +OSS sound on NetBSD/sparc || +============================ + +The only machines using the OSS drivers I can test sound on are Linux/x86 +machines and Sparcs running NetBSD. The sound from the NetBSD/sparc +machines sounds horrrible (twice as slow, full of static). I'm sure +there's an endian problem somewhere, but I know very little about sound +programming. So far, I haven't heard of anyone trying sound on machines +other than Linux/x86. + + +=================== +Other Known Bugs || +=================== + +Sound flag is not being set properly. + + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a43ea21 --- /dev/null +++ b/COPYING @@ -0,0 +1,339 @@ + 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. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..3a4761c --- /dev/null +++ b/ChangeLog @@ -0,0 +1,75 @@ +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. + + diff --git a/HOW_TO_PLAY b/HOW_TO_PLAY new file mode 100644 index 0000000..c363ae5 --- /dev/null +++ b/HOW_TO_PLAY @@ -0,0 +1,191 @@ +The adventure games known as "Infocom text adventures", "Infocom games", +or "Z-code games" are in the "Z-code" format (a machine-independent +abstract machine code), which was designed by Infocom and used for all +their adventure games from "Zork I" to "Shogun" (the "Z" in "Z-code" +stands for "Zork). Infocom created six versions of this virtual machine. +Their most popular versions were "Standard" (version 3) and "Advanced" +(version 5). + +Some time after Infocom's heyday of the 1980s ended, 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 ftp://ftp.gmd.de/if-archive/games/zcode. +They can also be found at assorted mirror sites, such as +http://www.ifarchive.org. + + +(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 KIND 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 BOOX 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 Frotz 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. +Most, but 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 store 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 Frotz 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". diff --git a/INSTALL b/INSTALL new file mode 100644 index 0000000..343b97f --- /dev/null +++ b/INSTALL @@ -0,0 +1,161 @@ +--------------------------------------------------------------------------- + READ THIS FILE ALL THE WAY THROUGH BEFORE ATTEMPTING TO INSTALL FROTZ +--------------------------------------------------------------------------- + +The Unix port of Frotz was written mainly with Linux in mind. Fortunately, +the source is rather generic C code and runs well on pretty much all +current Unices. These are the only two things needed to compile and run +Frotz: + + * Some variant of Unix with an ANSI C compiler (gcc works fine) + * A reasonably good SYSV derived curses library (ncurses is best) + +Linux uses ncurses, so you're safe there. The old standard *BSD curses +lack certain features, so you'll need to install ncurses. 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. + +If you want sound support, you'll need the OSS drivers (found on Linux +machines and some *BSD machines). Support for other drivers is in the +works. + + +======================= +Editing the Makefile || +======================= + +You should take a look at the Makefile. 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 || +================================= + +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. + +Under Linux, the header file "soundcard.h" is usually found in +. *BSD has this file in . The Makefile and +supporting scripts will check either of these places for "soundcard.h" and +create a local "soundcard.h" file which includes the correct filepath. I +haven't had any success getting sound to work on non-Linux machines. Your +milage may vary. + +If you get any compile errors, check the Makefile. Make sure you're +specifying the correct curses library and sound driver (only one right +now). Make sure the include and library paths are set correctly. + +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". + +======================================== +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 +continute. + +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 at ftp://ftp.gmd.de/if-archive/games/zcode and +several mirrors of this site. A good mirror site is at +http://www.ifarchive.org which also points to similar mirrors worldwide. + +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 + +[Net|Open|Free]BSD: Install ncurses. The standard BSD curses library +won't work. + +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. + +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). Since 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! diff --git a/Makefile b/Makefile index 21d055f..75749f7 100644 --- a/Makefile +++ b/Makefile @@ -1,73 +1,128 @@ - -# Define your c compiler. I recommend gcc if you've got it. -#CC = cc +#Define your C compiler. I recommend gcc if you have it. CC = gcc +#CC = cc # Define your optimization flags. Most compilers understand -O and -O2, -# Debugging (don't use) -#OPTS = -Wall -g -# Standard +# 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 - -# There are a few defines which may be necessary to make frotz compile on -# your system. Most of these are fairly straightforward. -# -DNO_MEMMOVE: Use this if your not-so-standard c library lacks the -# memmove(3) function. (SunOS, other old BSDish systems) -# -# You may need to define one of these to get the getopt prototypes: -# -DUSE_UNISTD_H: Use this if your getopt prototypes are in unistd.h. -# (Solaris?) -# -DUSE_GETOPT_H: Use this if you have a separate /usr/include/getopt.h -# file containing the getopt prototypes. (Linux, IRIX 5?) -# -DUSE_NOTHING: I've heard reports of some systems defining getopt in -# stdlib.h, which unix.c automatically includes. -# (*BSD, Solaris?) -# -# If none of the above are defined, frotz will define the getopt prototypes -# itself. -# -# -DUSE_NCURSES_H: Use this if you want to include ncurses.h rather -# than curses.h. -# -# These defines add various cosmetic features to the interpreter: -# -DCOLOR_SUPPORT: If the terminal you're using has color support, frotz -# will use the curses color routines...if your curses -# library supports color. -# -DEMACS_EDITING: This enables some of the standard emacs editing keys -# (Ctrl-B,-F,-P,-N, etc.) to be used when entering -# input. I can't see any reason why you wouldn't want -# to define it--it can't hurt anything--but you do -# have that option. -# -#DEFS = -DUSE_GETOPT_H -DCOLOR_SUPPORT -DEMACS_EDITING -DEFS = - -# This should point to the location of your curses or ncurses include file -# if it's in a non-standard place. -#INCL = -I/usr/local/include +#-malign-jumps=2 + +# Define where you want Frotz to be installed. Usually this is /usr/local +PREFIX = /usr/local +#PREFIX = + +# 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. Usually this requires ncurses. +#COLOR_DEFS = -DCOLOR_SUPPORT + +# Uncomment this if you have an OSS soundcard driver and want classical +# Infocom sound support. +#SOUND_DEFS = -DOSS_SOUND + +# This should point to the location of your curses.h or ncurses.h include +# file if your compiler doesn't know about it. +INCL = -I/usr/local/include +#INCL = -I/usr/pkg/include +#INCL = -I/usr/freeware/include #INCL = -I/5usr/include -INCL = +#INCL = # This should define the location and name of whatever curses library you're -# linking with. -#LIB = -L/usr/local/lib -#CURSES = -lncurses +# linking with. Usually, this isn't necessary if /etc/ld.so.conf is set +# up correctly. +LIB = -L/usr/local/lib +#LIB = -L/usr/pkg/lib +#LIB = -L/usr/freeware/lib #LIB = -L/5usr/lib -LIB = -CURSES = -lcurses +#LIB = +# One of these must be uncommented, use ncurses if you have it. +CURSES = -lncurses # Linux always uses ncurses. +#CURSES = -lcurses + +# Comment this out if you're not using ncurses. +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. +##################################################### + +VERSION = 2.40 + +BINNAME = frotz + +DISTNAME = $(BINNAME)-$(VERSION) + +OBJECTS = buffer.o err.o fastmem.o files.o hotkey.o input.o main.o \ + math.o object.o process.o quetzal.o random.o redirect.o \ + screen.o sound.o stream.o table.o text.o ux_init.o ux_input.o \ + ux_pic.o ux_screen.o ux_text.o variable.o \ + ux_audio_none.o ux_audio_oss.o + +OPT_DEFS = -DCONFIG_DIR="\"$(CONFIG_DIR)\"" $(CURSES_DEF) + +COMP_DEFS = $(OPT_DEFS) $(COLOR_DEFS) $(SOUND_DEFS) $(SOUNDCARD) \ + $(MEMMOVE_DEF) + +CFLAGS = $(OPTS) $(COMP_DEFS) $(INCL) + + +$(BINNAME): soundcard.h $(OBJECTS) + $(CC) -o $(BINNAME)$(EXTENSION) $(OBJECTS) $(LIB) $(CURSES) + +soundcard.h: + @if [ ! -f soundcard.h ] ; then ./findsound.sh; fi + +install: $(BINNAME) + strip $(BINNAME) + install -c -m 755 $(BINNAME)$(EXTENSION) $(PREFIX)/bin + install -c -m 644 $(BINNAME).6 $(PREFIX)/man/man6 + +uninstall: + rm -f $(PREFIX)/bin/$(BINNAME)$(EXTENSION) + rm -f $(PREFIX)/man/man6/$(BINNAME).6 + +deinstall: uninstall + +clean: + rm -f *.o + +distro: dist + +dist: distclean + cd .. ; tar cfv $(DISTNAME).tar $(DISTNAME) ; gzip -f $(DISTNAME).tar -OBJECTS = buffer.o fastmem.o files.o hotkey.o input.o main.o math.o object.o \ - process.o random.o redirect.o screen.o sound.o stream.o table.o \ - text.o ux_init.o ux_input.o ux_pic.o ux_screen.o ux_sample.o \ - ux_text.o variable.o +distclean: clean + rm -f soundcard.h + rm -f $(BINNAME)$(EXTENSION) -CFLAGS = $(OPTS) $(DEFS) $(INCL) +realclean: distclean -frotz: $(OBJECTS) - $(CC) -o frotz $(OBJECTS) $(LIB) $(CURSES) +clobber: distclean +help: + @echo + @echo "Targets:" + @echo " frotz" + @echo " install" + @echo " uninstall" + @echo " clean" + @echo " distclean" + @echo diff --git a/README b/README new file mode 100644 index 0000000..69f0783 --- /dev/null +++ b/README @@ -0,0 +1,72 @@ +FROTZ V2.40 - An interpreter for all Infocom games and other Z-machine games. +Complies with standard 1.0 of Graham Nelson's specification. + +Originally written by Stefan Jokisch in 1995-1997. +Reference code currently maintained by Jim Dunleavy. +Ported to Unix by Galen Hazelwood. +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. +- Stricter error checking. +- Default use fo the Quetzal file format. Command line option to use the + old format. +- Now distributed under the GNU Public License. + +The latest information on Unix Frotz is available at the Unix Frotz +homepage at http://www.cs.csubak.edu/~dgriffi/frotz/. + +The latest release of Unix Frotz is available for FTP download at +ftp://ftp.gmd.de/if-archive/infocom/interpreters/frotz/frotz-.tar.gz +or any reliable mirror such as http://www.ifarchive.org. + +For installation information, see the file "INSTALL". + +For information on what Interactive Fiction is and how to play it, see the +file "HOW_TO_PLAY". + +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. + + +============ +Packaging || +============ +Please 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 so I can note that on the Unix Frotz +webpage at http://www.cs.csubak.edu/~dgriffi. _PLEASE_ don't try to do +anything like have auto-install scripts grab files from that page. Use +ftp.gmd.de or one of its mirrors for that. + + +==================== +Maintainer switch || +==================== +A few months after I submitted my superpatch against Galen Hazelwood's +Unix port of Frotz 2.32, I started playing around again with Unix Frotz to +start adding new features. I emailed Galen asking if he would be putting +out a new release of Unix Frotz any time soon because I had some nifty new +features to add. He replied with "no" and asked if I was volunteering to +take over the port. + + +==================== +The switch to GPL || +==================== +Beginning with version 2.40, Frotz is now licensed under the GNU Public +License. This change came about from an email conversation with Stefan +Jokisch in late May 2000. I asked if he was still developing the core +code and asked if he'd consider changing the license to GPL or +BSD-like. He replied by introducing me to Jim Dunleavy to whom he handed +off development of the Frotz core code. At that time, he decided to +change the license to GPL starting with the one Jim was working on. Jim +and I then swapped patches back and forth to do some more work on the core +code and so now we have Frotz 2.40. + diff --git a/Readme.unix b/Readme.unix deleted file mode 100644 index 972b59e..0000000 --- a/Readme.unix +++ /dev/null @@ -1,318 +0,0 @@ -(This Readme file was adapted from the one released with the MS-DOS binary.) - -FROTZ V2.32 - an interpreter for all Infocom games. Complies with standard -1.0 of Graham Nelson's specification. Written by Stefan Jokisch in 1995-7 - - This program once started as a re-make of Mark Howell's Zip, but - has grown into an utterly new interpreter. - - Frotz is freeware: It may be used and distributed freely provided - no commercial profit is involved. (c) 1995-1997 Stefan Jokisch. - - Please report bugs to s.jokisch@avu.de - Please report unix bugs to galenh@micron.net - - This is the unix port of Stefan Jokisch's nifty-neato-cool Z-machine -interpreter. There are only two things you need to compile Frotz: - - * Some variant of Unix with an ANSI C compiler (gcc works fine) - * A reasonably good SYSV derived curses library - - Note that the second requirement is _very_ important. I had several people -send me complaints about Frotz not working, and found out they were unaware -that their curses library was obsolete. If your system curses won't work, -pick up and compile the ncurses library from ftp://prep.ai.mit.edu/pub/gnu -or any GNU mirror site. - -Changes since Release 1: - - * Fixed problems with backspace - * Now supports "delete" key for editing - * Wired in tab completion support (which I stupidly missed) - * Added emacs editing key support (-DEMACS_EDITING) - -How to Compile: - - This source distribution comes with a unsophisticated but functional -Makefile. Edit it to reflect your system; the comments are fairly self- -explanatory. Once that's done, just type "make". - - Color support is available by putting -DCOLOR_SUPPORT in the designated -spot. I've added code in ux_init.c for sound hooks, but as no sound code is -actually written, defining SOUND_SUPPORT will merely cause compilation to fail. - - Thanks for giving this a try. Any bug reports, success stories, or -constructive criticism will be greatly appreciated. - - -- Galen Hazelwood - -Syntax: frotz [options] story-file - - -d disable color - - If frotz discovers that your terminal has color support, it will - enable it by default. This switch overrides that behavior. - - -f # set the foreground colour - -b # set the background colour - - The color numbers are the Z-machine colors, which are as follows: - 2 = black 3 = red 4 = green 5 = yellow - 6 = blue 7 = magenta 8 = cyan 9 = white - - If color support is disabled in the binary, or not available on - your terminal, these options do nothing. - - -i ignore runtime errors - - Set this switch and Frotz no longer worries about anything the - game tries to do. This can help you to get around fatal errors. - - -w # set the screen width - -h # set the screen height - -l # set the left margin - -r # set the right margin - - Setting the screen format can be useful if you are running Frotz - under Microsoft Windows, or if you want to test a game on a more - narrow screen. Setting the margins is a matter of taste; Infocom - interpreters usually set a right margin of one character (-r1). - - -S # set the width of the transscript file - - 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 -S0 - to deactivate automatic line splitting in transscript files. - - -c # set the number of context lines - - When the game prints several pages of text in a row, Frotz stops - for a more prompt after each page. The first prompt appears when - your input reaches the top of the window. Further prompts appear - when the previous page has been scrolled off the window. You can - use this switch to make the latter more prompts appear earlier. - - -u # set the number of undo slots for multiple undo - - Frotz tries to allocate as much conventional memory as possible - for multiple UNDO. If this strategy causes some kind of problem, - use this switch to set a tighter limit. In particular, you might - want to turn off the UNDO feature altogether by typing -u0. - - -s # set the random number seed - - The given seed value is used as the initial seed value on every - restart. This is helpful for testing games like 'Curses' which - make random decisions before the first input (such that the hot - key Alt-S does not really help). The meaning of seed values is - explained in the next section. - - -x expand abbreviations (g, x, z ==> again, examine, wait) - - This switch was made for old Infocom games that lack the common - abbreviations introduced in later games. Use it with caution: A - few games might use "g", "x" or "z" for different purposes. - - -o watch object movement - -O watch object locating - -a watch attribute assignment - -A watch attribute testing - - Although these switches may be of assistance while debugging new - games, they are are actually meant to be cheat functions. The -o - switch, for example, helps to locate the thief in 'Zork 1' and - the cat in 'Curses'. The other switches produce a lot of obscure - messages during the game; but some of these messages might give - you important clues if you watch carefully. - - -t set the Tandy bit - - Some old Infocom games were sold by the Tandy Corporation. These - games behave slightly different when you use this option. For - example, 'The Witness' gets censored: bastards turn into idiots, - private dicks into private eyes and so on. - - -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. - - -P alter behaviour of piracy opcode - - The piracy opcode was never used by Infocom, and this switch is - only useful for those who like to toy around with Z-code. - -Special keys: - - Alt-D - toggle debugging options (-a, -A, -o, -O) - Alt-H - help on hot keys - Alt-N - new game (restart) - Alt-P - turn on input line playback - Alt-R - toggle input line recording on/off - Alt-S - set the random number seed - Alt-U - multiple undo, works even for old V1 to V4 games - Alt-X - exit game - - When testing a text adventure it can be difficult to reproduce a - specific bug. To avoid this problem you should use the Alt-R key - to record all your inputs in a command file. Later you can press - Alt-P to feed the command file back into Frotz. In many cases, - however, you will find that the result is different because most - games contain random events. Luckily, Frotz provides a hot key - to control these events. Type Alt-S and you are asked for a seed - value, i.e. a value in the range from 1 to 32767. Normally, you - would choose a number >= 1000. Smaller values generate a special - sequence of random numbers as proposed by Nelson. (For instance, - the seed value 4 generates 1, 2, 3, 4, 1, 2, 3, 4, 1...). In any - case, random events become predictable until the next restart. - - See also the command line option -s above. - - cursor left/Ctrl-B - move one character to the left - cursor right/Ctrl-F - move one character to the right - home/Ctrl-A - move to beginning of line - end/Ctrl-E - move to end of line - backspace - delete character to the left - delete/Ctrl-D - delete character below cursor - insert - toggle overwrite mode on/off - escape/Ctrl-U - delete whole input line - cursor up/Ctrl-P - get previous command - cursor down/Ctrl-N - get next command - page up - scroll status window up ('Beyond Zork') - page down - scroll status window down ('Beyond Zork') - tab - word completion (like "tcsh" under Unix) - - Note that the various Control aliases only function if emacs editing - support has been compiled into your binary. See the Makefile for - more information. - - When you need to type an unpleasantly long word, try to type the - first three or four letters then press the tabulator key. If you - are lucky, Frotz fills in some or all of the missing letters. A - high beep noise indicates that the word is ambiguous; a low beep - indicates that it does not exist. Apart from that, you can also - use the history feature to get to previous input lines. Type the - beginning of the input line you are looking for, then use cursor - up/down to scroll through all input lines matching that prefix. - -Questions and answers: - - Q: What is Frotz? - A: Frotz runs text adventures which come in so-called story files: - ZORK1.DAT, TRINITY.DAT, CURSES.Z5, JIGSAW.Z8, ARTHUR.ZIP etc. - - Q: Where can I find story files to use with Frotz? - A: First, you can use the files from your original Infocom games. It - is possible to play Atari ST, Amiga or Macintosh games on your PC - once you manage to transfer the story files. Some people even - extracted story files from old Atari 800, Apple II and C-64 disks - (ask your local 8bit guru). Second, there is an increasing number - of new games available on the Internet. Check the if-archive at - ftp.gmd.de. - - Q: Why does Frotz stop with an error message? - A: It might have detected a bug in the story file other interpreters - overlooked. Use the -i switch to run your story file anyway. It's - also possible that the story file is corrupt; be sure to download - story files in binary mode, especially when you use a WWW browser. - - Q: Is there a way to exit Frotz in emergency situations? - A: Try Ctrl-break, Ctrl-C, or Ctrl-Z. The last one suspends your process - and returns you to the shell, allowing you to kill it. - - Q: What do I need to play graphic games? - A: A different port of Frotz. Try the MS-DOS version if you can use - it, or wait for the X Windows port. You can run some of the V6 games - without graphics--Arthur and Journey seem to work, but not Shogun or - Zork Zero. (Shogun dies because I can't backscroll; Zork 0 just - sputters and dies rather amusingly.) - - Q: What do I need for sound? - A: A different port of Frotz, or a later version of this one. Sound - support will first be available on Linux, and any other system which - can use the Open Sound System (or OSS/Lite). - - Q: How can I send transscription to the printer? - A: Type PRN as file name. - - Q: Why do I get weird characters instead of accented letters? - A: Because your display/terminal/window doesn't support the Latin-1 - character set properly. Use the -p flag, which uses plain ASCII - output and converts accented letters appropriately. - - Q: Why don't the number pad keys work in 'Beyond Zork'? - A: Because curses dosen't seem to know how to handle them properly. - I might add a hack to support them later. - - Q: How can I scroll the status window in 'Beyond Zork'? - A: Use page up/down instead of cursor up/down. - - Q: Why are my default color selections so weird? - A: You're used to using the standard PC color values. The unix port - wants color numbers in Z-machine form. Quick reference: - 2 = black 3 = red 4 = green 5 = yellow - 6 = blue 7 = magenta 8 = cyan 9 = white - - -List of fatal errors: - - - "Bad stack frame" - - "Byte swapped story file" - - "Call to illegal address" - - "Call to non-routine" [1] - - "Cannot open story file" - - "Division by zero" - - "Error reading save file" - - "Illegal attribute" - - "Illegal object" [2] - - "Illegal window" - - "Illegal window property" - - "Jump to illegal address" - - "Nesting stream #3 too deep" - - "No such property" - - "Out of memory" - - "Print at illegal address" - - "Stack overflow" [3] - - "Stack underflow" [4] - - "Store out of dynamic memory" - - "Story file read error" - - "Text buffer overflow" - - "Unknown opcode" - - "Unknown Z-code version" - - "Can't Happen (os_scroll_area)" [5] - - [1] The first byte of a routine must be less than 16. - [2] In V4 and above, object numbers > 2000 are considered illegal. - [3] Checked on every call instruction. - [4] Checked on every return from a subroutine. - [5] Unix-specific. Should only happen in Shogun. - -Acknowledgements: - - Many thanks to Paul D. Doherty for his continuing support of this - project. Thanks to everyone who sent bug reports, contributions or - helpful hints (in alphabetical order): - - Thomas Biskup, Ian Carpenter, Graeme Cree, Jason Dyer, - Carl Edman, Julian Eggebrecht, Bernhard Fuchs, Joe Hachem, - John Kennedy, Kirk Klobe, Marnix Klooster, John Mackin, - Paul O'Brian, Magnus Olsson, Barry Prescott, L. Ross Raszewski, - Ambat Sasi Nair, Alan Sherrod, Linards Ticmanis and Paolo Vece. - - Last but not least, thanks to the porters: - - David Kinder (Amiga), Rich Lawrence (Windows 95/NT), - Andrew Holdsworth (RiscOS), Christos Dimitrakakis (HP-UX), - Christopher J. Madsen (OS/2), Galen Hazelwood (Unix curses library), - Ian Dean (Windows CE). - - Executables are available from ftp.gmd.de and from - - http://www.geocities.com/SiliconValley/Heights/3222/frotz.html - - which is the Frotz home page maintained by Chris Madsen. - - diff --git a/TODO b/TODO new file mode 100644 index 0000000..3f7b4ff --- /dev/null +++ b/TODO @@ -0,0 +1,54 @@ +================================= +Stuff to do for a minor update || +================================= + +Frotz has so far, been completely configurable throught the command-line. +When I added the config file support, I noticed the need to add overrides +for some options like "color", "sound", and "ascii". This sort of thing +is exhausting available command-line switches. + +Remove debug options from command line. They should be set through the +hotkey menu instead + +Hotkey menu needs work. + +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. + +Add an option to define the type of machine the Z-Machine is running on +(DECsystem 20, AppleIIe, etc). Using the wrong machine for certain V6 +games might cause trouble and this is a rather frivilous option, so I'll +let it go for now. + +Add support for more sound drivers, not just OSS. + OSS audio on bigendian machines doesn't seem to work! + +Fix bugs! (see file BUGS) + + +=================================================== +Stuff to do for the next major release (ie 2.50) || +=================================================== + +More sensible handling of saves, transcripts, and game files. This might +be best handled by doing a complete rewrite of the Unix-specific code, +which probably ought to be done anyhow. + +Curses menu to select desired game. Current working code heavily borrows +from Alan Shutko's "int-fiction" frontend program. + +Blorb gamefile support. + +Blorb sound support on all platforms. + +An X11 graphical interface using GTK widgets (thus allowing V6 full support). + +========================================= +Stuff that would be really really nice || +========================================= + +MacOS X support. Hey, it's Unix! +BeOS support. I guess it's close enough to Unix. diff --git a/Todo b/Todo deleted file mode 100644 index b189a4e..0000000 --- a/Todo +++ /dev/null @@ -1,12 +0,0 @@ -Things to do and bugs to investigate ------------------------------------- - -* Write sound support. This is on hold until frotz supports the new "Blorb" - format. - -* Border Zone has a strange status line feature, mixing bright and dull - whites. Not sure what's going on here. - -* Add support for GNU readline/history libraries instead of the kludgy - editing routines I wrote. If I can manage this, I'll probably release - it as a patch rather than part of a release. diff --git a/buffer.c b/buffer.c index 6aa48a1..f817187 100644 --- a/buffer.c +++ b/buffer.c @@ -1,8 +1,21 @@ -/* - * buffer.c +/* 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. * - * Text buffering and word wrapping + * 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" @@ -12,7 +25,7 @@ extern void stream_word (const zchar *); extern void stream_new_line (void); static zchar buffer[TEXT_BUFFER_SIZE]; -static bufpos = 0; +static int bufpos = 0; static zchar prev_c = 0; @@ -72,7 +85,9 @@ void print_char (zchar c) /* Flush the buffer before a whitespace or after a hyphen */ - if (c == ' ' || c == ZC_INDENT || c == ZC_GAP || prev_c == '-' && c != '-') + 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 */ @@ -91,7 +106,7 @@ void print_char (zchar c) buffer[bufpos++] = c; if (bufpos == TEXT_BUFFER_SIZE) - runtime_error ("Text buffer overflow"); + runtime_error (ERR_TEXT_BUF_OVF); } else stream_char (c); diff --git a/err.c b/err.c new file mode 100644 index 0000000..d065530 --- /dev/null +++ b/err.c @@ -0,0 +1,153 @@ +/* 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 (err_report_mode == ERR_REPORT_FATAL + || (!option_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 ((err_report_mode == ERR_REPORT_ALWAYS) + || (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 (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 */ diff --git a/fastmem.c b/fastmem.c index ea9b001..0ef77ea 100644 --- a/fastmem.c +++ b/fastmem.c @@ -1,15 +1,32 @@ -/* - * fastmem.c +/* fastmem.c - Memory related functions (fast version without virtual memory) + * Copyright (c) 1995-1997 Stefan Jokisch + * + * This file is part of Frotz. * - * Memory related functions (fast version without virtual memory) + * 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__ +#ifdef MSDOS_16BIT #include @@ -40,6 +57,13 @@ 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); @@ -53,11 +77,29 @@ zbyte far *pcp = NULL; static FILE *story_fp = NULL; -static zbyte far *undo[MAX_UNDO_SLOTS]; +/* + * 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 undo_slots = 0; -static undo_count = 0; -static undo_valid = 0; +static int undo_count = 0; /* * get_header_extension @@ -207,26 +249,8 @@ void init_memory (void) /* Open story file */ - if ((story_fp = fopen (story_name, "rb")) == NULL) { - - char *path, *p, tmp[256]; - - /* Story file may be on INFOCOM_PATH, rather than current directory */ - if ((path = getenv("INFOCOM_PATH")) == NULL) - os_fatal ("Cannot open story file"); - - p=strtok(path,":"); - while (p != NULL) { - sprintf(tmp, "%s/%s", p, story_name); - if ((story_fp = fopen (tmp, "rb")) == NULL) - p = strtok(NULL, ":"); - else - p = NULL; - } - - if (story_fp == NULL) - os_fatal ("Cannot open story file"); - } + if ((story_fp = os_path_open(story_name, "rb")) == NULL) + os_fatal ("Cannot open story file"); /* Allocate memory for story header */ @@ -276,7 +300,7 @@ void init_memory (void) } - no_match: + no_match: ; /* null statement */ } @@ -365,26 +389,55 @@ void init_undo (void) { void far *reserved; - if (reserve_mem != 0) + reserved = NULL; /* makes compilers shut up */ + + if (reserve_mem != 0) { if ((reserved = malloc (reserve_mem)) == NULL) return; - - while (undo_slots < option_undo_slots && undo_slots < MAX_UNDO_SLOTS) { - - void far *mem = malloc ((long) sizeof (stack) + h_dynamic_size); - - if (mem == NULL) - break; - - undo[undo_slots++] = mem; - } + /* 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 + option_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 * @@ -397,9 +450,10 @@ void reset_memory (void) fclose (story_fp); - while (undo_slots--) - free (undo[undo_slots]); - + if (undo_mem) { + free_undo (undo_count); + free (undo_mem); + } free (zmp); }/* reset_memory */ @@ -415,7 +469,7 @@ void storeb (zword addr, zbyte value) { if (addr >= h_dynamic_size) - runtime_error ("Store out of dynamic memory"); + runtime_error (ERR_STORE_RANGE); if (addr == H_FLAGS + 1) { /* flags register is modified */ @@ -483,6 +537,7 @@ void z_restart (void) restart_screen (); sp = fp = stack + STACK_SIZE; + frame_count = 0; if (h_version != V6) { @@ -597,68 +652,93 @@ void z_restore (void) if ((gfp = fopen (new_name, "rb")) == NULL) goto finished; - /* Load game file */ + if (option_save_quetzal) { + success = restore_quetzal (gfp, story_fp); - release = (unsigned) fgetc (gfp) << 8; - release |= fgetc (gfp); + } else { + /* Load game file */ - (void) fgetc (gfp); - (void) fgetc (gfp); + release = (unsigned) fgetc (gfp) << 8; + release |= fgetc (gfp); - /* Check the release number */ + (void) fgetc (gfp); + (void) fgetc (gfp); - if (release == h_release) { + /* Check the release number */ - pc = (long) fgetc (gfp) << 16; - pc |= (unsigned) fgetc (gfp) << 8; - pc |= fgetc (gfp); + if (release == h_release) { - SET_PC (pc) + pc = (long) fgetc (gfp) << 16; + pc |= (unsigned) fgetc (gfp) << 8; + pc |= fgetc (gfp); - sp = stack + (fgetc (gfp) << 8); - sp += fgetc (gfp); - fp = stack + (fgetc (gfp) << 8); - fp += fgetc (gfp); + SET_PC (pc) - for (i = (int) (sp - stack); i < STACK_SIZE; i++) { - stack[i] = (unsigned) fgetc (gfp) << 8; - stack[i] |= fgetc (gfp); - } + sp = stack + (fgetc (gfp) << 8); + sp += fgetc (gfp); + fp = stack + (fgetc (gfp) << 8); + fp += fgetc (gfp); - fseek (story_fp, 0, SEEK_SET); + for (i = (int) (sp - stack); i < STACK_SIZE; i++) { + stack[i] = (unsigned) fgetc (gfp) << 8; + stack[i] |= fgetc (gfp); + } - 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); - } + 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 */ + /* Check for errors */ - if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size) - os_fatal ("Error reading save file"); + if (ferror (gfp) || ferror (story_fp) || addr != h_dynamic_size) + success = -1; + else - /* Reset upper window (V3 only) */ + /* Success */ - if (h_version == V3) - split_window (0); + success = 2; - /* Initialise story header */ + } else print_string ("Invalid save file\n"); + } - restart_header (); + if ((short) success >= 0) { - /* Success */ + /* Close game file */ - success = 2; + fclose (gfp); - } else print_string ("Invalid save file\n"); + if ((short) success > 0) { + zbyte old_screen_rows; + zbyte old_screen_cols; - /* Close game file */ + /* In V3, reset the upper window. */ + if (h_version == V3) + split_window (0); - fclose (gfp); + 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: @@ -671,47 +751,120 @@ finished: }/* z_restore */ /* - * restore_undo + * mem_diff * - * This function does the dirty work for z_restore_undo. + * 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. * */ -int restore_undo (void) +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 */ - if (undo_slots == 0) /* undo feature unavailable */ +/* + * mem_undiff + * + * Applies a quetzal-like diff to dest + * + */ - return -1; +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); + } - else if (undo_valid == 0) /* no saved game state */ + dest += runlen + 1; + } else { + *dest++ ^= c; + } + } +}/* mem_undiff */ - return 0; +/* + * restore_undo + * + * This function does the dirty work for z_restore_undo. + * + */ - else { /* undo possible */ +int restore_undo (void) +{ - long pc; + if (option_undo_slots == 0) /* undo feature unavailable */ - if (undo_count == 0) - undo_count = undo_slots; + return -1; - memcpy (stack, undo[undo_count - 1], sizeof (stack)); - memcpy (zmp, undo[undo_count - 1] + sizeof (stack), h_dynamic_size); + if (curr_undo == NULL) /* no saved game state */ - pc = ((long) stack[0] << 16) | stack[1]; - sp = stack + stack[2]; - fp = stack + stack[3]; + return 0; - SET_PC (pc) + /* undo possible */ - restart_header (); + 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)); - undo_count--; - undo_valid--; + curr_undo = curr_undo->prev; - return 2; + restart_header (); - } + return 2; }/* restore_undo */ @@ -727,7 +880,7 @@ void z_restore_undo (void) store ((zword) restore_undo ()); -}/* restore_undo */ +}/* z_restore_undo */ /* * z_save, save [a part of] the Z-machine state to disk. @@ -790,40 +943,44 @@ void z_save (void) if ((gfp = fopen (new_name, "wb")) == NULL) goto finished; - /* Write game file */ + if (option_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); + 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) + GET_PC (pc) - fputc ((int) (pc >> 16) & 0xff, gfp); - fputc ((int) (pc >> 8) & 0xff, gfp); - fputc ((int) (pc) & 0xff, gfp); + 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); + 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); + 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); - } + 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); + 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++; + 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 */ @@ -856,35 +1013,58 @@ finished: int save_undo (void) { - long pc; + long diff_size; + zword stack_size; + undo_t *p; - if (undo_slots == 0) /* undo feature unavailable */ + if (option_undo_slots == 0) /* undo feature unavailable */ return -1; - else { /* save undo possible */ - - if (undo_count == undo_slots) - undo_count = 0; - - GET_PC (pc) - - stack[0] = (zword) (pc >> 16); - stack[1] = (zword) (pc & 0xffff); - stack[2] = (zword) (sp - stack); - stack[3] = (zword) (fp - stack); - - memcpy (undo[undo_count], stack, sizeof (stack)); - memcpy (undo[undo_count] + sizeof (stack), zmp, h_dynamic_size); - - if (++undo_count == undo_slots) - undo_count = 0; - if (++undo_valid > undo_slots) - undo_valid = undo_slots; - - 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 == option_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 */ diff --git a/files.c b/files.c index 784f04d..0410e13 100644 --- a/files.c +++ b/files.c @@ -1,8 +1,21 @@ -/* - * files.c +/* 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. * - * Transscription, recording and playback + * 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 @@ -28,7 +41,7 @@ char command_name[MAX_FILE_NAME + 1] = DEFAULT_COMMAND_NAME; extern char latin1_to_ibm[]; #endif -static script_width = 0; +static int script_width = 0; static FILE *sfp = NULL; static FILE *rfp = NULL; @@ -341,7 +354,7 @@ static void record_code (int c, bool force_encoding) static void record_char (zchar c) { - if (c != ZC_RETURN) + if (c != ZC_RETURN) { if (c < ZC_HKEY_MIN || c > ZC_HKEY_MAX) { @@ -351,7 +364,7 @@ static void record_char (zchar c) record_code (mouse_x, TRUE); record_code (mouse_y, TRUE); } - + } } else record_code (1000 + c - ZC_HKEY_MIN, TRUE); }/* record_char */ @@ -476,7 +489,7 @@ static zchar replay_char (void) if ((c = replay_code ()) != EOF) { - if (c != '\n') + if (c != '\n') { if (c < 1000) { @@ -490,6 +503,7 @@ static zchar replay_char (void) return c; } else return ZC_HKEY_MIN + c - 1000; + } ungetc ('\n', pfp); diff --git a/findsound.sh b/findsound.sh new file mode 100755 index 0000000..91918a7 --- /dev/null +++ b/findsound.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# This thing is intended primarily to find soundcard.h for use with the +# OSS soundcard driver. It will first look in then +# . Linux systems use the former. *BSD uses the latter. +# 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" +SOUNDCARD_H="soundcard.h" +OUTFILE="soundcard.h" + +echo "checking for $SOUNDCARD_H" + +if [ -f $OUTFILE ] ; then + exit 0 +fi + +cat << EOF > $OUTFILE +/* + * This file automatically generated by findsound.sh which run by the + * Makefile found in the Unix Frotz 2.40 source distribution. + * Copying this nasty hack to find headers which may be in any of several + * places is not reccomended. I didn'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..." + if [ -r $FILE ] ; then + echo '#include ' >> $OUTFILE + else + echo "Oops... Can't read it!" + 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..." + if [ -r $FILE ] ; then + echo '#include ' >> $OUTFILE + else + echo "Oops... Can't read it!" + exit 2 + fi + exit 0 +fi + + diff --git a/frotz.6 b/frotz.6 index f5dd8fb..a93dbe3 100644 --- a/frotz.6 +++ b/frotz.6 @@ -1,18 +1,30 @@ .\" -*- nroff -*- -.TH FROTZ 6 2.32 +.TH FROTZ 6 2.40 .SH NAME -frotz \- interpreter for all Infocom games +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 was a virtual machine designed +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 +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 @@ -20,51 +32,56 @@ 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 test an attribute value, -the test and the result will be reported. +Watch attribute testing. Every time the z-machine tests an attribute +value, the test and the result will be reported. .TP -.B \-b N -Sets the default background color. N corresponds to one of the Z-machine -colors, which are as follows: +.B \-b +Sets the default background color. corresponds to one of the +Z-machine colors, which are as follows: .br -2 = black 3 = red 4 = green 5 = yellow +.B black red green yellow blue magenta cyan white .br -6 = blue 7 = magenta 8 = cyan 9 = white -.br -If color support is disabled in the binary, or not -available on your terminal, this option does nothing. +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 +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 +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 -Disables color. If frotz discovers that your terminal has color support, -it will enable it by default. This switch overrides that behavior. +Disable color. .TP -.B \-f N -Sets the default foreground color. N is a number corresponding to one of -the Z-machine colors, as listed above. If color support is disabled in -the binary, or not available on your terminal, this option does nothing. +.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 +.B 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 runtime errors. Set this switch and Frotz no longer worries about -anything the game tries to do. This can help you to get around fatal errors. -.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 detail the moving of objects in the object tree. +interpreter which describe the moving of objects in the object tree. .TP .B \-O Watch object location. These debugging messages detail the locations of @@ -77,8 +94,19 @@ 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, and -this switch is only useful for those who like to toy around with Z-code. +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 or restore using the +old Frotz format, use this flag. .TP .B \-r N Sets the right margin. @@ -100,11 +128,11 @@ to deactivate automatic line splitting in transscript files. 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 sequals, and Witness has it's language +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 +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. @@ -114,18 +142,285 @@ 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 was made for old Infocom games that lack the common -abbreviations introduced in later games. Use it with caution: A -few games might use "g", "x" or "z" for different purposes. +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. 1 = report first error +.br +2 = report all errors, 3 = exit after any error. +.br +Default is 0 (don't report errors). + + +.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 color +\ \ yes\ |\ no +.br +Use color text. Default is "yes" if supported. + +.PP +.BR foreground +\ \ +.br +Set foreground color. Default is terminal's default forground color. + +.PP +.BR background +\ \ +.br +Set background color. Default is terminal's default background color. + +.PP +.BR errormode +\ \ never\ |\ once\ |\ always\ |\ never +.br +Set error reporting mode. Default is "once". + +.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. + +.PP +.BR ascii +\ \ on\ |\ off +.br +Use plain ASCII only. Default is "off". + +.PP +.BR sound +\ \ on\ |\ off +.br +Turn sound effects on or off. Default is "on". + +.PP +.BR randseed +\ \ +.br +Set random number seed. Default comes from the Unix epoch. + +.PP +.BR undo_slots +\ \ +.br +Set number of undo slots. Default is 500. + +.PP +.BR tandy +\ \ on\ |\ off +.br +Set Tandy bit. Default is off. 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. + +.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 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. + +.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 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. + +.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. + +.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_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". + +.PP +.BR obj_loc +\ \ on\ |\ off +.br +Watch object location. These debugging messages detail the locations of +objects in the object tree. + .SH ENVIRONMENT -If the INFOCOM_PATH environment variable is defined, frotz will search -those paths for game files. -.SH "SEE ALSO" +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/frotz/ + +.PP +The latest release of Unix Frotz is here: +.br +ftp://ftp.gmd.de/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.ifarchive.org + .SH BUGS This program has no bugs. no bugs. no bugs. no *WHAP* thank you. -.SH AUTHOR + +.SH AUTHORS .B Frotz -was written by Stefan Jokisch in 1995-7. The unix port (and this man -page) were done by Galen Hazelwood. +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 nitfol (6) +.BR rezrov (6) +.BR jzip (6) +.BR xzip (6) +.BR inform (1) + + diff --git a/frotz.conf-big b/frotz.conf-big new file mode 100644 index 0000000..373f7ed --- /dev/null +++ b/frotz.conf-big @@ -0,0 +1,97 @@ +# 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 diff --git a/frotz.conf-small b/frotz.conf-small new file mode 100644 index 0000000..d58e4db --- /dev/null +++ b/frotz.conf-small @@ -0,0 +1,16 @@ +# 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 + diff --git a/frotz.h b/frotz.h index b62bc83..678e784 100644 --- a/frotz.h +++ b/frotz.h @@ -10,12 +10,17 @@ this definition, but have the unix port see the curses version. */ #ifndef __UNIX_PORT_FILE +#include typedef int bool; #define TRUE 1 #define FALSE 0 #endif + +#include + + typedef unsigned char zbyte; typedef unsigned short zword; @@ -35,7 +40,7 @@ typedef unsigned char zchar; /*** Constants that may be set at compile time ***/ #ifndef MAX_UNDO_SLOTS -#define MAX_UNDO_SLOTS 25 +#define MAX_UNDO_SLOTS 500 #endif #ifndef MAX_FILE_NAME #define MAX_FILE_NAME 80 @@ -62,6 +67,9 @@ typedef unsigned char zchar; #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 ***/ @@ -254,81 +262,9 @@ extern zbyte *zmp; #endif -#if defined (__MSDOS__) - -extern zbyte far *pcp; -extern zbyte far *zmp; - -#define lo(v) ((zbyte *)&v)[0] -#define hi(v) ((zbyte *)&v)[1] - -#define SET_WORD(addr,v) asm {\ - les bx,zmp;\ - add bx,addr;\ - mov ax,v;\ - xchg al,ah;\ - mov es:[bx],ax } - -#define LOW_WORD(addr,v) asm {\ - les bx,zmp;\ - add bx,addr;\ - mov ax,es:[bx];\ - xchg al,ah;\ - mov v,ax } - -#define HIGH_WORD(addr,v) asm {\ - mov bx,word ptr zmp;\ - add bx,word ptr addr;\ - mov al,bh;\ - mov bh,0;\ - mov ah,0;\ - adc ah,byte ptr addr+2;\ - mov cl,4;\ - shl ax,cl;\ - add ax,word ptr zmp+2;\ - mov es,ax;\ - mov ax,es:[bx];\ - xchg al,ah;\ - mov v,ax } - -#define CODE_WORD(v) asm {\ - les bx,pcp;\ - mov ax,es:[bx];\ - xchg al,ah;\ - mov v,ax;\ - add word ptr pcp,2 } - -#define GET_PC(v) asm {\ - mov bx,word ptr pcp+2;\ - sub bx,word ptr zmp+2;\ - mov ax,bx;\ - mov cl,4;\ - shl bx,cl;\ - mov cl,12;\ - shr ax,cl;\ - add bx,word ptr pcp;\ - adc al,0;\ - sub bx,word ptr zmp;\ - sbb al,0;\ - mov word ptr v,bx;\ - mov word ptr v+2,ax } - -#define SET_PC(v) asm {\ - mov bx,word ptr zmp;\ - add bx,word ptr v;\ - mov al,bh;\ - mov bh,0;\ - mov ah,0;\ - adc ah,byte ptr v+2;\ - mov cl,4;\ - shl ax,cl;\ - add ax,word ptr zmp+2;\ - mov word ptr pcp,bx;\ - mov word ptr pcp+2,ax } - -#endif +/* A bunch of x86 assembly code previously appeared here. */ -#if !defined (AMIGA) && !defined (__MSDOS__) +#if !defined (AMIGA) && !defined (MSDOS_16BIT) extern zbyte *pcp; extern zbyte *zmp; @@ -345,6 +281,7 @@ extern zbyte *zmp; #endif + /*** Story file header data ***/ extern zbyte h_version; @@ -388,7 +325,7 @@ extern zword hx_unicode_table; /*** Various data ***/ -extern const char *story_name; +extern char *story_name; extern enum story story_id; extern long story_size; @@ -396,9 +333,10 @@ extern long story_size; extern zword stack[STACK_SIZE]; extern zword *sp; extern zword *fp; +extern zword frame_count; extern zword zargs[8]; -extern zargc; +extern int zargc; extern bool ostream_screen; extern bool ostream_script; @@ -407,29 +345,32 @@ extern bool ostream_record; extern bool istream_replay; extern bool message; -extern cwin; -extern mwin; +extern int cwin; +extern int mwin; -extern mouse_x; -extern mouse_y; +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 option_attribute_assignment; -extern option_attribute_testing; -extern option_object_locating; -extern option_object_movement; -extern option_context_lines; -extern option_left_margin; -extern option_right_margin; -extern option_ignore_errors; -extern option_piracy; -extern option_undo_slots; -extern option_expand_abbreviations; -extern option_script_cols; +extern int option_attribute_assignment; +extern int option_attribute_testing; +extern int option_object_locating; +extern int option_object_movement; +extern int option_context_lines; +extern int option_left_margin; +extern int option_right_margin; +extern int option_ignore_errors; +extern int option_piracy; +extern int option_undo_slots; +extern int option_expand_abbreviations; +extern int option_script_cols; +extern int option_save_quetzal; +extern int option_sound; /* dg */ +extern char *option_zcode_path; /* dg */ extern long reserve_mem; @@ -546,6 +487,70 @@ 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 */ +#define ERR_DEFAULT_REPORT_MODE ERR_REPORT_NEVER + + /*** Various global functions ***/ zchar translate_from_zscii (zbyte); @@ -561,8 +566,6 @@ void print_string (const char *); void stream_mssg_on (void); void stream_mssg_off (void); -void runtime_error (const char *); - void ret (zword); void store (zword); void branch (bool); diff --git a/getopt.c b/getopt.c index c1e40e1..415fcde 100644 --- a/getopt.c +++ b/getopt.c @@ -3,12 +3,15 @@ * * 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__ +#ifndef MSDOS_16BIT #define cdecl #endif @@ -19,7 +22,7 @@ const char *optarg = NULL; int cdecl getopt (int argc, char *argv[], const char *options) { - static pos = 1; + static int pos = 1; const char *p; @@ -36,14 +39,14 @@ int cdecl getopt (int argc, char *argv[], const char *options) if (optopt == ':' || p == NULL) { - fputs ("illegal option -- ", stderr); + fputs ("illegal option -- ", stdout); goto error; - } else if (p[1] == ':') + } else if (p[1] == ':') { if (optind >= argc) { - fputs ("option requires an argument -- ", stderr); + fputs ("option requires an argument -- ", stdout); goto error; } else { @@ -56,13 +59,13 @@ int cdecl getopt (int argc, char *argv[], const char *options) pos = 1; optind++; } - + } return optopt; error: - fputc (optopt, stderr); - fputc ('\n', stderr); + fputc (optopt, stdout); + fputc ('\n', stdout); return '?'; diff --git a/getopt.h b/getopt.h new file mode 100644 index 0000000..ad6bd56 --- /dev/null +++ b/getopt.h @@ -0,0 +1,131 @@ +/* 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 */ diff --git a/hotkey.c b/hotkey.c index 56bc413..cbb2483 100644 --- a/hotkey.c +++ b/hotkey.c @@ -1,8 +1,21 @@ -/* - * hotkey.c +/* hotkey.c - Hot key functions + * Copyright (c) 1995-1997 Stefan Jokisch + * + * This file is part of Frotz. * - * Hot key functions + * 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" @@ -217,6 +230,8 @@ bool handle_hot_key (zchar key) bool aborting; + aborting = FALSE; + print_string ("\nHot key -- "); switch (key) { diff --git a/input.c b/input.c index 0642aef..4169ca5 100644 --- a/input.c +++ b/input.c @@ -1,8 +1,21 @@ -/* - * input.c +/* 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. * - * High level input functions + * 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" diff --git a/main.c b/main.c index 9a8e9ba..c09f1e7 100644 --- a/main.c +++ b/main.c @@ -1,21 +1,34 @@ -/* - * main.c +/* 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 V2.32 main function + * 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 * - * Frotz is freeware. It may be used and distributed freely provided - * no commercial profit is involved. (c) 1995-1997 Stefan Jokisch - * */ #include "frotz.h" -#ifndef __MSDOS__ +#ifndef MSDOS_16BIT #define cdecl #endif @@ -26,7 +39,7 @@ extern void reset_memory (void); /* Story file name, id number and size */ -const char *story_name = 0; +char *story_name = 0; enum story story_id = UNKNOWN; long story_size = 0; @@ -77,6 +90,7 @@ zword hx_unicode_table = 0; zword stack[STACK_SIZE]; zword *sp = 0; zword *fp = 0; +zword frame_count = 0; /* IO streams */ @@ -116,26 +130,15 @@ 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; -/* - * runtime_error - * - * An error has occured. Ignore it or pass it to os_fatal. - * - */ - -void runtime_error (const char *s) -{ - - if (!option_ignore_errors) - { flush_buffer (); os_fatal (s); } - -}/* runtime_error */ - /* * z_piracy, branch if the story file is a legal copy. * @@ -162,6 +165,8 @@ int cdecl main (int argc, char *argv[]) os_process_arguments (argc, argv); + init_err (); + init_memory (); os_init_screen (); diff --git a/math.c b/math.c index 20ae247..5ff5163 100644 --- a/math.c +++ b/math.c @@ -1,8 +1,21 @@ -/* - * math.c +/* 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. * - * Arithmetic, compare and logical opcodes + * 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" @@ -67,7 +80,7 @@ void z_div (void) { if (zargs[1] == 0) - runtime_error ("Division by zero"); + runtime_error (ERR_DIV_ZERO); store ((zword) ((short) zargs[0] / (short) zargs[1])); @@ -167,7 +180,7 @@ void z_mod (void) { if (zargs[1] == 0) - runtime_error ("Division by zero"); + runtime_error (ERR_DIV_ZERO); store ((zword) ((short) zargs[0] % (short) zargs[1])); diff --git a/object.c b/object.c index c6c79e5..96ac032 100644 --- a/object.c +++ b/object.c @@ -1,8 +1,21 @@ -/* - * object.c +/* object.c - Object manipulation opcodes + * Copyright (c) 1995-1997 Stefan Jokisch + * + * This file is part of Frotz. * - * Object manipulation opcodes + * 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" @@ -34,7 +47,7 @@ static zword object_address (zword obj) /* Check object number */ if (obj > ((h_version <= V3) ? 255 : MAX_OBJECT)) - runtime_error ("Illegal object"); + runtime_error (ERR_ILL_OBJ); /* Return object address */ @@ -149,6 +162,11 @@ static void unlink_object (zword object) 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) { @@ -251,7 +269,7 @@ void z_clear_attr (void) return; if (zargs[1] > ((h_version <= V3) ? 31 : 47)) - runtime_error ("Illegal attribute"); + runtime_error (ERR_ILL_ATTR); /* If we are monitoring attribute assignment display a short note */ @@ -264,6 +282,11 @@ void z_clear_attr (void) 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; @@ -299,6 +322,12 @@ void z_jin (void) 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) { @@ -351,6 +380,13 @@ void z_get_child (void) 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) { @@ -399,6 +435,12 @@ void z_get_next_prop (void) 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; @@ -419,7 +461,7 @@ void z_get_next_prop (void) /* Exit if the property does not exist */ if ((value & mask) != zargs[1]) - runtime_error ("No such property"); + runtime_error (ERR_NO_PROP); } @@ -450,6 +492,12 @@ void z_get_parent (void) 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) { @@ -498,6 +546,12 @@ void z_get_prop (void) 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; @@ -521,7 +575,7 @@ void z_get_prop (void) prop_addr++; - if (h_version <= V3 && !(value & 0xe0) || h_version >= V4 && !(value & 0xc0)) { + if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) { LOW_BYTE (prop_addr, bprop_val) wprop_val = bprop_val; @@ -557,6 +611,12 @@ void z_get_prop_addr (void) 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; } @@ -638,6 +698,13 @@ 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) { @@ -698,6 +765,16 @@ void z_insert_obj (void) 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); @@ -752,6 +829,11 @@ void z_put_prop (void) 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; @@ -772,13 +854,13 @@ void z_put_prop (void) /* Exit if the property does not exist */ if ((value & mask) != zargs[1]) - runtime_error ("No such property"); + 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)) { + if ((h_version <= V3 && !(value & 0xe0)) || (h_version >= V4 && !(value & 0xc0))) { zbyte v = zargs[2]; SET_BYTE (prop_addr, v) } else { @@ -831,7 +913,7 @@ void z_set_attr (void) return; if (zargs[1] > ((h_version <= V3) ? 31 : 47)) - runtime_error ("Illegal attribute"); + runtime_error (ERR_ILL_ATTR); /* If we are monitoring attribute assignment display a short note */ @@ -844,6 +926,11 @@ void z_set_attr (void) 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; @@ -876,7 +963,7 @@ void z_test_attr (void) zbyte value; if (zargs[1] > ((h_version <= V3) ? 31 : 47)) - runtime_error ("Illegal attribute"); + runtime_error (ERR_ILL_ATTR); /* If we are monitoring attribute testing display a short note */ @@ -889,6 +976,12 @@ void z_test_attr (void) 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; diff --git a/process.c b/process.c index dd42baa..5152bcd 100644 --- a/process.c +++ b/process.c @@ -1,16 +1,34 @@ -/* - * process.c +/* 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. * - * Interpreter loop and program control + * 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 finished = 0; +static int finished = 0; static void __extended__ (void); static void __illegal__ (void); @@ -270,6 +288,11 @@ void interpret (void) } +#if defined(DJGPP) && defined(SOUND_SUPPORT) + if (end_of_sound_flag) + end_of_sound (); +#endif + } while (finished == 0); finished--; @@ -294,16 +317,17 @@ void call (zword routine, int argc, zword *args, int ct) int i; if (sp - stack < 4) - runtime_error ("Stack overflow"); + runtime_error (ERR_STK_OVF); GET_PC (pc) - *--sp = (zword) (pc >> 9); /* for historical reasons */ - *--sp = (zword) (pc & 0x1ff); /* Frotz keeps its stack */ - *--sp = (zword) (fp - stack - 1); /* format compatible with */ - *--sp = (zword) (argc | (ct << 8)); /* Mark Howell's Zip */ + *--sp = (zword) (pc >> 9); + *--sp = (zword) (pc & 0x1ff); + *--sp = (zword) (fp - stack - 1); + *--sp = (zword) (argc | (ct << (option_save_quetzal ? 12 : 8))); fp = sp; + frame_count++; /* Calculate byte address of routine */ @@ -317,7 +341,7 @@ void call (zword routine, int argc, zword *args, int ct) pc = (long) routine << 3; if (pc >= story_size) - runtime_error ("Call to illegal address"); + runtime_error (ERR_ILL_CALL_ADDR); SET_PC (pc) @@ -326,9 +350,12 @@ void call (zword routine, int argc, zword *args, int ct) CODE_BYTE (count) if (count > 15) - runtime_error ("Call to non-routine"); + runtime_error (ERR_CALL_NON_RTN); if (sp - stack < count) - runtime_error ("Stack overflow"); + runtime_error (ERR_STK_OVF); + + if (option_save_quetzal) + fp[0] |= (zword) count << 8; /* Save local var count for Quetzal. */ value = 0; @@ -364,11 +391,12 @@ void ret (zword value) int ct; if (sp > fp) - runtime_error ("Stack underflow"); + runtime_error (ERR_STK_UNDF); sp = fp; - ct = *sp++ >> 8; + ct = *sp++ >> (option_save_quetzal ? 12 : 8); + frame_count--; fp = stack + 1 + *sp++; pc = *sp++; pc = ((long) *sp++ << 9) | pc; @@ -430,7 +458,7 @@ void branch (bool flag) } else offset = off1; /* it's a short branch */ - if (specifier & 0x80) + if (specifier & 0x80) { if (offset > 1) { /* normal branch */ @@ -439,6 +467,7 @@ void branch (bool flag) SET_PC (pc) } else ret (offset); /* special case, return 0 or 1 */ + } }/* branch */ @@ -546,7 +575,7 @@ static void __extended__ (void) static void __illegal__ (void) { - runtime_error ("Illegal opcode"); + runtime_error (ERR_ILL_OPCODE); }/* __illegal__ */ @@ -560,7 +589,7 @@ static void __illegal__ (void) void z_catch (void) { - store ((zword) (fp - stack)); + store (option_save_quetzal ? frame_count : (zword) (fp - stack)); }/* z_catch */ @@ -575,10 +604,19 @@ void z_catch (void) void z_throw (void) { - if (zargs[1] > STACK_SIZE) - runtime_error ("Bad stack frame"); + if (option_save_quetzal) { + if (zargs[1] > frame_count) + runtime_error (ERR_BAD_FRAME); - fp = stack + zargs[1]; + /* 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]); @@ -655,7 +693,7 @@ void z_jump (void) pc += (short) zargs[0] - 2; if (pc >= story_size) - runtime_error ("Jump to illegal address"); + runtime_error (ERR_ILL_JUMP_ADDR); SET_PC (pc) diff --git a/quetzal.c b/quetzal.c new file mode 100644 index 0000000..f7ccacd --- /dev/null +++ b/quetzal.c @@ -0,0 +1,562 @@ +/* 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 Text and keys (except for file names) are now presented - using the new "zchar" type. This is a 1-byte value that - contains an ISO Latin-1 character or a special Infocom - character like code $09 (paragraph indentation). See - FROTZ.H for possible character codes. Some Infocom codes - have changed, too, since they collided with ISO Latin-1 - codes. I strongly recommend using the constants defined - in FROTZ.H. - - If you want to support different character sets, look at - - * zchar definition (FROTZ.H), - * z_print_unicode (TEXT.C), - * z_check_unicode (TEXT.C), - * translate_from_zscii (TEXT.C), - * translate_to_zscii (TEXT.C), - * conversion to lower case in z_read (INPUT.C), - * script_char (FILES.C). - - If you can't read accented characters from the keyboard, - make sure z_check_unicode stores 1 for codes from $0a to - $ff. If you can't write accented characters, prints some - suitable ASCII representation instead. - --> There are some differences between Amiga and DOS picture - files, and an interpreter must take some extra care to - combine Amiga story files with DOS picture files. So far - the Amiga and DOS front-ends contained the necessary - code to make this work. Since Frotz 2.32 this problem is - dealt with in SCREEN.C, and interfaces no longer have to - worry about this. - --> os_cursor_on and os_cursor_off are no more. Instead, an - additional argument tells os_read_key to make the cursor - visible or not. - - (I believe a game would not want to turn off the cursor - for reading a string.) - --> os_wait_sample is no longer needed! - - SOUND.C uses the end_of_sound call to solve all problems - with 'The Lurking Horror' more effectively than before. - --> os_start_sample should now ignore the play-once-or-loop- - forever information in the sound files and rely on its - arguments instead. The necessary information for 'The - Lurking Horror' has been wired into SOUND.C. This should - make it easier to use a more common sound format in the - future. - --> 'Journey' asked for font #4 (fixed font) on most systems - since it simply expected the interpreter to be unable to - print a proportional font. This unpleasant behaviour is - now overwritten by Frotz. - --> Many extern declarations in FROTZ.H have been removed. In - particular, this includes - - * end_of_sound, - * restart_header, - * resize_screen, - * completion, - * is_terminator, - * read_yes_or_no, - * read_string. - - It's fine to call these functions from your front-end, - though. Just add their prototypes to your code. - --> For different reasons, several front-ends felt the need - to interfere with the process of restarting a game. - - There is now an interface function os_restart_game that - gives the front-end a chance to do its business while - the game restarts. The function is called several times - during the process, its sole argument indicating the - current "stage" of the restart: - - * RESTART_BEGIN -- restart has just begun - * RESTART_WPROP_SET -- window properties have been set - * RESTART_END -- restart completed - - os_restart_game is also useful to display the 'Beyond - Zork' title picture that is now available in MG1 format. - - - -Final note: The FONT.DAT file in this distribution must be -appended to the DOS executable -- sorry about this! - - diff --git a/redirect.c b/redirect.c index fc92b41..d81776d 100644 --- a/redirect.c +++ b/redirect.c @@ -1,8 +1,21 @@ -/* - * redirect.c +/* 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. * - * Output redirection to Z-machine memory + * 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" @@ -11,7 +24,7 @@ extern zword get_max_width (zword); -static depth = -1; +static int depth = -1; static struct { zword xsize; @@ -46,7 +59,7 @@ void memory_open (zword table, zword xsize, bool buffering) ostream_memory = TRUE; - } else runtime_error ("Nesting stream #3 too deep"); + } else runtime_error (ERR_STR3_NESTING); }/* memory_open */ diff --git a/screen.c b/screen.c index 851451e..85e97d0 100644 --- a/screen.c +++ b/screen.c @@ -1,8 +1,21 @@ -/* - * screen.c +/* screen.c - Generic screen manipulation + * Copyright (c) 1995-1997 Stefan Jokisch + * + * This file is part of Frotz. * - * Generic screen manipulation + * 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" @@ -26,15 +39,15 @@ static struct { { UNKNOWN, 0, 0, 0 } }; -static font_height = 1; -static font_width = 1; +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 input_window = 0; +static int input_window = 0; static struct { zword y_pos; @@ -55,6 +68,7 @@ static struct { zword line_count; } wp[8], *cwp; + /* * winarg0 * @@ -70,7 +84,7 @@ static zword winarg0 (void) return cwin; if (zargs[0] >= ((h_version == V6) ? 8 : 2)) - runtime_error ("Illegal window"); + runtime_error (ERR_ILL_WIN); return zargs[0]; @@ -92,7 +106,7 @@ static zword winarg2 (void) return cwin; if (zargs[2] >= 8) - runtime_error ("Illegal window"); + runtime_error (ERR_ILL_WIN); return zargs[2]; @@ -181,7 +195,7 @@ zword get_max_width (zword win) if (h_version == V6) { if (win >= 8) - runtime_error ("Illegal window"); + runtime_error (ERR_ILL_WIN); return wp[win].x_size - wp[win].left - wp[win].right; @@ -587,7 +601,7 @@ static void set_window (zword win) * */ -static void erase_window (zword win) +void erase_window (zword win) { zword y = wp[win].y_pos; zword x = wp[win].x_pos; @@ -682,7 +696,7 @@ static void erase_screen (zword win) }/* erase_screen */ -#ifdef AMIGA +/* #ifdef AMIGA */ /* * resize_screen @@ -706,7 +720,7 @@ void resize_screen (void) }/* resize_screen */ -#endif +/* #endif */ /* * restart_screen @@ -1083,7 +1097,7 @@ void z_get_wind_prop (void) flush_buffer (); if (zargs[1] >= 16) - runtime_error ("Illegal window property"); + runtime_error (ERR_ILL_WIN_PROP); store (((zword *) (wp + winarg0 ())) [zargs[1]]); @@ -1146,7 +1160,7 @@ void z_picture_data (void) for (i = 0; mapper[i].story_id != UNKNOWN; i++) - if (story_id == mapper[i].story_id) + if (story_id == mapper[i].story_id) { if (pic == mapper[i].pic) { @@ -1160,6 +1174,7 @@ void z_picture_data (void) } else if (pic == mapper[i].pic1 || pic == mapper[i].pic2) avail = FALSE; + } storew ((zword) (table + 0), (zword) (height)); storew ((zword) (table + 2), (zword) (width)); @@ -1259,7 +1274,7 @@ void z_put_wind_prop (void) flush_buffer (); if (zargs[1] >= 16) - runtime_error ("Illegal window property"); + runtime_error (ERR_ILL_WIN_PROP); ((zword *) (wp + winarg0 ())) [zargs[1]] = zargs[2]; @@ -1450,6 +1465,10 @@ void z_set_cursor (void) /* 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; diff --git a/sound.c b/sound.c index bfaf156..df5925b 100644 --- a/sound.c +++ b/sound.c @@ -1,12 +1,29 @@ -/* - * sound.c +/* 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. * - * Sound effect function + * 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 @@ -16,8 +33,8 @@ extern int direct_call (zword); static zword routine = 0; -static next_sample = 0; -static next_volume = 0; +static int next_sample = 0; +static int next_volume = 0; static bool locked = FALSE; static bool playing = FALSE; @@ -81,6 +98,10 @@ static void start_next_sample (void) void end_of_sound (void) { +#if defined(DJGPP) && defined(SOUND_SUPPORT) + end_of_sound_flag = 0; +#endif + playing = FALSE; if (!locked) { @@ -113,7 +134,7 @@ void z_sound_effect (void) zword effect = zargs[1]; zword volume = zargs[2]; - if (number >= 3) { + if (number >= 3 || number == 0) { locked = TRUE; diff --git a/stream.c b/stream.c index c0c6e3b..4ccb444 100644 --- a/stream.c +++ b/stream.c @@ -1,8 +1,21 @@ -/* - * stream.c +/* 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. * - * IO stream implementation + * 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" diff --git a/table.c b/table.c index 9999b67..eb3a163 100644 --- a/table.c +++ b/table.c @@ -1,8 +1,21 @@ -/* - * table.c +/* 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. * - * Table handling opcodes + * 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" diff --git a/text.c b/text.c index c8c3dbe..611e2d2 100644 --- a/text.c +++ b/text.c @@ -1,8 +1,21 @@ -/* - * text.c +/* text.c - Text manipulation functions + * Copyright (c) 1995-1997 Stefan Jokisch + * + * This file is part of Frotz. * - * Text manipulation functions + * 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" @@ -45,7 +58,7 @@ zchar translate_from_zscii (zbyte c) if (c == 0xfe) return ZC_SINGLE_CLICK; - if (c >= 0x9b && story_id != BEYOND_ZORK) + if (c >= 0x9b && story_id != BEYOND_ZORK) { if (hx_unicode_table != 0) { /* game has its own Unicode table */ @@ -74,6 +87,7 @@ zchar translate_from_zscii (zbyte c) return zscii_to_latin1[c - 0x9b]; } else return '?'; + } return c; @@ -97,7 +111,7 @@ zbyte translate_to_zscii (zchar c) if (c == ZC_MENU_CLICK) return 0xfc; - if (c >= ZC_LATIN1_MIN) + if (c >= ZC_LATIN1_MIN) { if (hx_unicode_table != 0) { /* game has its own Unicode table */ @@ -129,6 +143,7 @@ zbyte translate_to_zscii (zchar c) return '?'; } + } return c; @@ -364,6 +379,9 @@ static void decode_text (enum string_type st, zword addr) 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) @@ -382,7 +400,7 @@ static void decode_text (enum string_type st, zword addr) byte_addr = (long) addr << 3; if (byte_addr >= story_size) - runtime_error ("Print at illegal address"); + runtime_error (ERR_ILL_PRINT_ADDR); } @@ -890,6 +908,8 @@ void tokenise_line (zword text, zword token, zword dct, bool flag) zbyte length; zbyte c; + length = 0; /* makes compilers shut up */ + /* Use standard dictionary if the given dictionary is zero */ if (dct == 0) diff --git a/ux_sample.c b/ux_audio_none.c similarity index 63% rename from ux_sample.c rename to ux_audio_none.c index 1fa1871..15675fb 100644 --- a/ux_sample.c +++ b/ux_audio_none.c @@ -1,8 +1,21 @@ /* - * ux_sample.c + * ux_audio_none.c - Unix interface, sound support * - * 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 @@ -16,6 +29,8 @@ #include "frotz.h" #include "ux_frotz.h" +#ifdef NO_SOUND /* don't compile this unless we're using no audio */ + /* * os_beep * @@ -105,3 +120,5 @@ void os_wait_sample (void) /* Not implemented */ }/* os_wait_sample */ + +#endif /* NO_SOUND */ diff --git a/ux_audio_oss.c b/ux_audio_oss.c new file mode 100644 index 0000000..023613e --- /dev/null +++ b/ux_audio_oss.c @@ -0,0 +1,363 @@ +/* + * 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 "frotz.h" +#include "ux_frotz.h" + +#ifdef OSS_SOUND /* don't compile this if not using OSS */ + +#include +#include +#include +#include +#include +#include +#include "soundcard.h" +#include +#include +#include + + +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; + + dsp_fd = open("/dev/dsp", O_WRONLY); + if (dsp_fd < 0) { + perror("/dev/dsp"); + _exit(1); + } + 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("write on /dev/dsp"); + 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 as well + * as checking to see if Frotz was started in quiet mode. + */ + + 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; + + 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 = malloc(sound_length); + if (! sound_buffer) { + perror("malloc"); + return; + } + + if (sound_length < 0 || + fread(sound_buffer, 1, sound_length, samples) < sound_length) { + if (feof(samples)) + fprintf(stderr, "%s: premature EOF\n", filename); + else { + errno = ferror(samples); + perror(filename); + } + free(sound_buffer); + sound_buffer = NULL; + } + + current_num = number; +}/* 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) +{ + sigset_t sigchld_mask; + struct sigaction sa; + + os_prepare_sample(number); + if (! sound_buffer) + return; + os_stop_sample(); + + 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 (void) +{ + stop_player(SIGTERM); +}/* os_stop_sample */ + +/* + * os_finish_with_sample + * + * Remove the current sample from memory (if any). + * + */ + +void os_finish_with_sample (void) +{ + 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 */ diff --git a/ux_frotz.h b/ux_frotz.h index dc23873..41e584d 100644 --- a/ux_frotz.h +++ b/ux_frotz.h @@ -1,10 +1,92 @@ /* * ux_frotz.h * - * Unix interface, declarations + * Unix interface, declarations, definitions, and defaults * */ -extern int current_text_style; /* ux_init */ -extern char unix_plain_ascii; /* ux_init */ -extern int current_color; /* ux_text */ +#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 int current_text_style; /* ux_init */ +extern char unix_plain_ascii; /* ux_init */ +extern int current_color; /* ux_text */ +extern bool color_enabled; /* ux_text */ +extern bool unix_init_pictures(); /* ux_pic */ +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 */ + + +/*** Functions specific to the Unix port of Frotz ***/ + +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 diff --git a/ux_init.c b/ux_init.c index b2f528e..a7dcfbe 100644 --- a/ux_init.c +++ b/ux_init.c @@ -1,8 +1,23 @@ /* - * ux_init.c + * ux_init.c - Unix interface, initialisation + * Galen Hazelwood + * David Griffith * - * Unix interface, initialisation + * 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 @@ -13,18 +28,14 @@ #include -/* Prototypes for getopt */ -#ifdef USE_UNISTD_H #include -#elif USE_GETOPT_H -#include -#else -#ifndef USE_NOTHING -extern int getopt(int, char **, char *); -extern int optind, opterr; -extern char *optarg; -#endif -#endif +#include + +#include "getopt.h" + + +/* We will use our own private getopt functions. */ + #ifdef USE_NCURSES_H #include @@ -32,30 +43,33 @@ extern char *optarg; #include #endif +#include + #include "frotz.h" #include "ux_frotz.h" #define INFORMATION "\ \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\ +FROTZ V2.40 - 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\ \n\ - -a watch attribute setting \t -l # left margin\n\ - -A watch attribute testing \t -o watch object movement\n\ - -b # background colour \t -O watch object locating\n\ - \t -p plain ASCII output only\n\ - \t -P alter piracy opcode\n\ - -c # context lines \t -r # right margin\n\ - -d disable color \t -s # random number seed value\n\ - \t -S # transscript width\n\ - -f # foreground colour \t -t set Tandy bit\n\ - \t -u # slots for multiple undo\n\ - -h # screen height \t -w # screen width\n\ - -i ignore runtime errors \t -x expand abbreviations g/x/z\n" + -a watch attribute setting \t -p plain ASCII output only\n\ + -A watch attribute testing \t -P alter piracy opcode\n\ + -b # background color \t -r # right margin\n\ + -c # context lines \t -q quiet (disable sound effects\n\ + -d disable color \t -Q use old-style save format\n\ + -e enable sound \t -s # random number seed value\n\ + -f # foreground colour \t -S # transcript width\n\ + -F force color mode \t -t set Tandy bit\n\ + -h # screen height \t -u # slots for multiple undo\n\ + -l # left margin \t -w # screen width\n\ + -o watch object movement \t -x expand abbreviations g/x/z\n\ + -O watch object locating" static int user_disable_color = 0; +static int user_force_color = 0; static int user_foreground_color = -1; static int user_background_color = -1; static int user_screen_width = -1; @@ -71,6 +85,10 @@ int current_text_style = 0; /* Since I can't use attr_get, which global variable. */ char unix_plain_ascii = 0; /* true if user wants to disable latin-1 */ + +char stripped_story_name[FILENAME_MAX+1]; +char semi_stripped_story_name[FILENAME_MAX+1]; + /* * os_fatal * @@ -82,13 +100,14 @@ void os_fatal (const char *s) { if (curses_active) { - os_display_string("\n\n"); + /* Solaris 2.6's cc complains if the below cast is missing */ + os_display_string((zchar *)"\n\n"); beep(); os_set_text_style(BOLDFACE_STYLE); - os_display_string("Fatal error: "); + os_display_string((zchar *)"Fatal error: "); os_set_text_style(0); - os_display_string(s); - os_display_string("\n"); + os_display_string((zchar *)s); + os_display_string((zchar *)"\n"); os_reset_screen(); exit(1); } @@ -127,20 +146,62 @@ extern char auxilary_name[]; * * The global pointer "story_name" is set to the story file name. * + * */ void os_process_arguments (int argc, char *argv[]) { int c, i; - char stripped_story_name[MAX_FILE_NAME]; + char *p = NULL; - /* Parse the options */ + char *home; + char configfile[FILENAME_MAX + 1]; - do { + 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 */ + } - c = getopt(argc, argv, "aAb:c:df:h:il:oOpPr:s:S:tu:w:x"); + /* Parse the options */ + do { + c = getopt(argc, argv, "aAb:c:def:Fh:il:oOpPQqr:s:S:tu:w:xZ:"); switch(c) { case 'a': option_attribute_assignment = 1; break; case 'A': option_attribute_testing = 1; break; @@ -151,18 +212,24 @@ void os_process_arguments (int argc, char *argv[]) break; case 'c': option_context_lines = atoi(optarg); break; case 'd': user_disable_color = 1; break; + case 'e': option_sound = 1; break; case 'f': user_foreground_color = atoi(optarg); if ((user_foreground_color < 2) || (user_foreground_color > 9)) user_foreground_color = -1; break; + case 'F': user_force_color = 1; + user_disable_color = 0; + break; case 'h': user_screen_height = atoi(optarg); break; - case 'i': option_ignore_errors = 1; break; +/* case 'i': option_ignore_errors = 1; break; */ case 'l': option_left_margin = atoi(optarg); break; case 'o': option_object_movement = 1; break; case 'O': option_object_locating = 1; break; case 'p': unix_plain_ascii = 1; break; case 'P': option_piracy = 1; break; + case 'q': option_sound = 0; break; + case 'Q': option_save_quetzal = 0; break; case 'r': option_right_margin = atoi(optarg); break; case 's': user_random_seed = atoi(optarg); break; case 'S': option_script_cols = atoi(optarg); break; @@ -170,26 +237,46 @@ void os_process_arguments (int argc, char *argv[]) case 'u': option_undo_slots = atoi(optarg); break; case 'w': user_screen_width = atoi(optarg); break; case 'x': option_expand_abbreviations = 1; break; + case 'Z': err_report_mode = atoi(optarg); + if ((err_report_mode < ERR_REPORT_NEVER) || + (err_report_mode > ERR_REPORT_FATAL)) + err_report_mode = ERR_DEFAULT_REPORT_MODE; + break; } } while (c != EOF); if (optind != argc - 1) { 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 = argv[optind]; + story_name = malloc(FILENAME_MAX + 1); + strcpy(story_name, argv[optind]); /* Strip path off the story file name */ - p = story_name; + p = (char *)story_name; for (i = 0; story_name[i] != 0; i++) if (story_name[i] == '/') - p = story_name + i + 1; + 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]; @@ -262,28 +349,22 @@ void os_init_screen (void) if (h_version >= V4) h_config |= CONFIG_BOLDFACE | CONFIG_EMPHASIS | CONFIG_FIXED | CONFIG_TIMEDINPUT; -#ifndef SOUND_SUPPORT if (h_version >= V5) - h_flags &= ~(GRAPHICS_FLAG | SOUND_FLAG | MOUSE_FLAG | MENU_FLAG); + h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG); - if (h_version == V3) - h_flags &= ~OLD_SOUND_FLAG; -#else +#ifdef NO_SOUND if (h_version >= V5) - h_flags &= ~(GRAPHICS_FLAG | MOUSE_FLAG | MENU_FLAG); + h_flags &= ~SOUND_FLAG; - if ((h_version >= 5) && (h_flags & SOUND_FLAG)) { - if (unix_init_sound()) + if (h_version == V3) + h_flags &= ~OLD_SOUND_FLAG; +#else + if ((h_version >= 5) && (h_flags & SOUND_FLAG)) h_flags |= SOUND_FLAG; - else - h_flags &= ~SOUND_FLAG; - } - if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG)) { - if (unix_init_sound()) + + if ((h_version == 3) && (h_flags & OLD_SOUND_FLAG)) h_flags |= OLD_SOUND_FLAG; - else - h_flags &= ~OLD_SOUND_FLAG; - } + #endif if (h_version >= V5 && (h_flags & UNDO_FLAG)) @@ -303,33 +384,57 @@ void os_init_screen (void) h_font_width = 1; h_font_height = 1; - h_interpreter_number = INTERP_DEC_20; /* We is a DECsystem-20 :) */ + /* 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 - if (has_colors() && !user_disable_color) { + /* 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. + */ + color_enabled = (has_colors() + && !user_disable_color + && (((h_version >= 5) && (h_flags & COLOUR_FLAG)) + || (user_foreground_color != -1) + || (user_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 (user_force_color) + color_enabled = TRUE; + + if (color_enabled) { h_config |= CONFIG_COLOUR; h_flags |= COLOUR_FLAG; /* FIXME: beyond zork handling? */ start_color(); - if (user_foreground_color != -1) - h_default_foreground = user_foreground_color; - else - h_default_foreground = WHITE_COLOUR; - if (user_background_color != -1) - h_default_background = user_background_color; - else - h_default_background = BLUE_COLOUR; - os_set_colour(h_default_foreground, h_default_background); - bkgdset(current_color); - erase(); - /*os_erase_area(1, 1, h_screen_rows, h_screen_cols);*/ - } - else - if (h_flags & COLOUR_FLAG) h_flags &= ~COLOUR_FLAG; + h_default_foreground = + (user_foreground_color == -1) + ? FOREGROUND_DEF : user_foreground_color; + h_default_background = + (user_background_color ==-1) + ? BACKGROUND_DEF : user_background_color; + } else #endif - - refresh(); - + { + /* 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 */ /* @@ -383,3 +488,375 @@ int os_random_seed (void) else return user_random_seed; }/* os_random_seed */ + + +/* + * os_path_open + * + * Open a file in the current directory. If this fails, then search the + * directories in the ZCODE_PATH 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) { + option_attribute_assignment = getbool(value); + } + else if (strcmp(varname, "attrib_test") == 0) { + option_attribute_testing = getbool(value); + } + +/* else if (strcmp(varname, "error_halt") == 0) { + option_ignore_errors = !getbool(value); + } +*/ + else if (strcmp(varname, "color") == 0) { + user_disable_color = !getbool(value); + } + else if (strcmp(varname, "colour") == 0) { + user_disable_color = !getbool(value); + } + else if (strcmp(varname, "force_color") == 0) { + user_force_color = getbool(value); + } + else if (strcmp(varname, "obj_move") == 0) { + option_object_movement = getbool(value); + } + else if (strcmp(varname, "obj_loc") == 0) { + option_object_locating = getbool(value); + } + else if (strcmp(varname, "piracy") == 0) { + option_piracy = getbool(value); + } + else if (strcmp(varname, "ascii") == 0) { + unix_plain_ascii = getbool(value); + } + else if (strcmp(varname, "sound") == 0) { + option_sound = getbool(value); + } + else if (strcmp(varname, "quetzal") == 0) { + option_save_quetzal = getbool(value); + } + else if (strcmp(varname, "tandy") == 0) { + user_tandy_bit = getbool(value); + } + else if (strcmp(varname, "expand_abb") == 0) { + option_expand_abbreviations = getbool(value); + } + + /* now for stringtype yet still numeric variables */ + else if (strcmp(varname, "background") == 0) { + user_background_color = getcolor(value); + } + else if (strcmp(varname, "foreground") == 0) { + user_foreground_color = getcolor(value); + } + else if (strcmp(varname, "context_lines") == 0) { + option_context_lines = atoi(value); + } + else if (strcmp(varname, "screen_height") == 0) { + user_screen_height = atoi(value); + } + else if (strcmp(varname, "left_margin") == 0) { + option_left_margin = atoi(value); + } + else if (strcmp(varname, "right_margin") == 0) { + option_right_margin = atoi(value); + } + else if (strcmp(varname, "randseed") == 0) { + user_random_seed = atoi(value); + } + else if (strcmp(varname, "script_width") == 0) { + option_script_cols = atoi(value); + } + else if (strcmp(varname, "undo_slots") == 0) { + option_undo_slots = atoi(value); + } + else if (strcmp(varname, "screen_width") == 0) { + user_screen_width = atoi(value); + } + /* default is set in main() by call to init_err() */ + else if (strcmp(varname, "errormode") == 0) { + 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, I've left this function +currently does nothing. +*/ + +/* + signal(sig, SIG_DFL); + signal(sig, SIG_IGN); + + + signal(SIGWINCH, sig_winch_handler); +*/ +} + +void redraw(void) +{ + /* not implemented */ +} + diff --git a/ux_input.c b/ux_input.c index 3676162..27a94a3 100644 --- a/ux_input.c +++ b/ux_input.c @@ -1,15 +1,30 @@ /* - * ux_input.c + * ux_input.c - Unix interface, input functions * - * 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 @@ -24,10 +39,21 @@ 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 short history_pointer = 0; /* Pointer to next available slot */ -static short history_frame = 0; /* Pointer to what the user's looking at */ +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 *); @@ -56,107 +82,160 @@ static void unix_set_global_timeout(int timeout) } } +/* 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 unless the global timeout is reached. It returns values - * which the standard considers to be legal input, and also returns editing - * and frotz hot keys. If called with a non-zero flag, it will also return - * line-editing keys like INSERT, etc, + * 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 flag) +static int unix_read_char(int extkeys) { int c; - struct timeval ctime; while(1) { - /* Timed keyboard input. Crude but functional. */ - if (global_timeout.tv_sec) { - nodelay(stdscr, TRUE); - do { - c = getch(); - if (c == ERR) { - gettimeofday(&ctime, NULL); - if ((ctime.tv_sec >= global_timeout.tv_sec) && - (ctime.tv_usec >= global_timeout.tv_usec)) { - nodelay(stdscr, FALSE); - return 0; - } - } - } while (c == ERR); - nodelay(stdscr, FALSE); - } - /* The easy way. */ - else c = getch(); - - /* Catch 98% of all input right here... */ - if ( ((c >= 32) && (c <= 126)) || (c == 13) || (c == 8)) - return c; - - /* ...and the other 2% makes up 98% of the code. :( */ - switch(c) { - /* Lucian P. Smith reports KEY_ENTER on Irix 5.3. 10 has never - been reported, but I'm leaving it in just in case. */ - case 10: case KEY_ENTER: return 13; - /* I've seen KEY_BACKSPACE returned on some terminals. */ - case KEY_BACKSPACE: return 8; - /* On seven-bit connections, "Alt-Foo" is 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 27: nodelay(stdscr, TRUE); c = getch(); nodelay(stdscr, FALSE); - if (c == ERR) return 27; - switch(c) { - case 112: return ZC_HKEY_PLAYBACK; /* Alt-P */ - case 114: return ZC_HKEY_RECORD; /* Alt-R */ - case 115: return ZC_HKEY_SEED; /* Alt-S */ - case 117: return ZC_HKEY_UNDO; /* Alt-U */ - case 110: return ZC_HKEY_RESTART; /* Alt-N */ - case 120: return ZC_HKEY_QUIT; /* Alt-X */ - case 100: return ZC_HKEY_DEBUG; /* Alt-D */ - case 104: return ZC_HKEY_HELP; /* Alt-H */ - default: return 27; - } - break; + timeout( timeout_to_ms()); + c = getch(); + + /* Catch 98% of all input right here... */ + if ((c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX) + || (!unix_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; + /* 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 129; - case KEY_DOWN: return 130; - case KEY_LEFT: return 131; - case KEY_RIGHT: return 132; - case KEY_F(1): return 133; case KEY_F(2): return 134; - case KEY_F(3): return 135; case KEY_F(4): return 136; - case KEY_F(5): return 137; case KEY_F(6): return 138; - case KEY_F(7): return 139; case KEY_F(8): return 140; - case KEY_F(9): return 141; case KEY_F(10): return 142; - case KEY_F(11): return 143; case KEY_F(12): return 144; - /* Curses can't differentiate keypad numbers from cursor keys. Damn. */ - /* This will catch the alt-keys on 8-bit clean input streams... */ - case 240: return ZC_HKEY_PLAYBACK; /* Alt-P */ - case 242: return ZC_HKEY_RECORD; /* Alt-R */ - case 243: return ZC_HKEY_SEED; /* Alt-S */ - case 245: return ZC_HKEY_UNDO; /* Alt-U */ - case 238: return ZC_HKEY_RESTART; /* Alt-N */ - case 248: return ZC_HKEY_QUIT; /* Alt-X */ - case 228: return ZC_HKEY_DEBUG; /* Alt-D */ - case 232: return ZC_HKEY_HELP; /* Alt-H */ -#ifdef EMACS_EDITING - case 21: return 27; /* Ctrl-U, erase line */ - case 2: return 131; /* Ctrl-B, left arrow */ - case 6: return 132; /* Ctrl-F, right arrow */ - case 16: return 129; /* Ctrl-P, up arrow */ - case 14: return 130; /* Ctrl-N, down arrow */ - case 1: c = KEY_HOME; break; /* Ctrl-A */ - case 4: c = 127; break; /* Ctrl-D */ - case 5: c = KEY_END; break; /* Ctrl-E */ -#endif + 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 + unix_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? */ - } + } - /* 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 (flag) return c; + /* 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; } } @@ -164,65 +243,109 @@ static int unix_read_char(int flag) /* * unix_add_to_history * - * Add the given string to the next available history buffer slot. Commandeer - * that slot if necessary using realloc. + * Add the given string to the next available history buffer slot. * */ static void unix_add_to_history(zchar *str) { - if (history_buffer[history_pointer] == NULL) - history_buffer[history_pointer] = (char *) malloc(strlen(str) + 1); - else - history_buffer[history_pointer] = - (char *) realloc(history_buffer[history_pointer], strlen(str) + 1); - strcpy(history_buffer[history_pointer], str); - history_pointer = ((history_pointer + 1) % MAX_HISTORY); - history_frame = history_pointer; /* Reset user frame after each line */ + 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) +static int unix_history_back(zchar *str, int searchlen, int maxlen) { + char **prev = history_view; - history_frame--; if (history_frame==-1) history_frame = (MAX_HISTORY - 1); - if ((history_frame == history_pointer) || - (history_buffer[history_frame] == NULL)) { - beep(); history_frame = (history_frame + 1) % MAX_HISTORY; - return 0; - } - strcpy(str, history_buffer[history_frame]); + do { + RING_DEC( history_view, history_buffer, history_end); + if ((history_view == history_next) + || (*history_view == NULL)) { + beep(); + 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)) { + beep(); + 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 int unix_history_forward(zchar *str) +static void scrnmove(int dest, int src, int n) { + int col, x, y; - history_frame = (history_frame + 1) % MAX_HISTORY; - if ((history_frame == history_pointer) || - (history_buffer[history_frame] == NULL)) { - beep(); history_frame--; if (history_frame == -1) history_frame = - (MAX_HISTORY - 1); - return 0; + 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); + } } - strcpy(str, history_buffer[history_frame]); - return 1; + 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); } /* @@ -253,7 +376,7 @@ static int unix_history_forward(zchar *str) * ZC_HKEY_HELP (Alt-H) * * If the timeout argument is not zero, the input gets interrupted - * after timeout/10 seconds (and the return value is 0). + * 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. @@ -273,145 +396,148 @@ static int unix_history_forward(zchar *str) zchar os_read_line (int max, zchar *buf, int timeout, int width, int continued) { - int ch, scrpos, pos, y, x; - char insert_flag = 1; /* Insert mode is now default */ - - scrpos = pos = strlen((char *) buf); - - if (!continued) - history_frame = history_pointer; /* Reset user's history view */ - - unix_set_global_timeout(timeout); + 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. */ + } - do { - refresh(); /* Shouldn't be necessary, but is, to print spaces */ - - ch = unix_read_char(1); - - /* Backspace */ - if ((ch == 8) && (scrpos)) { - mvdelch(y, --x); - pos--; scrpos--; - if (scrpos != pos) - memmove(&(buf[scrpos]), &(buf[scrpos+1]), pos-scrpos); - } - - /* Delete */ - if (((ch == 127) || (ch == KEY_DC)) && (scrpos < pos)) { - delch(); pos--; - memmove(&(buf[scrpos]), &(buf[scrpos+1]), pos-scrpos); - } - - /* Left key */ - if ((ch == 131) && (scrpos)) { - scrpos--; - move(y, --x); - } - /* Right key */ - if ((ch == 132) && (scrpos < pos)) { - scrpos++; - move(y, ++x); - } - - /* Home */ - if (ch == KEY_HOME) { - x -= scrpos; scrpos = 0; - move(y, x); - } - /* End */ - if (ch == KEY_END) { - x += (pos - scrpos); scrpos = pos; - move(y,x); - } - - /* Insert */ - if (ch == KEY_IC) - insert_flag = (insert_flag ? 0 : 1); - - /* Up and down keys */ - if (ch == 129) { - if (unix_history_back(buf)) { - x -= scrpos; - move(y, x); - while (scrpos) {addch(' '); scrpos--;} - move(y, x); - addstr(buf); - scrpos = pos = strlen(buf); - x += scrpos; - } - } - if (ch == 130) { - if (unix_history_forward(buf)) { - x -= scrpos; - move(y, x); - while(scrpos) {addch(' '); scrpos--;} - move(y, x); - addstr(buf); - scrpos = pos = strlen(buf); - x += scrpos; - } - } - - /* Page up/down (passthrough as up/down arrows for beyond zork) */ - if (ch == KEY_PPAGE) ch = 129; - if (ch == KEY_NPAGE) ch = 130; - - /* Escape */ - if (ch == 27) { - /* Move cursor to end of buffer first */ - x += (pos - scrpos); scrpos = pos; move(y,x); - x -= scrpos; - move(y, x); - while (scrpos) {addch(' '); scrpos--;} - move(y, x); pos = 0; - } - - /* Tab */ - if ((ch == 9) && (scrpos == pos)) { - int status; - zchar extension[10]; - - status = completion(buf, extension); - if (status != 2) { - addstr(extension); - strcpy(&buf[pos], extension); - pos += strlen(extension); scrpos += strlen(extension); + 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); } - if (status) beep(); - } - - /* ASCII printable */ - if ((ch >= 32) && (ch <= 126)) { - if (pos == scrpos) { - /* Append to end of buffer */ - if ((pos < max) && (pos < width)) { - buf[pos++] = (char) ch; - addch(ch); - scrpos++; x++; - } else beep(); - } - else { - /* Insert/overwrite in middle of buffer */ - if (insert_flag) { - memmove(&buf[scrpos+1], &buf[scrpos], pos-scrpos); - buf[scrpos++] = (char) ch; - insch(ch); - pos++; x++; move(y, x); - } else { - buf[scrpos++] = (char) ch; - addch(ch); - x++; - } + 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) beep(); + } + continue; /* TAB is invalid as an input character. */ + default: + /* ASCII or ISO-Latin-1 */ + if ((ch >= ZC_ASCII_MIN && ch <= ZC_ASCII_MAX) + || (!unix_plain_ascii + && ch >= ZC_LATIN1_MIN && ch <= ZC_LATIN1_MAX)) { + searchpos = -1; + if ((scrpos == max) || (insert_flag && (len == max))) { + beep(); + 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; + } } - } while (!is_terminator(ch)); - - buf[pos] = '\0'; - if (ch == 13) unix_add_to_history(buf); - return ch; - + 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 */ /* @@ -472,7 +598,7 @@ int os_read_file_name (char *file_name, const char *default_name, int flag) print_string (default_name); print_string ("\": "); - read_string (MAX_FILE_NAME, file_name); + read_string (FILENAME_MAX, (zchar *)file_name); /* Use the default name if nothing was typed */ @@ -487,3 +613,33 @@ int os_read_file_name (char *file_name, const char *default_name, int flag) return 1; } /* os_read_file_name */ + +/* + * 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. Because 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 */ diff --git a/ux_pic.c b/ux_pic.c index 39e343b..f8d766b 100644 --- a/ux_pic.c +++ b/ux_pic.c @@ -1,9 +1,213 @@ /* - * ux_pic.c + * ux_pic.c - Unix interface, picture outline functions * - * Unix interface, stubs for picture 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 "frotz.h" +#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); + } + 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 @@ -12,12 +216,94 @@ * */ -void os_draw_picture (int picture, int y, int x) +/* 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 = unix_plain_ascii ? '+' : ACS_PLUS; + ltee = unix_plain_ascii ? '<' : ACS_LTEE; + rtee = unix_plain_ascii ? '>' : ACS_RTEE; + ttee = unix_plain_ascii ? '^' : ACS_TTEE; + btee = unix_plain_ascii ? 'v' : ACS_BTEE; + hline = unix_plain_ascii ? '-' : ACS_HLINE; + vline = unix_plain_ascii ? '|' : ACS_VLINE; + ckboard = unix_plain_ascii ? ':' : ACS_CKBOARD; + urcorner = unix_plain_ascii ? '\\' : ACS_URCORNER; + ulcorner = unix_plain_ascii ? '/' : ACS_ULCORNER; + llcorner = unix_plain_ascii ? '\\' : ACS_LLCORNER; + lrcorner = unix_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 + */ - /* Not Implemented */ -}/* os_draw_picture */ + + + + + 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 @@ -36,26 +322,23 @@ void os_draw_picture (int picture, int y, int x) int os_peek_colour (void) { - - /* Not Implemented */ + if (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; - -}/* os_peek_colour */ - -/* - * os_picture_data - * - * Return true if the given picture is available. If so, write the - * width and height of the picture into the appropriate variables. - * Only when picture 0 is asked for, write the number of available - * pictures and the release number instead. - * - */ - -int os_picture_data (int picture, int *height, int *width) -{ - - /* Not implemented */ - return 0; - -}/* os_picture_data */ +#endif /* COLOR_SUPPORT */ + } else { + return (inch() & A_REVERSE) ? h_default_foreground : h_default_background; + } +} diff --git a/ux_screen.c b/ux_screen.c index 879cd28..d486dd6 100644 --- a/ux_screen.c +++ b/ux_screen.c @@ -1,8 +1,21 @@ /* - * ux_screen.c + * ux_screen.c - Unix interface, screen manipulation * - * 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 @@ -31,30 +44,34 @@ void os_erase_area (int top, int left, int bottom, int right) { int y, x, i, j; - int saved_style; /* Catch the most common situation and do things the easy way */ if ((top == 1) && (bottom == h_screen_rows) && - (left == 1) && (right == h_screen_cols)) + (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(current_color | ' '); erase(); - else { + bkgdset(0); +#else + erase(); +#endif + } else { /* Sigh... */ - getyx(stdscr, y, x); - saved_style = current_text_style; - os_set_text_style(0); + int saved_style = current_text_style; + os_set_text_style(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(' '); } - - os_set_text_style(saved_style); move(y, x); + os_set_text_style(saved_style); } - - refresh(); - }/* os_erase_area */ /* @@ -66,36 +83,47 @@ void os_erase_area (int top, int left, int bottom, int right) * */ -static int old_scroll_top = 0; -static int old_scroll_bottom = 0; - void os_scroll_area (int top, int left, int bottom, int right, int units) { -#ifdef COLOR_SUPPORT - int y, x, i; - int saved_style; -#endif + top--; left--; bottom--; right--; - if (units != 1) os_fatal("Can't Happen (os_scroll_area)"); /* FIXME */ + 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); + setscrreg(top, bottom); } scrollok(stdscr, TRUE); - scroll(stdscr); + scrl(units); scrollok(stdscr, FALSE); - -#ifdef COLOR_SUPPORT - if (h_flags & COLOUR_FLAG) { - getyx(stdscr, y, x); - move(old_scroll_bottom, 0); - saved_style = current_text_style; - os_set_text_style(0); - for (i = 0; i <= right; i++) addch(' '); - os_set_text_style(saved_style); - move(y, x); + } 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); + } } -#endif - + /* Restore attributes. */ + os_set_text_style(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 */ diff --git a/ux_text.c b/ux_text.c index ccb933c..e2d21c6 100644 --- a/ux_text.c +++ b/ux_text.c @@ -1,10 +1,24 @@ /* - * ux_text.c + * ux_text.c - Unix interface, text functions * - * 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 @@ -20,9 +34,14 @@ #include "frotz.h" #include "ux_frotz.h" -#ifdef COLOR_SUPPORT +/* 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; -#endif static char latin1_to_ascii[] = " ! c L >o= 32 && c <= 126) { + if (c >= ZC_ASCII_MIN && c <= ZC_ASCII_MAX) { addch(c); return; } @@ -271,7 +285,10 @@ int os_char_width (zchar c) char c2 = *ptr++; char c3 = *ptr++; - width++; + /* 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 != ' ') diff --git a/variable.c b/variable.c index 86a663b..3e5c6e0 100644 --- a/variable.c +++ b/variable.c @@ -1,8 +1,21 @@ -/* - * variable.c +/* 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. * - * Variable and stack related opcodes + * 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" -- 2.34.1