+This SDK release includes the Torque Engine, related
+tools and the demo application. Binaries and sources are included for all
+applications. All Torque SDK documentation is found online on the
+Torque Game Engine SDK Documentation
+home page. You must be logged into the GarageGames site for all the documentation to be visible.
+Documentation is divided into two main books:
+
+
General Torque Documentation, which includes a more complete section on getting started
+ as well as information on using the demo application, compiling the torque engine, using the torque editors,
+ scripting language basics, etc.
+
Torque Source Code Documentation, which is engine API and source level documentation. This is
+ for more advanced users and is generated from documentation imbeded in the source code using a tool
+ called Doxygen.
+
+
+
+
SDK Home Page
+
The Torque SDK home page contains links to
+all the documentation as well as tutorials, example code and all other things Torque related. This should be
+your first stop for information regarding the SDK.
+
+
+
Forums
+
The Torque SDK has a large online community located on the GarageGames
+forums. Forums dedicated to the Torque engine
+include both the public and
+private SDK forums. To access the private
+forums you must be logged in to the GarageGames site.
+
+
+
FAQ
+
Visit the online FAQ resources
+for frequently answered questions regarding the engine SDK and tools.
+
+
+
Support
+
Support is provided exclusively through the online Torque SDK Private Forums
+and FAQ resources.
+Please make sure you read through the FAQs before posting questions. The Private forums are only visible if you are logged into the
+GarageGames website.
+
+
- GarageGames Staff
+
+
+
+
diff --git a/bin/bison/bison.exe b/bin/bison/bison.exe
new file mode 100755
index 0000000..4881bf6
Binary files /dev/null and b/bin/bison/bison.exe differ
diff --git a/bin/bison/bison.hairy b/bin/bison/bison.hairy
new file mode 100755
index 0000000..260b687
--- /dev/null
+++ b/bin/bison/bison.hairy
@@ -0,0 +1,334 @@
+
+extern int timeclock;
+
+
+int yyerror; /* Yyerror and yycost are set by guards. */
+int yycost; /* If yyerror is set to a nonzero value by a */
+ /* guard, the reduction with which the guard */
+ /* is associated is not performed, and the */
+ /* error recovery mechanism is invoked. */
+ /* Yycost indicates the cost of performing */
+ /* the reduction given the attributes of the */
+ /* symbols. */
+
+
+/* YYMAXDEPTH indicates the size of the parser's state and value */
+/* stacks. */
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 500
+#endif
+
+/* YYMAXRULES must be at least as large as the number of rules that */
+/* could be placed in the rule queue. That number could be determined */
+/* from the grammar and the size of the stack, but, as yet, it is not. */
+
+#ifndef YYMAXRULES
+#define YYMAXRULES 100
+#endif
+
+#ifndef YYMAXBACKUP
+#define YYMAXBACKUP 100
+#endif
+
+
+short yyss[YYMAXDEPTH]; /* the state stack */
+YYSTYPE yyvs[YYMAXDEPTH]; /* the semantic value stack */
+YYLTYPE yyls[YYMAXDEPTH]; /* the location stack */
+short yyrq[YYMAXRULES]; /* the rule queue */
+int yychar; /* the lookahead symbol */
+
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+YYSTYPE yytval; /* the semantic value for the state */
+ /* at the top of the state stack. */
+
+YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+
+YYLTYPE yytloc; /* location data for the state at the */
+ /* top of the state stack */
+
+
+int yynunlexed;
+short yyunchar[YYMAXBACKUP];
+YYSTYPE yyunval[YYMAXBACKUP];
+YYLTYPE yyunloc[YYMAXBACKUP];
+
+short *yygssp; /* a pointer to the top of the state */
+ /* stack; only set during error */
+ /* recovery. */
+
+YYSTYPE *yygvsp; /* a pointer to the top of the value */
+ /* stack; only set during error */
+ /* recovery. */
+
+YYLTYPE *yyglsp; /* a pointer to the top of the */
+ /* location stack; only set during */
+ /* error recovery. */
+
+
+/* Yyget is an interface between the parser and the lexical analyzer. */
+/* It is costly to provide such an interface, but it avoids requiring */
+/* the lexical analyzer to be able to back up the scan. */
+
+yyget()
+{
+ if (yynunlexed > 0)
+ {
+ yynunlexed--;
+ yychar = yyunchar[yynunlexed];
+ yylval = yyunval[yynunlexed];
+ yylloc = yyunloc[yynunlexed];
+ }
+ else if (yychar <= 0)
+ yychar = 0;
+ else
+ {
+ yychar = yylex();
+ if (yychar < 0)
+ yychar = 0;
+ else yychar = YYTRANSLATE(yychar);
+ }
+}
+
+
+
+yyunlex(chr, val, loc)
+int chr;
+YYSTYPE val;
+YYLTYPE loc;
+{
+ yyunchar[yynunlexed] = chr;
+ yyunval[yynunlexed] = val;
+ yyunloc[yynunlexed] = loc;
+ yynunlexed++;
+}
+
+
+
+yyrestore(first, last)
+register short *first;
+register short *last;
+{
+ register short *ssp;
+ register short *rp;
+ register int symbol;
+ register int state;
+ register int tvalsaved;
+
+ ssp = yygssp;
+ yyunlex(yychar, yylval, yylloc);
+
+ tvalsaved = 0;
+ while (first != last)
+ {
+ symbol = yystos[*ssp];
+ if (symbol < YYNTBASE)
+ {
+ yyunlex(symbol, yytval, yytloc);
+ tvalsaved = 1;
+ ssp--;
+ }
+
+ ssp--;
+
+ if (first == yyrq)
+ first = yyrq + YYMAXRULES;
+
+ first--;
+
+ for (rp = yyrhs + yyprhs[*first]; symbol = *rp; rp++)
+ {
+ if (symbol < YYNTBASE)
+ state = yytable[yypact[*ssp] + symbol];
+ else
+ {
+ state = yypgoto[symbol - YYNTBASE] + *ssp;
+
+ if (state >= 0 && state <= YYLAST && yycheck[state] == *ssp)
+ state = yytable[state];
+ else
+ state = yydefgoto[symbol - YYNTBASE];
+ }
+
+ *++ssp = state;
+ }
+ }
+
+ if ( ! tvalsaved && ssp > yyss)
+ {
+ yyunlex(yystos[*ssp], yytval, yytloc);
+ ssp--;
+ }
+
+ yygssp = ssp;
+}
+
+
+
+int
+yyparse()
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register short *yyrq0;
+ register short *yyptr;
+ register YYSTYPE *yyvsp;
+
+ int yylen;
+ YYLTYPE *yylsp;
+ short *yyrq1;
+ short *yyrq2;
+
+ yystate = 0;
+ yyssp = yyss - 1;
+ yyvsp = yyvs - 1;
+ yylsp = yyls - 1;
+ yyrq0 = yyrq;
+ yyrq1 = yyrq0;
+ yyrq2 = yyrq0;
+
+ yychar = yylex();
+ if (yychar < 0)
+ yychar = 0;
+ else yychar = YYTRANSLATE(yychar);
+
+yynewstate:
+
+ if (yyssp >= yyss + YYMAXDEPTH - 1)
+ {
+ yyabort("Parser Stack Overflow");
+ YYABORT;
+ }
+
+ *++yyssp = yystate;
+
+yyresume:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ yyn += yychar;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ yystate = yyn;
+
+ yyptr = yyrq2;
+ while (yyptr != yyrq1)
+ {
+ yyn = *yyptr++;
+ yylen = yyr2[yyn];
+ yyvsp -= yylen;
+ yylsp -= yylen;
+
+ yyguard(yyn, yyvsp, yylsp);
+ if (yyerror)
+ goto yysemerr;
+
+ yyaction(yyn, yyvsp, yylsp);
+ *++yyvsp = yyval;
+
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->timestamp = timeclock;
+ yylsp->first_line = yytloc.first_line;
+ yylsp->first_column = yytloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+
+ if (yyptr == yyrq + YYMAXRULES)
+ yyptr = yyrq;
+ }
+
+ if (yystate == YYFINAL)
+ YYACCEPT;
+
+ yyrq2 = yyptr;
+ yyrq1 = yyrq0;
+
+ *++yyvsp = yytval;
+ *++yylsp = yytloc;
+ yytval = yylval;
+ yytloc = yylloc;
+ yyget();
+
+ goto yynewstate;
+
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+yyreduce:
+
+ *yyrq0++ = yyn;
+
+ if (yyrq0 == yyrq + YYMAXRULES)
+ yyrq0 = yyrq;
+
+ if (yyrq0 == yyrq2)
+ {
+ yyabort("Parser Rule Queue Overflow");
+ YYABORT;
+ }
+
+ yyssp -= yyr2[yyn];
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yysemerr:
+ *--yyptr = yyn;
+ yyrq2 = yyptr;
+ yyvsp += yyr2[yyn];
+
+yyerrlab:
+
+ yygssp = yyssp;
+ yygvsp = yyvsp;
+ yyglsp = yylsp;
+ yyrestore(yyrq0, yyrq2);
+ yyrecover();
+ yystate = *yygssp;
+ yyssp = yygssp;
+ yyvsp = yygvsp;
+ yyrq0 = yyrq;
+ yyrq1 = yyrq0;
+ yyrq2 = yyrq0;
+ goto yyresume;
+}
+
+$
diff --git a/bin/bison/bison.html b/bin/bison/bison.html
new file mode 100755
index 0000000..f56b912
--- /dev/null
+++ b/bin/bison/bison.html
@@ -0,0 +1,4501 @@
+
+
+
+Bison 1.24
+
+
+
+
+
+Bison is a general-purpose parser generator that converts a
+grammar description for an LALR(1) context-free grammar into a C
+program to parse that grammar. Once you are proficient with Bison,
+you may use it to develop a wide range of language parsers, from those
+used in simple desk calculators to complex programming languages.
+
+Bison is upward compatible with Yacc: all properly-written Yacc grammars
+ought to work with Bison with no change. Anyone familiar with Yacc
+should be able to use Bison with little trouble. You need to be fluent in
+C programming in order to use Bison or to understand this manual.
+
+We begin with tutorial chapters that explain the basic concepts of using
+Bison and show three explained examples, each building on the last. If you
+don't know Bison or Yacc, start by reading these chapters. Reference
+chapters follow which describe specific aspects of Bison in detail.
+
+Bison was written primarily by Robert Corbett; Richard Stallman made
+it Yacc-compatible. This edition corresponds to version 1.24 of Bison.
+
+As of Bison version 1.24, we have changed the distribution terms for
+yyparse to permit using Bison's output in non-free programs.
+Formerly, Bison parsers could be used only in programs that were free
+software.
+
+The other GNU programming tools, such as the GNU C compiler, have never
+had such a requirement. They could always be used for non-free
+software. The reason Bison was different was not due to a special
+policy decision; it resulted from applying the usual General Public
+License to all of the Bison source code.
+
+The output of the Bison utility--the Bison parser file--contains a
+verbatim copy of a sizable piece of Bison, which is the code for the
+yyparse function. (The actions from your grammar are inserted
+into this function at one point, but the rest of the function is not
+changed.) When we applied the GPL terms to the code for yyparse,
+the effect was to restrict the use of Bison output to free software.
+
+We didn't change the terms because of sympathy for people who want to
+make software proprietary. Software should be free. But we
+concluded that limiting Bison's use to free software was doing little to
+encourage people to make other software free. So we decided to make the
+practical conditions for using Bison match the practical conditions for
+using the other GNU tools.
+
+ 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.
+
+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.
+
+
+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.
+
+
+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:
+
+
+
+You must cause the modified files to carry prominent notices
+stating that you changed the files and the date of any change.
+
+
+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.
+
+
+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.
+
+
+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:
+
+
+
+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,
+
+
+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,
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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.
+
+
+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
+
+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.
+
+
+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.
+
+ 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.
+
+
+one line to give the program's name and a brief idea of what it does.
+Copyright (C) 19yyname of author
+
+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) 19yyname 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.
+
+signature of Ty Coon, 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.
+
+This chapter introduces many of the basic concepts without which the
+details of Bison will not make sense. If you do not already know how to
+use Bison or Yacc, we suggest you start by reading this chapter carefully.
+
+
+
+In order for Bison to parse a language, it must be described by a
+context-free grammar. This means that you specify one or more
+syntactic groupings and give rules for constructing them from their
+parts. For example, in the C language, one kind of grouping is called an
+`expression'. One rule for making an expression might be, "An expression
+can be made of a minus sign and another expression". Another would be,
+"An expression can be an integer". As you can see, rules are often
+recursive, but there must be at least one rule which leads out of the
+recursion.
+
+
+
+The most common formal system for presenting such rules for humans to read
+is Backus-Naur Form or "BNF", which was developed in order to
+specify the language Algol 60. Any grammar expressed in BNF is a
+context-free grammar. The input to Bison is essentially machine-readable
+BNF.
+
+Not all context-free languages can be handled by Bison, only those
+that are LALR(1). In brief, this means that it must be possible to
+tell how to parse any portion of an input string with just a single
+token of look-ahead. Strictly speaking, that is a description of an
+LR(1) grammar, and LALR(1) involves additional restrictions that are
+hard to explain simply; but it is rare in actual practice to find an
+LR(1) grammar that fails to be LALR(1). See section Mysterious Reduce/Reduce Conflicts, for more information on this.
+
+
+
+
+
+In the formal grammatical rules for a language, each kind of syntactic unit
+or grouping is named by a symbol. Those which are built by grouping
+smaller constructs according to grammatical rules are called
+nonterminal symbols; those which can't be subdivided are called
+terminal symbols or token types. We call a piece of input
+corresponding to a single terminal symbol a token, and a piece
+corresponding to a single nonterminal symbol a grouping.
+We can use the C language as an example of what symbols, terminal and
+nonterminal, mean. The tokens of C are identifiers, constants (numeric and
+string), and the various keywords, arithmetic operators and punctuation
+marks. So the terminal symbols of a grammar for C include `identifier',
+`number', `string', plus one symbol for each keyword, operator or
+punctuation mark: `if', `return', `const', `static', `int', `char',
+`plus-sign', `open-brace', `close-brace', `comma' and many more. (These
+tokens can be subdivided into characters, but that is a matter of
+lexicography, not grammar.)
+
+Here is a simple C function subdivided into tokens:
+
+The syntactic groupings of C include the expression, the statement, the
+declaration, and the function definition. These are represented in the
+grammar of C by nonterminal symbols `expression', `statement',
+`declaration' and `function definition'. The full grammar uses dozens of
+additional language constructs, each with its own nonterminal symbol, in
+order to express the meanings of these four. The example above is a
+function definition; it contains one declaration, and one statement. In
+the statement, each `x' is an expression and so is `x * x'.
+Each nonterminal symbol must have grammatical rules showing how it is made
+out of simpler constructs. For example, one kind of C statement is the
+return statement; this would be described with a grammar rule which
+reads informally as follows:
+
+
+A `statement' can be made of a `return' keyword, an `expression' and a
+`semicolon'.
+
+
+There would be many other rules for `statement', one for each kind of
+statement in C.
+
+
+One nonterminal symbol must be distinguished as the special one which
+defines a complete utterance in the language. It is called the start
+symbol. In a compiler, this means a complete input program. In the C
+language, the nonterminal symbol `sequence of definitions and declarations'
+plays this role.
+
+For example, `1 + 2' is a valid C expression--a valid part of a C
+program--but it is not valid as an entire C program. In the
+context-free grammar of C, this follows from the fact that `expression' is
+not the start symbol.
+
+The Bison parser reads a sequence of tokens as its input, and groups the
+tokens using the grammar rules. If the input is valid, the end result is
+that the entire token sequence reduces to a single grouping whose symbol is
+the grammar's start symbol. If we use a grammar for C, the entire input
+must be a `sequence of definitions and declarations'. If not, the parser
+reports a syntax error.
+
+
+
+
+A formal grammar is a mathematical construct. To define the language
+for Bison, you must write a file expressing the grammar in Bison syntax:
+a Bison grammar file. See section Bison Grammar Files.
+A nonterminal symbol in the formal grammar is represented in Bison input
+as an identifier, like an identifier in C. By convention, it should be
+in lower case, such as expr, stmt or declaration.
+The Bison representation for a terminal symbol is also called a token
+type. Token types as well can be represented as C-like identifiers. By
+convention, these identifiers should be upper case to distinguish them from
+nonterminals: for example, INTEGER, IDENTIFIER, IF or
+RETURN. A terminal symbol that stands for a particular keyword in
+the language should be named after that keyword converted to upper case.
+The terminal symbol error is reserved for error recovery.
+
+See section Symbols, Terminal and Nonterminal.
+A terminal symbol can also be represented as a character literal, just like
+a C character constant. You should do this whenever a token is just a
+single character (parenthesis, plus-sign, etc.): use that same character in
+a literal as the terminal symbol for that token.
+
+The grammar rules also have an expression in Bison syntax. For example,
+here is the Bison rule for a C return statement. The semicolon in
+quotes is a literal character token, representing part of the C syntax for
+the statement; the naked semicolon, and the colon, are Bison punctuation
+used in every rule.
+
+
+
+A formal grammar selects tokens only by their classifications: for example,
+if a rule mentions the terminal symbol `integer constant', it means that
+any integer constant is grammatically valid in that position. The
+precise value of the constant is irrelevant to how to parse the input: if
+`x+4' is grammatical then `x+1' or `x+3989' is equally
+grammatical.
+
+But the precise value is very important for what the input means once it is
+parsed. A compiler is useless if it fails to distinguish between 4, 1 and
+3989 as constants in the program! Therefore, each token in a Bison grammar
+has both a token type and a semantic value. See section Defining Language Semantics,
+for details.
+
+The token type is a terminal symbol defined in the grammar, such as
+INTEGER, IDENTIFIER or ','. It tells everything
+you need to know to decide where the token may validly appear and how to
+group it with other tokens. The grammar rules know nothing about tokens
+except their types.
+
+The semantic value has all the rest of the information about the
+meaning of the token, such as the value of an integer, or the name of an
+identifier. (A token such as ',' which is just punctuation doesn't
+need to have any semantic value.)
+
+For example, an input token might be classified as token type
+INTEGER and have the semantic value 4. Another input token might
+have the same token type INTEGER but value 3989. When a grammar
+rule says that INTEGER is allowed, either of these tokens is
+acceptable because each is an INTEGER. When the parser accepts the
+token, it keeps track of the token's semantic value.
+
+Each grouping can also have a semantic value as well as its nonterminal
+symbol. For example, in a calculator, an expression typically has a
+semantic value that is a number. In a compiler for a programming
+language, an expression typically has a semantic value that is a tree
+structure describing the meaning of the expression.
+
+
+
+In order to be useful, a program must do more than parse input; it must
+also produce some output based on the input. In a Bison grammar, a grammar
+rule can have an action made up of C statements. Each time the
+parser recognizes a match for that rule, the action is executed.
+See section Actions.
+
+Most of the time, the purpose of an action is to compute the semantic value
+of the whole construct from the semantic values of its parts. For example,
+suppose we have a rule which says an expression can be the sum of two
+expressions. When the parser recognizes such a sum, each of the
+subexpressions has a semantic value which describes how it was built up.
+The action for this rule should create a similar sort of value for the
+newly recognized larger expression.
+For example, here is a rule that says an expression can be the sum of
+two subexpressions:
+
+
+
+expr: expr '+' expr { $$ = $1 + $3; }
+ ;
+
+
+
+The action says how to produce the semantic value of the sum expression
+from the values of the two subexpressions.
+
+
+
+
+
+When you run Bison, you give it a Bison grammar file as input. The output
+is a C source file that parses the language described by the grammar.
+This file is called a Bison parser. Keep in mind that the Bison
+utility and the Bison parser are two distinct programs: the Bison utility
+is a program whose output is the Bison parser that becomes part of your
+program.
+
+The job of the Bison parser is to group tokens into groupings according to
+the grammar rules--for example, to build identifiers and operators into
+expressions. As it does this, it runs the actions for the grammar rules it
+uses.
+
+The tokens come from a function called the lexical analyzer that you
+must supply in some fashion (such as by writing it in C). The Bison parser
+calls the lexical analyzer each time it wants a new token. It doesn't know
+what is "inside" the tokens (though their semantic values may reflect
+this). Typically the lexical analyzer makes the tokens by parsing
+characters of text, but Bison does not depend on this. See section The Lexical Analyzer Function yylex.
+The Bison parser file is C code which defines a function named
+yyparse which implements that grammar. This function does not make
+a complete C program: you must supply some additional functions. One is
+the lexical analyzer. Another is an error-reporting function which the
+parser calls to report an error. In addition, a complete C program must
+start with a function called main; you have to provide this, and
+arrange for it to call yyparse or the parser will never run.
+See section Parser C-Language Interface.
+Aside from the token type names and the symbols in the actions you
+write, all variable and function names used in the Bison parser file
+begin with `yy' or `YY'. This includes interface functions
+such as the lexical analyzer function yylex, the error reporting
+function yyerror and the parser function yyparse itself.
+This also includes numerous identifiers used for internal purposes.
+Therefore, you should avoid using C identifiers starting with `yy'
+or `YY' in the Bison grammar file except for the ones defined in
+this manual.
+
+
+
+The actual language-design process using Bison, from grammar specification
+to a working compiler or interpreter, has these parts:
+
+
+Formally specify the grammar in a form recognized by Bison
+(see section Bison Grammar Files). For each grammatical rule in the language,
+describe the action that is to be taken when an instance of that rule
+is recognized. The action is described by a sequence of C statements.
+
+Write a lexical analyzer to process input and pass tokens to the
+parser. The lexical analyzer may be written by hand in C
+(see section The Lexical Analyzer Function yylex). It could also be produced using Lex, but the use
+of Lex is not discussed in this manual.
+
+Write a controlling function that calls the Bison-produced parser.
+
+Write error-reporting routines.
+
+
+To turn this source code as written into a runnable program, you
+must follow these steps:
+
+
+Run Bison on the grammar to produce the parser.
+
+Compile the code output by Bison, as well as any other source files.
+
+Link the object files to produce the finished product.
+
+The `%%', `%{' and `%}' are punctuation that appears
+in every Bison grammar file to separate the sections.
+The C declarations may define types and variables used in the actions.
+You can also use preprocessor commands to define macros used there, and use
+#include to include header files that do any of these things.
+The Bison declarations declare the names of the terminal and nonterminal
+symbols, and may also describe operator precedence and the data types of
+semantic values of various symbols.
+
+The grammar rules define how to construct each nonterminal symbol from its
+parts.
+
+The additional C code can contain any C code you want to use. Often the
+definition of the lexical analyzer yylex goes here, plus subroutines
+called by the actions in the grammar rules. In a simple program, all the
+rest of the program can go here.
+
+
+
+Now we show and explain three sample programs written using Bison: a
+reverse polish notation calculator, an algebraic (infix) notation
+calculator, and a multi-function calculator. All three have been tested
+under BSD Unix 4.3; each produces a usable, though limited, interactive
+desk-top calculator.
+
+These examples are simple, but Bison grammars for real programming
+languages are written the same way.
+
+
+
+
+
+The first example is that of a simple double-precision reverse polish
+notation calculator (a calculator using postfix operators). This example
+provides a good starting point, since operator precedence is not an issue.
+
+The second example will illustrate how operator precedence is handled.
+The source code for this calculator is named `rpcalc.y'. The
+`.y' extension is a convention used for Bison input files.
+
+The C declarations section (see section The C Declarations Section) contains two
+preprocessor directives.
+
+The #define directive defines the macro YYSTYPE, thus
+specifying the C data type for semantic values of both tokens and groupings
+(see section Data Types of Semantic Values). The Bison parser will use whatever type
+YYSTYPE is defined as; if you don't define it, int is the
+default. Because we specify double, each token and each expression
+has an associated value, which is a floating point number.
+
+The #include directive is used to declare the exponentiation
+function pow.
+
+The second section, Bison declarations, provides information to Bison about
+the token types (see section The Bison Declarations Section). Each terminal symbol that is
+not a single-character literal must be declared here. (Single-character
+literals normally don't need to be declared.) In this example, all the
+arithmetic operators are designated by single-character literals, so the
+only terminal symbol that needs to be declared is NUM, the token
+type for numeric constants.
+
+The groupings of the rpcalc "language" defined here are the expression
+(given the name exp), the line of input (line), and the
+complete input transcript (input). Each of these nonterminal
+symbols has several alternate rules, joined by the `|' punctuator
+which is read as "or". The following sections explain what these rules
+mean.
+
+The semantics of the language is determined by the actions taken when a
+grouping is recognized. The actions are the C code that appears inside
+braces. See section Actions.
+
+You must specify these actions in C, but Bison provides the means for
+passing semantic values between the rules. In each action, the
+pseudo-variable $$ stands for the semantic value for the grouping
+that the rule is going to construct. Assigning a value to $$ is the
+main job of most actions. The semantic values of the components of the
+rule are referred to as $1, $2, and so on.
+
+This definition reads as follows: "A complete input is either an empty
+string, or a complete input followed by an input line". Notice that
+"complete input" is defined in terms of itself. This definition is said
+to be left recursive since input appears always as the
+leftmost symbol in the sequence. See section Recursive Rules.
+
+The first alternative is empty because there are no symbols between the
+colon and the first `|'; this means that input can match an
+empty string of input (no tokens). We write the rules this way because it
+is legitimate to type Ctrl-d right after you start the calculator.
+
+It's conventional to put an empty alternative first and write the comment
+`/* empty */' in it.
+
+The second alternate rule (input line) handles all nontrivial input.
+It means, "After reading any number of lines, read one more line if
+possible." The left recursion makes this rule into a loop. Since the
+first alternative matches empty input, the loop can be executed zero or
+more times.
+
+The parser function yyparse continues to process input until a
+grammatical error is seen or the lexical analyzer says there are no more
+input tokens; we will arrange for the latter to happen at end of file.
+
+The first alternative is a token which is a newline character; this means
+that rpcalc accepts a blank line (and ignores it, since there is no
+action). The second alternative is an expression followed by a newline.
+This is the alternative that makes rpcalc useful. The semantic value of
+the exp grouping is the value of $1 because the exp in
+question is the first symbol in the alternative. The action prints this
+value, which is the result of the computation the user asked for.
+This action is unusual because it does not assign a value to $$. As
+a consequence, the semantic value associated with the line is
+uninitialized (its value will be unpredictable). This would be a bug if
+that value were ever used, but we don't use it: once rpcalc has printed the
+value of the user's input line, that value is no longer needed.
+
+The exp grouping has several rules, one for each kind of expression.
+The first rule handles the simplest expressions: those that are just numbers.
+The second handles an addition-expression, which looks like two expressions
+followed by a plus-sign. The third handles subtraction, and so on.
+
+Most of the rules have actions that compute the value of the expression in
+terms of the value of its parts. For example, in the rule for addition,
+$1 refers to the first component exp and $2 refers to
+the second one. The third component, '+', has no meaningful
+associated semantic value, but if it had one you could refer to it as
+$3. When yyparse recognizes a sum expression using this
+rule, the sum of the two subexpressions' values is produced as the value of
+the entire expression. See section Actions.
+You don't have to give an action for every rule. When a rule has no
+action, Bison by default copies the value of $1 into $$.
+This is what happens in the first rule (the one that uses NUM).
+The formatting shown here is the recommended convention, but Bison does
+not require it. You can add or change whitespace as much as you wish.
+For example, this:
+
+
+
+The lexical analyzer's job is low-level parsing: converting characters or
+sequences of characters into tokens. The Bison parser gets its tokens by
+calling the lexical analyzer. See section The Lexical Analyzer Function yylex.
+Only a simple lexical analyzer is needed for the RPN calculator. This
+lexical analyzer skips blanks and tabs, then reads in numbers as
+double and returns them as NUM tokens. Any other character
+that isn't part of a number is a separate token. Note that the token-code
+for such a single-character token is the character itself.
+The return value of the lexical analyzer function is a numeric code which
+represents a token type. The same text used in Bison rules to stand for
+this token type is also a C expression for the numeric code for the type.
+This works in two ways. If the token type is a character literal, then its
+numeric code is the ASCII code for that character; you can use the same
+character literal in the lexical analyzer to express the number. If the
+token type is an identifier, that identifier is defined by Bison as a C
+macro whose definition is the appropriate number. In this example,
+therefore, NUM becomes a macro for yylex to use.
+The semantic value of the token (if it has one) is stored into the global
+variable yylval, which is where the Bison parser will look for it.
+(The C data type of yylval is YYSTYPE, which was defined
+at the beginning of the grammar; see section Declarations for rpcalc.)
+A token type code of zero is returned if the end-of-file is encountered.
+(Bison recognizes any nonpositive value as indicating the end of the
+input.)
+Here is the code for the lexical analyzer:
+
+/* Lexical analyzer returns a double floating point
+ number on the stack and the token NUM, or the ASCII
+ character read if not a number. Skips all blanks
+ and tabs, returns 0 for EOF. */
+#include <ctype.h>
+yylex ()
+{
+ int c;
+ /* skip white space */
+ while ((c = getchar ()) == ' ' || c == '\t')
+ ;
+ /* process numbers */
+ if (c == '.' || isdigit (c))
+ {
+ ungetc (c, stdin);
+ scanf ("%lf", &yylval);
+ return NUM;
+ }
+ /* return end-of-file */
+ if (c == EOF)
+ return 0;
+ /* return single chars */
+ return c;
+}
+
+
+
+In keeping with the spirit of this example, the controlling function is
+kept to the bare minimum. The only requirement is that it call
+yyparse to start the process of parsing.
+
+
+When yyparse detects a syntax error, it calls the error reporting
+function yyerror to print an error message (usually but not always
+"parse error"). It is up to the programmer to supply yyerror
+(see section Parser C-Language Interface), so here is the definition we will use:
+
+#include <stdio.h>
+yyerror (s) /* Called by yyparse on error */
+ char *s;
+{
+ printf ("%s\n", s);
+}
+
+
+After yyerror returns, the Bison parser may recover from the error
+and continue parsing if the grammar contains a suitable error rule
+(see section Error Recovery). Otherwise, yyparse returns nonzero. We
+have not written any error rules in this example, so any invalid input will
+cause the calculator program to exit. This is not clean behavior for a
+real calculator, but it is adequate in the first example.
+
+
+Before running Bison to produce a parser, we need to decide how to arrange
+all the source code in one or more source files. For such a simple example,
+the easiest thing is to put everything in one file. The definitions of
+yylex, yyerror and main go at the end, in the
+"additional C code" section of the file (see section The Overall Layout of a Bison Grammar).
+For a large project, you would probably have several source files, and use
+make to arrange to recompile them.
+With all the source in a single file, you use the following command to
+convert it into a parser file:
+
+bison file_name.y
+
+
+In this example the file was called `rpcalc.y' (for "Reverse Polish
+CALCulator"). Bison produces a file named `file_name.tab.c',
+removing the `.y' from the original file name. The file output by
+Bison contains the source code for yyparse. The additional
+functions in the input file (yylex, yyerror and main)
+are copied verbatim to the output.
+
+
+Here is how to compile and run the parser file:
+
+# List files in current directory.
+% ls
+rpcalc.tab.c rpcalc.y
+# Compile the Bison parser.
+# `-lm' tells compiler to search math library for pow.
+% cc rpcalc.tab.c -lm -o rpcalc
+# List files again.
+% ls
+rpcalc rpcalc.tab.c rpcalc.y
+
+
+The file `rpcalc' now contains the executable code. Here is an
+example session using rpcalc.
+
+
+
+
+We now modify rpcalc to handle infix operators instead of postfix. Infix
+notation involves the concept of operator precedence and the need for
+parentheses nested to arbitrary depth. Here is the Bison code for
+`calc.y', an infix desk-top calculator.
+
+The functions yylex, yyerror and main can be the same
+as before.
+There are two important new features shown in this code.
+In the second section (Bison declarations), %left declares token
+types and says they are left-associative operators. The declarations
+%left and %right (right associativity) take the place of
+%token which is used to declare a token type name without
+associativity. (These tokens are single-character literals, which
+ordinarily don't need to be declared. We declare them here to specify
+the associativity.)
+Operator precedence is determined by the line ordering of the
+declarations; the higher the line number of the declaration (lower on
+the page or screen), the higher the precedence. Hence, exponentiation
+has the highest precedence, unary minus (NEG) is next, followed
+by `*' and `/', and so on. See section Operator Precedence.
+The other important new feature is the %prec in the grammar section
+for the unary minus operator. The %prec simply instructs Bison that
+the rule `| '-' exp' has the same precedence as NEG---in this
+case the next-to-highest. See section Context-Dependent Precedence.
+Here is a sample run of `calc.y':
+
+
+Up to this point, this manual has not addressed the issue of error
+recovery---how to continue parsing after the parser detects a syntax
+error. All we have handled is error reporting with yyerror. Recall
+that by default yyparse returns after calling yyerror. This
+means that an erroneous input line causes the calculator program to exit.
+Now we show how to rectify this deficiency.
+The Bison language itself includes the reserved word error, which
+may be included in the grammar rules. In the example below it has
+been added to one of the alternatives for line:
+
+This addition to the grammar allows for simple error recovery in the event
+of a parse error. If an expression that cannot be evaluated is read, the
+error will be recognized by the third rule for line, and parsing
+will continue. (The yyerror function is still called upon to print
+its message as well.) The action executes the statement yyerrok, a
+macro defined automatically by Bison; its meaning is that error recovery is
+complete (see section Error Recovery). Note the difference between
+yyerrok and yyerror; neither one is a misprint.
+This form of error recovery deals with syntax errors. There are other
+kinds of errors; for example, division by zero, which raises an exception
+signal that is normally fatal. A real calculator program must handle this
+signal and use longjmp to return to main and resume parsing
+input lines; it would also have to discard the rest of the current line of
+input. We won't discuss this issue further because it is not specific to
+Bison programs.
+
+
+
+
+Now that the basics of Bison have been discussed, it is time to move on to
+a more advanced problem. The above calculators provided only five
+functions, `+', `-', `*', `/' and `^'. It would
+be nice to have a calculator that provides other mathematical functions such
+as sin, cos, etc.
+It is easy to add new operators to the infix calculator as long as they are
+only single-character literals. The lexical analyzer yylex passes
+back all non-number characters as tokens, so new grammar rules suffice for
+adding a new operator. But we want something more flexible: built-in
+functions whose syntax has this form:
+
+function_name (argument)
+
+
+At the same time, we will add memory to the calculator, by allowing you
+to create named variables, store values in them, and use them later.
+Here is a sample session with the multi-function calculator:
+
+Here are the C and Bison declarations for the multi-function calculator.
+
+%{
+#include <math.h> /* For math functions, cos(), sin(), etc. */
+#include "calc.h" /* Contains definition of `symrec' */
+%}
+%union {
+double val; /* For returning numbers. */
+symrec *tptr; /* For returning symbol-table pointers */
+}
+%token <val> NUM /* Simple double precision number */
+%token <tptr> VAR FNCT /* Variable and Function */
+%type <val> exp
+%right '='
+%left '-' '+'
+%left '*' '/'
+%left NEG /* Negation--unary minus */
+%right '^' /* Exponentiation */
+/* Grammar follows */
+%%
+
+
+The above grammar introduces only two new features of the Bison language.
+These features allow semantic values to have various data types
+(see section More Than One Value Type).
+The %union declaration specifies the entire list of possible types;
+this is instead of defining YYSTYPE. The allowable types are now
+double-floats (for exp and NUM) and pointers to entries in
+the symbol table. See section The Collection of Value Types.
+Since values can now have various types, it is necessary to associate a
+type with each grammar symbol whose semantic value is used. These symbols
+are NUM, VAR, FNCT, and exp. Their
+declarations are augmented with information about their data type (placed
+between angle brackets).
+The Bison construct %type is used for declaring nonterminal symbols,
+just as %token is used for declaring token types. We have not used
+%type before because nonterminal symbols are normally declared
+implicitly by the rules that define them. But exp must be declared
+explicitly so we can specify its value type. See section Nonterminal Symbols.
+
+Here are the grammar rules for the multi-function calculator.
+Most of them are copied directly from calc; three rules,
+those which mention VAR or FNCT, are new.
+
+
+The multi-function calculator requires a symbol table to keep track of the
+names and meanings of variables and functions. This doesn't affect the
+grammar rules (except for the actions) or the Bison declarations, but it
+requires some additional C functions for support.
+The symbol table itself consists of a linked list of records. Its
+definition, which is kept in the header `calc.h', is as follows. It
+provides for either functions or variables to be placed in the table.
+
+/* Data type for links in the chain of symbols. */
+struct symrec
+{
+ char *name; /* name of symbol */
+ int type; /* type of symbol: either VAR or FNCT */
+ union {
+ double var; /* value of a VAR */
+ double (*fnctptr)(); /* value of a FNCT */
+ } value;
+ struct symrec *next; /* link field */
+};
+typedef struct symrec symrec;
+/* The symbol table: a chain of `struct symrec'. */
+extern symrec *sym_table;
+symrec *putsym ();
+symrec *getsym ();
+
+
+The new version of main includes a call to init_table, a
+function that initializes the symbol table. Here it is, and
+init_table as well:
+
+#include <stdio.h>
+main ()
+{
+ init_table ();
+ yyparse ();
+}
+yyerror (s) /* Called by yyparse on error */
+ char *s;
+{
+ printf ("%s\n", s);
+}
+struct init
+{
+ char *fname;
+ double (*fnct)();
+};
+struct init arith_fncts[]
+ = {
+ "sin", sin,
+ "cos", cos,
+ "atan", atan,
+ "ln", log,
+ "exp", exp,
+ "sqrt", sqrt,
+ 0, 0
+ };
+/* The symbol table: a chain of `struct symrec'. */
+symrec *sym_table = (symrec *)0;
+init_table () /* puts arithmetic functions in table. */
+{
+ int i;
+ symrec *ptr;
+ for (i = 0; arith_fncts[i].fname != 0; i++)
+ {
+ ptr = putsym (arith_fncts[i].fname, FNCT);
+ ptr->value.fnctptr = arith_fncts[i].fnct;
+ }
+}
+
+
+By simply editing the initialization list and adding the necessary include
+files, you can add additional functions to the calculator.
+Two important functions allow look-up and installation of symbols in the
+symbol table. The function putsym is passed a name and the type
+(VAR or FNCT) of the object to be installed. The object is
+linked to the front of the list, and a pointer to the object is returned.
+The function getsym is passed the name of the symbol to look up. If
+found, a pointer to that symbol is returned; otherwise zero is returned.
+
+The function yylex must now recognize variables, numeric values, and
+the single-character arithmetic operators. Strings of alphanumeric
+characters with a leading nondigit are recognized as either variables or
+functions depending on what the symbol table says about them.
+The string is passed to getsym for look up in the symbol table. If
+the name appears in the table, a pointer to its location and its type
+(VAR or FNCT) is returned to yyparse. If it is not
+already in the table, then it is installed as a VAR using
+putsym. Again, a pointer and its type (which must be VAR) is
+returned to yyparse.
+No change is needed in the handling of numeric values and arithmetic
+operators in yylex.
+
+#include <ctype.h>
+yylex ()
+{
+ int c;
+ /* Ignore whitespace, get first nonwhite character. */
+ while ((c = getchar ()) == ' ' || c == '\t');
+ if (c == EOF)
+ return 0;
+ /* Char starts a number => parse the number. */
+ if (c == '.' || isdigit (c))
+ {
+ ungetc (c, stdin);
+ scanf ("%lf", &yylval.val);
+ return NUM;
+ }
+ /* Char starts an identifier => read the name. */
+ if (isalpha (c))
+ {
+ symrec *s;
+ static char *symbuf = 0;
+ static int length = 0;
+ int i;
+ /* Initially make the buffer long enough
+ for a 40-character symbol name. */
+ if (length == 0)
+ length = 40, symbuf = (char *)malloc (length + 1);
+ i = 0;
+ do
+ {
+ /* If buffer is full, make it bigger. */
+ if (i == length)
+ {
+ length *= 2;
+ symbuf = (char *)realloc (symbuf, length + 1);
+ }
+ /* Add this character to the buffer. */
+ symbuf[i++] = c;
+ /* Get another character. */
+ c = getchar ();
+ }
+ while (c != EOF && isalnum (c));
+ ungetc (c, stdin);
+ symbuf[i] = '\0';
+ s = getsym (symbuf);
+ if (s == 0)
+ s = putsym (symbuf, VAR);
+ yylval.tptr = s;
+ return s->type;
+ }
+ /* Any other character is a token by itself. */
+ return c;
+}
+
+
+This program is both powerful and flexible. You may easily add new
+functions, and it is a simple job to modify this code to install predefined
+variables such as pi or e as well.
+
+Add some new functions from `math.h' to the initialization list.
+
+Add another array that contains constants and their values. Then
+modify init_table to add these constants to the symbol table.
+It will be easiest to give the constants type VAR.
+
+Make the program report an error if the user refers to an
+uninitialized variable in any way except to store a value in it.
+
+Bison takes as input a context-free grammar specification and produces a
+C-language function that recognizes correct instances of the grammar.
+The Bison grammar input file conventionally has a name ending in `.y'.
+
+
+
+The C declarations section contains macro definitions and
+declarations of functions and variables that are used in the actions in the
+grammar rules. These are copied to the beginning of the parser file so
+that they precede the definition of yyparse. You can use
+`#include' to get the declarations from a header file. If you don't
+need any C declarations, you may omit the `%{' and `%}'
+delimiters that bracket this section.
+
+
+
+The Bison declarations section contains declarations that define
+terminal and nonterminal symbols, specify precedence, and so on.
+In some simple grammars you may not need any declarations.
+See section Bison Declarations.
+
+
+
+The grammar rules section contains one or more Bison grammar
+rules, and nothing else. See section Syntax of Grammar Rules.
+There must always be at least one grammar rule, and the first
+`%%' (which precedes the grammar rules) may never be omitted even
+if it is the first thing in the file.
+
+
+
+The additional C code section is copied verbatim to the end of
+the parser file, just as the C declarations section is copied to
+the beginning. This is the most convenient place to put anything
+that you want to have in the parser file but which need not come before
+the definition of yyparse. For example, the definitions of
+yylex and yyerror often go here. See section Parser C-Language Interface.
+If the last section is empty, you may omit the `%%' that separates it
+from the grammar rules.
+The Bison parser itself contains many static variables whose names start
+with `yy' and many macros whose names start with `YY'. It is a
+good idea to avoid using any such names (except those documented in this
+manual) in the additional C code section of the grammar file.
+
+
+
+
+
+Symbols in Bison grammars represent the grammatical classifications
+of the language.
+A terminal symbol (also known as a token type) represents a
+class of syntactically equivalent tokens. You use the symbol in grammar
+rules to mean that a token in that class is allowed. The symbol is
+represented in the Bison parser by a numeric code, and the yylex
+function returns a token type code to indicate what kind of token has been
+read. You don't need to know what the code value is; you can use the
+symbol to stand for it.
+A nonterminal symbol stands for a class of syntactically equivalent
+groupings. The symbol name is used in writing grammar rules. By convention,
+it should be all lower case.
+Symbol names can contain letters, digits (not at the beginning),
+underscores and periods. Periods make sense only in nonterminals.
+There are two ways of writing terminal symbols in the grammar:
+
+
+A named token type is written with an identifier, like an
+identifier in C. By convention, it should be all upper case. Each
+such name must be defined with a Bison declaration such as
+%token. See section Token Type Names.
+
+
+
+
+A character token type (or literal token) is written in
+the grammar using the same syntax used in C for character constants;
+for example, '+' is a character token type. A character token
+type doesn't need to be declared unless you need to specify its
+semantic value data type (see section Data Types of Semantic Values), associativity, or
+precedence (see section Operator Precedence).
+By convention, a character token type is used only to represent a
+token that consists of that particular character. Thus, the token
+type '+' is used to represent the character `+' as a
+token. Nothing enforces this convention, but if you depart from it,
+your program will confuse other readers.
+All the usual escape sequences used in character literals in C can be
+used in Bison as well, but you must not use the null character as a
+character literal because its ASCII code, zero, is the code
+yylex returns for end-of-input (see section Calling Convention for yylex).
+
+
+How you choose to write a terminal symbol has no effect on its
+grammatical meaning. That depends only on where it appears in rules and
+on when the parser function returns that symbol.
+The value returned by yylex is always one of the terminal symbols
+(or 0 for end-of-input). Whichever way you write the token type in the
+grammar rules, you write it the same way in the definition of yylex.
+The numeric code for a character token type is simply the ASCII code for
+the character, so yylex can use the identical character constant to
+generate the requisite code. Each named token type becomes a C macro in
+the parser file, so yylex can use the name to stand for the code.
+(This is why periods don't make sense in terminal symbols.)
+See section Calling Convention for yylex.
+If yylex is defined in a separate file, you need to arrange for the
+token-type macro definitions to be available there. Use the `-d'
+option when you run Bison, so that it will write these macro definitions
+into a separate header file `name.tab.h' which you can include
+in the other source files that need it. See section Invoking Bison.
+The symbol error is a terminal symbol reserved for error recovery
+(see section Error Recovery); you shouldn't use it for any other purpose.
+In particular, yylex should never return this value.
+
+
+
+
+A Bison grammar rule has the following general form:
+
+result: components...
+ ;
+
+
+where result is the nonterminal symbol that this rule describes
+and components are various terminal and nonterminal symbols that
+are put together by this rule (see section Symbols, Terminal and Nonterminal).
+For example,
+
+exp: exp '+' exp
+ ;
+
+
+says that two groupings of type exp, with a `+' token in between,
+can be combined into a larger grouping of type exp.
+Whitespace in rules is significant only to separate symbols. You can add
+extra whitespace as you wish.
+Scattered among the components can be actions that determine
+the semantics of the rule. An action looks like this:
+
+{C statements}
+
+
+Usually there is only one action and it follows the components.
+See section Actions.
+
+Multiple rules for the same result can be written separately or can
+be joined with the vertical-bar character `|' as follows:
+
+They are still considered distinct rules even when joined in this way.
+If components in a rule is empty, it means that result can
+match the empty string. For example, here is how to define a
+comma-separated sequence of zero or more exp groupings:
+
+
+A rule is called recursive when its result nonterminal appears
+also on its right hand side. Nearly all Bison grammars need to use
+recursion, because that is the only way to define a sequence of any number
+of somethings. Consider this recursive definition of a comma-separated
+sequence of one or more expressions:
+
+expseq1: exp
+ | expseq1 ',' exp
+ ;
+
+
+
+
+Since the recursive use of expseq1 is the leftmost symbol in the
+right hand side, we call this left recursion. By contrast, here
+the same construct is defined using right recursion:
+
+expseq1: exp
+ | exp ',' expseq1
+ ;
+
+
+Any kind of sequence can be defined using either left recursion or
+right recursion, but you should always use left recursion, because it
+can parse a sequence of any number of elements with bounded stack
+space. Right recursion uses up space on the Bison stack in proportion
+to the number of elements in the sequence, because all the elements
+must be shifted onto the stack before the rule can be applied even
+once. See section The Bison Parser Algorithm, for
+further explanation of this.
+
+Indirect or mutual recursion occurs when the result of the
+rule does not appear directly on its right hand side, but does appear
+in rules for other nonterminals which do appear on its right hand
+side.
+For example:
+
+
+
+The grammar rules for a language determine only the syntax. The semantics
+are determined by the semantic values associated with various tokens and
+groupings, and by the actions taken when various groupings are recognized.
+For example, the calculator calculates properly because the value
+associated with each expression is the proper number; it adds properly
+because the action for the grouping `x + y' is to add
+the numbers associated with x and y.
+
+
+
+
+
+In a simple program it may be sufficient to use the same data type for
+the semantic values of all language constructs. This was true in the
+RPN and infix calculator examples (see section Reverse Polish Notation Calculator).
+Bison's default is to use type int for all semantic values. To
+specify some other type, define YYSTYPE as a macro, like this:
+
+#define YYSTYPE double
+
+
+This macro definition must go in the C declarations section of the grammar
+file (see section Outline of a Bison Grammar).
+
+In most programs, you will need different data types for different kinds
+of tokens and groupings. For example, a numeric constant may need type
+int or long, while a string constant needs type char *,
+and an identifier might need a pointer to an entry in the symbol table.
+To use more than one data type for semantic values in one parser, Bison
+requires you to do two things:
+
+
+Specify the entire collection of possible data types, with the
+%union Bison declaration (see section The Collection of Value Types).
+
+Choose one of those types for each symbol (terminal or nonterminal)
+for which semantic values are used. This is done for tokens with the
+%token Bison declaration (see section Token Type Names) and for groupings
+with the %type Bison declaration (see section Nonterminal Symbols).
+
+
+
+
+An action accompanies a syntactic rule and contains C code to be executed
+each time an instance of that rule is recognized. The task of most actions
+is to compute a semantic value for the grouping built by the rule from the
+semantic values associated with tokens or smaller groupings.
+An action consists of C statements surrounded by braces, much like a
+compound statement in C. It can be placed at any position in the rule; it
+is executed at that position. Most rules have just one action at the end
+of the rule, following all the components. Actions in the middle of a rule
+are tricky and used only for special purposes (see section Actions in Mid-Rule).
+The C code in an action can refer to the semantic values of the components
+matched by the rule with the construct $n, which stands for
+the value of the nth component. The semantic value for the grouping
+being constructed is $$. (Bison translates both of these constructs
+into array element references when it copies the actions into the parser
+file.)
+Here is a typical example:
+
+exp: ...
+ | exp '+' exp
+ { $$ = $1 + $3; }
+
+
+This rule constructs an exp from two smaller exp groupings
+connected by a plus-sign token. In the action, $1 and $3
+refer to the semantic values of the two component exp groupings,
+which are the first and third symbols on the right hand side of the rule.
+The sum is stored into $$ so that it becomes the semantic value of
+the addition-expression just recognized by the rule. If there were a
+useful semantic value associated with the `+' token, it could be
+referred to as $2.
+
+If you don't specify an action for a rule, Bison supplies a default:
+$$ = $1. Thus, the value of the first symbol in the rule becomes
+the value of the whole rule. Of course, the default rule is valid only
+if the two data types match. There is no meaningful default action for
+an empty rule; every empty rule must have an explicit action unless the
+rule's value does not matter.
+$n with n zero or negative is allowed for reference
+to tokens and groupings on the stack before those that match the
+current rule. This is a very risky practice, and to use it reliably
+you must be certain of the context in which the rule is applied. Here
+is a case in which you can use this reliably:
+
+
+
+If you have chosen a single data type for semantic values, the $$
+and $n constructs always have that data type.
+If you have used %union to specify a variety of data types, then you
+must declare a choice among these types for each terminal or nonterminal
+symbol that can have a semantic value. Then each time you use $$ or
+$n, its data type is determined by which symbol it refers to
+in the rule. In this example,
+
+exp: ...
+ | exp '+' exp
+ { $$ = $1 + $3; }
+
+
+$1 and $3 refer to instances of exp, so they all
+have the data type declared for the nonterminal symbol exp. If
+$2 were used, it would have the data type declared for the
+terminal symbol '+', whatever that might be.
+Alternatively, you can specify the data type when you refer to the value,
+by inserting `<type>' after the `$' at the beginning of the
+reference. For example, if you have defined types as shown here:
+
+%union {
+ int itype;
+ double dtype;
+}
+
+
+then you can write $<itype>1 to refer to the first subunit of the
+rule as an integer, or $<dtype>1 to refer to it as a double.
+
+
+
+Occasionally it is useful to put an action in the middle of a rule.
+These actions are written just like usual end-of-rule actions, but they
+are executed before the parser even recognizes the following components.
+A mid-rule action may refer to the components preceding it using
+$n, but it may not refer to subsequent components because
+it is run before they are parsed.
+The mid-rule action itself counts as one of the components of the rule.
+This makes a difference when there is another action later in the same rule
+(and usually there is another at the end): you have to count the actions
+along with the symbols when working out which number n to use in
+$n.
+The mid-rule action can also have a semantic value. The action can set
+its value with an assignment to $$, and actions later in the rule
+can refer to the value using $n. Since there is no symbol
+to name the action, there is no way to declare a data type for the value
+in advance, so you must use the `$<...>' construct to specify a
+data type each time you refer to this value.
+There is no way to set the value of the entire rule with a mid-rule
+action, because assignments to $$ do not have that effect. The
+only way to set the value for the entire rule is with an ordinary action
+at the end of the rule.
+Here is an example from a hypothetical compiler, handling a let
+statement that looks like `let (variable) statement' and
+serves to create a variable named variable temporarily for the
+duration of statement. To parse this construct, we must put
+variable into the symbol table while statement is parsed, then
+remove it afterward. Here is how it is done:
+
+As soon as `let (variable)' has been recognized, the first
+action is run. It saves a copy of the current semantic context (the
+list of accessible variables) as its semantic value, using alternative
+context in the data-type union. Then it calls
+declare_variable to add the new variable to that list. Once the
+first action is finished, the embedded statement stmt can be
+parsed. Note that the mid-rule action is component number 5, so the
+`stmt' is component number 6.
+After the embedded statement is parsed, its semantic value becomes the
+value of the entire let-statement. Then the semantic value from the
+earlier action is used to restore the prior list of variables. This
+removes the temporary let-variable from the list so that it won't
+appear to exist while the rest of the program is parsed.
+Taking action before a rule is completely recognized often leads to
+conflicts since the parser must commit to a parse in order to execute the
+action. For example, the following two rules, without mid-rule actions,
+can coexist in a working parser because the parser can shift the open-brace
+token and look at what follows before deciding whether there is a
+declaration or not:
+
+Now the parser is forced to decide whether to run the mid-rule action
+when it has read no farther than the open-brace. In other words, it
+must commit to using one rule or the other, without sufficient
+information to do it correctly. (The open-brace token is what is called
+the look-ahead token at this time, since the parser is still
+deciding what to do about it. See section Look-Ahead Tokens.)
+You might think that you could correct the problem by putting identical
+actions into the two rules, like this:
+
+But this does not help, because Bison does not realize that the two actions
+are identical. (Bison never tries to understand the C code in an action.)
+If the grammar is such that a declaration can be distinguished from a
+statement by the first token (which is true in C), then one solution which
+does work is to put the action after the open-brace, like this:
+
+Now the first token of the following declaration or statement,
+which would in any case tell Bison which rule to use, can still do so.
+Another solution is to bury the action inside a nonterminal symbol which
+serves as a subroutine:
+
+Now Bison can execute the action in the rule for subroutine without
+deciding which rule for compound it will eventually use. Note that
+the action is now at the end of its rule. Any mid-rule action can be
+converted to an end-of-rule action in this way, and this is what Bison
+actually does to implement mid-rule actions.
+
+
+
+The Bison declarations section of a Bison grammar defines the symbols
+used in formulating the grammar and the data types of semantic values.
+See section Symbols, Terminal and Nonterminal.
+All token type names (but not single-character literal tokens such as
+'+' and '*') must be declared. Nonterminal symbols must be
+declared if you need to specify which data type to use for the semantic
+value (see section More Than One Value Type).
+The first rule in the file also specifies the start symbol, by default.
+If you want some other symbol to be the start symbol, you must declare
+it explicitly (see section Languages and Context-Free Grammars).
+
+
+
+
+The basic way to declare a token type name (terminal symbol) is as follows:
+
+%token name
+
+
+Bison will convert this into a #define directive in
+the parser, so that the function yylex (if it is in this file)
+can use the name name to stand for this token type's code.
+Alternatively, you can use %left, %right, or %nonassoc
+instead of %token, if you wish to specify precedence.
+See section Operator Precedence.
+You can explicitly specify the numeric code for a token type by appending
+an integer value in the field immediately following the token name:
+
+%token NUM 300
+
+
+It is generally best, however, to let Bison choose the numeric codes for
+all token types. Bison will automatically select codes that don't conflict
+with each other or with ASCII characters.
+In the event that the stack type is a union, you must augment the
+%token or other token declaration to include the data type
+alternative delimited by angle-brackets (see section More Than One Value Type).
+For example:
+
+%union { /* define stack type */
+ double val;
+ symrec *tptr;
+}
+%token <val> NUM /* define token NUM and its type */
+
+
+
+
+Use the %left, %right or %nonassoc declaration to
+declare a token and specify its precedence and associativity, all at
+once. These are called precedence declarations.
+See section Operator Precedence, for general information on operator precedence.
+The syntax of a precedence declaration is the same as that of
+%token: either
+
+%left symbols...
+
+
+or
+
+%left <type> symbols...
+
+
+And indeed any of these declarations serves the purposes of %token.
+But in addition, they specify the associativity and relative precedence for
+all the symbols:
+
+
+The associativity of an operator op determines how repeated uses
+of the operator nest: whether `xopyop
+z' is parsed by grouping x with y first or by
+grouping y with z first. %left specifies
+left-associativity (grouping x with y first) and
+%right specifies right-associativity (grouping y with
+z first). %nonassoc specifies no associativity, which
+means that `xopyopz' is
+considered a syntax error.
+
+The precedence of an operator determines how it nests with other operators.
+All the tokens declared in a single precedence declaration have equal
+precedence and nest together according to their associativity.
+When two tokens declared in different precedence declarations associate,
+the one declared later has the higher precedence and is grouped first.
+
+
+
+
+The %union declaration specifies the entire collection of possible
+data types for semantic values. The keyword %union is followed by a
+pair of braces containing the same thing that goes inside a union in
+C.
+For example:
+
+%union {
+ double val;
+ symrec *tptr;
+}
+
+
+This says that the two alternative types are double and symrec
+*. They are given names val and tptr; these names are used
+in the %token and %type declarations to pick one of the types
+for a terminal or nonterminal symbol (see section Nonterminal Symbols).
+Note that, unlike making a union declaration in C, you do not write
+a semicolon after the closing brace.
+
+
+
+
+When you use %union to specify multiple value types, you must
+declare the value type of each nonterminal symbol for which values are
+used. This is done with a %type declaration, like this:
+
+%type <type> nonterminal...
+
+
+Here nonterminal is the name of a nonterminal symbol, and type
+is the name given in the %union to the alternative that you want
+(see section The Collection of Value Types). You can give any number of nonterminal symbols in
+the same %type declaration, if they have the same value type. Use
+spaces to separate the symbol names.
+
+
+
+
+
+
+Bison normally warns if there are any conflicts in the grammar
+(see section Shift/Reduce Conflicts), but most real grammars have harmless shift/reduce
+conflicts which are resolved in a predictable way and would be difficult to
+eliminate. It is desirable to suppress the warning about these conflicts
+unless the number of conflicts changes. You can do this with the
+%expect declaration.
+The declaration looks like this:
+
+%expect n
+
+
+Here n is a decimal integer. The declaration says there should be no
+warning if there are n shift/reduce conflicts and no reduce/reduce
+conflicts. The usual warning is given if there are either more or fewer
+conflicts, or if there are any reduce/reduce conflicts.
+In general, using %expect involves these steps:
+
+
+Compile your grammar without %expect. Use the `-v' option
+to get a verbose list of where the conflicts occur. Bison will also
+print the number of conflicts.
+
+Check each of the conflicts to make sure that Bison's default
+resolution is what you really want. If not, rewrite the grammar and
+go back to the beginning.
+
+Add an %expect declaration, copying the number n from the
+number which Bison printed.
+
+
+Now Bison will stop annoying you about the conflicts you have checked, but
+it will warn you again if changes in the grammar result in additional
+conflicts.
+
+
+
+
+
+Bison assumes by default that the start symbol for the grammar is the first
+nonterminal specified in the grammar specification section. The programmer
+may override this restriction with the %start declaration as follows:
+
+
+
+
+A reentrant program is one which does not alter in the course of
+execution; in other words, it consists entirely of pure (read-only)
+code. Reentrancy is important whenever asynchronous execution is possible;
+for example, a nonreentrant program may not be safe to call from a signal
+handler. In systems with multiple threads of control, a nonreentrant
+program must be called only within interlocks.
+The Bison parser is not normally a reentrant program, because it uses
+statically allocated variables for communication with yylex. These
+variables include yylval and yylloc.
+The Bison declaration %pure_parser says that you want the parser
+to be reentrant. It looks like this:
+
+%pure_parser
+
+
+The effect is that the two communication variables become local
+variables in yyparse, and a different calling convention is used
+for the lexical analyzer function yylex. See section Calling Conventions for Pure Parsers, for the details of this. The
+variable yynerrs also becomes local in yyparse
+(see section The Error Reporting Function yyerror).
+The convention for calling yyparse itself is unchanged.
+
+Declare a terminal symbol (token type name) with no precedence
+or associativity specified (see section Token Type Names).
+
%right
+
+Declare a terminal symbol (token type name) that is right-associative
+(see section Operator Precedence).
+
%left
+
+Declare a terminal symbol (token type name) that is left-associative
+(see section Operator Precedence).
+
%nonassoc
+
+Declare a terminal symbol (token type name) that is nonassociative
+(using it in a way that would be associative is a syntax error)
+(see section Operator Precedence).
+
%type
+
+Declare the type of semantic values for a nonterminal symbol
+(see section Nonterminal Symbols).
+
%start
+
+Specify the grammar's start symbol (see section The Start-Symbol).
+
+Most programs that use Bison parse only one language and therefore contain
+only one Bison parser. But what if you want to parse more than one
+language with the same program? Then you need to avoid a name conflict
+between different definitions of yyparse, yylval, and so on.
+The easy way to do this is to use the option `-p prefix'
+(see section Invoking Bison). This renames the interface functions and
+variables of the Bison parser to start with prefix instead of
+`yy'. You can use this to give each parser distinct names that do
+not conflict.
+The precise list of symbols renamed is yyparse, yylex,
+yyerror, yynerrs, yylval, yychar and
+yydebug. For example, if you use `-p c', the names become
+cparse, clex, and so on.
+All the other variables and macros associated with Bison are not
+renamed. These others are not global; there is no conflict if the same
+name is used in different parsers. For example, YYSTYPE is not
+renamed, but defining this in different ways in different parsers causes
+no trouble (see section Data Types of Semantic Values).
+The `-p' option works by adding macro definitions to the beginning
+of the parser source file, defining yyparse as
+prefixparse, and so on. This effectively substitutes one
+name for the other in the entire parser file.
+
+
+
+The Bison parser is actually a C function named yyparse. Here we
+describe the interface conventions of yyparse and the other
+functions that it needs to use.
+Keep in mind that the parser uses many C identifiers starting with
+`yy' and `YY' for internal purposes. If you use such an
+identifier (aside from those in this manual) in an action or in additional
+C code in the grammar file, you are likely to run into trouble.
+
+
+You call the function yyparse to cause parsing to occur. This
+function reads tokens, executes actions, and ultimately returns when it
+encounters end-of-input or an unrecoverable syntax error. You can also
+write an action which directs yyparse to return immediately without
+reading further.
+The value returned by yyparse is 0 if parsing was successful (return
+is due to end-of-input).
+The value is 1 if parsing failed (return is due to a syntax error).
+In an action, you can cause immediate return from yyparse by using
+these macros:
+
+
YYACCEPT
+
+
+Return immediately with value 0 (to report success).
+
YYABORT
+
+
+Return immediately with value 1 (to report failure).
+
+
+
+The lexical analyzer function, yylex, recognizes tokens from
+the input stream and returns them to the parser. Bison does not create
+this function automatically; you must write it so that yyparse can
+call it. The function is sometimes referred to as a lexical scanner.
+In simple programs, yylex is often defined at the end of the Bison
+grammar file. If yylex is defined in a separate source file, you
+need to arrange for the token-type macro definitions to be available there.
+To do this, use the `-d' option when you run Bison, so that it will
+write these macro definitions into a separate header file
+`name.tab.h' which you can include in the other source files
+that need it. See section Invoking Bison.
+
+The value that yylex returns must be the numeric code for the type
+of token it has just found, or 0 for end-of-input.
+When a token is referred to in the grammar rules by a name, that name
+in the parser file becomes a C macro whose definition is the proper
+numeric code for that token type. So yylex can use the name
+to indicate that type. See section Symbols, Terminal and Nonterminal.
+When a token is referred to in the grammar rules by a character literal,
+the numeric code for that character is also the code for the token type.
+So yylex can simply return that character code. The null character
+must not be used this way, because its code is zero and that is what
+signifies end-of-input.
+Here is an example showing these things:
+
+yylex ()
+{
+ ...
+ if (c == EOF) /* Detect end of file. */
+ return 0;
+ ...
+ if (c == '+' || c == '-')
+ return c; /* Assume token type for `+' is '+'. */
+ ...
+ return INT; /* Return the type of the token. */
+ ...
+}
+
+
+This interface has been designed so that the output from the lex
+utility can be used without change as the definition of yylex.
+
+
+In an ordinary (nonreentrant) parser, the semantic value of the token must
+be stored into the global variable yylval. When you are using
+just one data type for semantic values, yylval has that type.
+Thus, if the type is int (the default), you might write this in
+yylex:
+
+ ...
+ yylval = value; /* Put value onto Bison stack. */
+ return INT; /* Return the type of the token. */
+ ...
+
+
+When you are using multiple data types, yylval's type is a union
+made from the %union declaration (see section The Collection of Value Types). So when
+you store a token's value, you must use the proper member of the union.
+If the %union declaration looks like this:
+
+
+If you are using the `@n'-feature (see section Special Features for Use in Actions) in
+actions to keep track of the textual locations of tokens and groupings,
+then you must provide this information in yylex. The function
+yyparse expects to find the textual location of a token just parsed
+in the global variable yylloc. So yylex must store the
+proper data in that variable. The value of yylloc is a structure
+and you need only initialize the members that are going to be used by the
+actions. The four members are called first_line,
+first_column, last_line and last_column. Note that
+the use of this feature makes the parser noticeably slower.
+
+The data type of yylloc has the name YYLTYPE.
+
+When you use the Bison declaration %pure_parser to request a
+pure, reentrant parser, the global communication variables yylval
+and yylloc cannot be used. (See section A Pure (Reentrant) Parser.) In such parsers the two global variables are replaced by
+pointers passed as arguments to yylex. You must declare them as
+shown here, and pass the information back by storing it through those
+pointers.
+
+yylex (lvalp, llocp)
+ YYSTYPE *lvalp;
+ YYLTYPE *llocp;
+{
+ ...
+ *lvalp = value; /* Put value onto Bison stack. */
+ return INT; /* Return the type of the token. */
+ ...
+}
+
+
+If the grammar file does not use the `@' constructs to refer to
+textual positions, then the type YYLTYPE will not be defined. In
+this case, omit the second argument; yylex will be called with
+only one argument.
+
+You can pass parameter information to a reentrant parser in a reentrant
+way. Define the macro YYPARSE_PARAM as a variable name. The
+resulting yyparse function then accepts one argument, of type
+void *, with that name.
+When you call yyparse, pass the address of an object, casting the
+address to void *. The grammar actions can refer to the contents
+of the object by casting the pointer value back to its proper type and
+then dereferencing it. Here's an example. Write this in the parser:
+
+%{
+struct parser_control
+{
+ int nastiness;
+ int randomness;
+};
+#define YYPARSE_PARAM parm
+%}
+
+
+Then call the parser like this:
+
+struct parser_control
+{
+ int nastiness;
+ int randomness;
+};
+...
+{
+ struct parser_control foo;
+ ... /* Store proper data in foo. */
+ value = yyparse ((void *) &foo);
+ ...
+}
+
+
+In the grammar actions, use expressions like this to refer to the data:
+
+((struct parser_control *) parm)->randomness
+
+
+
+If you wish to pass the additional parameter data to yylex,
+define the macro YYLEX_PARAM just like YYPARSE_PARAM, as
+shown here:
+
+%{
+struct parser_control
+{
+ int nastiness;
+ int randomness;
+};
+#define YYPARSE_PARAM parm
+#define YYLEX_PARAM parm
+%}
+
+
+You should then define yylex to accept one additional
+argument--the value of parm. (This makes either two or three
+arguments in total, depending on whether an argument of type
+YYLTYPE is passed.) You can declare the argument as a pointer to
+the proper object type, or you can declare it as void * and
+access the contents as shown above.
+
+
+
+
+
+The Bison parser detects a parse error or syntax error
+whenever it reads a token which cannot satisfy any syntax rule. A
+action in the grammar can also explicitly proclaim an error, using the
+macro YYERROR (see section Special Features for Use in Actions).
+The Bison parser expects to report the error by calling an error
+reporting function named yyerror, which you must supply. It is
+called by yyparse whenever a syntax error is found, and it
+receives one argument. For a parse error, the string is normally
+"parse error".
+
+If you define the macro YYERROR_VERBOSE in the Bison declarations
+section (see section The Bison Declarations Section), then Bison provides a more verbose
+and specific error message string instead of just plain "parse
+error". It doesn't matter what definition you use for
+YYERROR_VERBOSE, just whether you define it.
+The parser can detect one other kind of error: stack overflow. This
+happens when the input contains constructions that are very deeply
+nested. It isn't likely you will encounter this, since the Bison
+parser extends its stack automatically up to a very large limit. But
+if overflow happens, yyparse calls yyerror in the usual
+fashion, except that the argument string is "parser stack
+overflow".
+The following definition suffices in simple programs:
+
+After yyerror returns to yyparse, the latter will attempt
+error recovery if you have written suitable error recovery grammar rules
+(see section Error Recovery). If recovery is impossible, yyparse will
+immediately return 1.
+
+The variable yynerrs contains the number of syntax errors
+encountered so far. Normally this variable is global; but if you
+request a pure parser (see section A Pure (Reentrant) Parser) then it is a local variable
+which only the actions can access.
+
+
+Unshift a token. This macro is allowed only for rules that reduce
+a single value, and only when there is no look-ahead token.
+It installs a look-ahead token with token type token and
+semantic value value; then it discards the value that was
+going to be reduced by this rule.
+If the macro is used when it is not valid, such as when there is
+a look-ahead token already, then it reports a syntax error with
+a message `cannot back up' and performs ordinary error
+recovery.
+In either case, the rest of the action is not executed.
+
`YYEMPTY'
+
+
+Value stored in yychar when there is no look-ahead token.
+
`YYERROR;'
+
+
+Cause an immediate syntax error. This statement initiates error
+recovery just as if the parser itself had detected an error; however, it
+does not call yyerror, and does not print any message. If you
+want to print an error message, call yyerror explicitly before
+the `YYERROR;' statement. See section Error Recovery.
+
`YYRECOVERING'
+
+This macro stands for an expression that has the value 1 when the parser
+is recovering from a syntax error, and 0 the rest of the time.
+See section Error Recovery.
+
`yychar'
+
+Variable containing the current look-ahead token. (In a pure parser,
+this is actually a local variable within yyparse.) When there is
+no look-ahead token, the value YYEMPTY is stored in the variable.
+See section Look-Ahead Tokens.
+
`yyclearin;'
+
+Discard the current look-ahead token. This is useful primarily in
+error rules. See section Error Recovery.
+
`yyerrok;'
+
+Resume generating error messages immediately for subsequent syntax
+errors. This is useful primarily in error rules.
+See section Error Recovery.
+
`@n'
+
+
+Acts like a structure variable containing information on the line
+numbers and column numbers of the nth component of the current
+rule. The structure has four members, like this:
+
+struct {
+ int first_line, last_line;
+ int first_column, last_column;
+};
+
+Thus, to get the starting line number of the third component, use
+`@3.first_line'.
+In order for the members of this structure to contain valid information,
+you must make yylex supply this information about each token.
+If you need only certain members, then yylex need only fill in
+those members.
+The use of this feature makes the parser noticeably slower.
+
+
+
+
+
+
+
+As Bison reads tokens, it pushes them onto a stack along with their
+semantic values. The stack is called the parser stack. Pushing a
+token is traditionally called shifting.
+For example, suppose the infix calculator has read `1 + 5 *', with a
+`3' to come. The stack will have four elements, one for each token
+that was shifted.
+But the stack does not always have an element for each token read. When
+the last n tokens and groupings shifted match the components of a
+grammar rule, they can be combined according to that rule. This is called
+reduction. Those tokens and groupings are replaced on the stack by a
+single grouping whose symbol is the result (left hand side) of that rule.
+Running the rule's action is part of the process of reduction, because this
+is what computes the semantic value of the resulting grouping.
+For example, if the infix calculator's parser stack contains this:
+
+1 + 5 * 3
+
+
+and the next input token is a newline character, then the last three
+elements can be reduced to 15 via the rule:
+
+expr: expr '*' expr;
+
+
+Then the stack contains just these three elements:
+
+1 + 15
+
+
+At this point, another reduction can be made, resulting in the single value
+16. Then the newline token can be shifted.
+The parser tries, by shifts and reductions, to reduce the entire input down
+to a single grouping whose symbol is the grammar's start-symbol
+(see section Languages and Context-Free Grammars).
+This kind of parser is known in the literature as a bottom-up parser.
+
+
+The Bison parser does not always reduce immediately as soon as the
+last n tokens and groupings match a rule. This is because such a
+simple strategy is inadequate to handle most languages. Instead, when a
+reduction is possible, the parser sometimes "looks ahead" at the next
+token in order to decide what to do.
+When a token is read, it is not immediately shifted; first it becomes the
+look-ahead token, which is not on the stack. Now the parser can
+perform one or more reductions of tokens and groupings on the stack, while
+the look-ahead token remains off to the side. When no more reductions
+should take place, the look-ahead token is shifted onto the stack. This
+does not mean that all possible reductions have been done; depending on the
+token type of the look-ahead token, some rules may choose to delay their
+application.
+Here is a simple case where look-ahead is needed. These three rules define
+expressions which contain binary addition operators and postfix unary
+factorial operators (`!'), and allow parentheses for grouping.
+
+expr: term '+' expr
+ | term
+ ;
+term: '(' expr ')'
+ | term '!'
+ | NUMBER
+ ;
+
+
+Suppose that the tokens `1 + 2' have been read and shifted; what
+should be done? If the following token is `)', then the first three
+tokens must be reduced to form an expr. This is the only valid
+course, because shifting the `)' would produce a sequence of symbols
+term ')', and no rule allows this.
+If the following token is `!', then it must be shifted immediately so
+that `2 !' can be reduced to make a term. If instead the
+parser were to reduce before shifting, `1 + 2' would become an
+expr. It would then be impossible to shift the `!' because
+doing so would produce on the stack the sequence of symbols expr
+'!'. No rule allows that sequence.
+
+The current look-ahead token is stored in the variable yychar.
+See section Special Features for Use in Actions.
+
+
+
+
+
+Suppose we are parsing a language which has if-then and if-then-else
+statements, with a pair of rules like this:
+
+if_stmt:
+ IF expr THEN stmt
+ | IF expr THEN stmt ELSE stmt
+ ;
+
+
+Here we assume that IF, THEN and ELSE are
+terminal symbols for specific keyword tokens.
+When the ELSE token is read and becomes the look-ahead token, the
+contents of the stack (assuming the input is valid) are just right for
+reduction by the first rule. But it is also legitimate to shift the
+ELSE, because that would lead to eventual reduction by the second
+rule.
+This situation, where either a shift or a reduction would be valid, is
+called a shift/reduce conflict. Bison is designed to resolve
+these conflicts by choosing to shift, unless otherwise directed by
+operator precedence declarations. To see the reason for this, let's
+contrast it with the other alternative.
+Since the parser prefers to shift the ELSE, the result is to attach
+the else-clause to the innermost if-statement, making these two inputs
+equivalent:
+
+if x then if y then win (); else lose;
+if x then do; if y then win (); else lose; end;
+
+
+But if the parser chose to reduce when possible rather than shift, the
+result would be to attach the else-clause to the outermost if-statement,
+making these two inputs equivalent:
+
+if x then if y then win (); else lose;
+if x then do; if y then win (); end; else lose;
+
+
+The conflict exists because the grammar as written is ambiguous: either
+parsing of the simple nested if-statement is legitimate. The established
+convention is that these ambiguities are resolved by attaching the
+else-clause to the innermost if-statement; this is what Bison accomplishes
+by choosing to shift rather than reduce. (It would ideally be cleaner to
+write an unambiguous grammar, but that is very hard to do in this case.)
+This particular ambiguity was first encountered in the specifications of
+Algol 60 and is called the "dangling else" ambiguity.
+To avoid warnings from Bison about predictable, legitimate shift/reduce
+conflicts, use the %expect n declaration. There will be no
+warning as long as the number of shift/reduce conflicts is exactly n.
+See section Suppressing Conflict Warnings.
+The definition of if_stmt above is solely to blame for the
+conflict, but the conflict does not actually appear without additional
+rules. Here is a complete Bison input file that actually manifests the
+conflict:
+
+%token IF THEN ELSE variable
+%%
+stmt: expr
+ | if_stmt
+ ;
+if_stmt:
+ IF expr THEN stmt
+ | IF expr THEN stmt ELSE stmt
+ ;
+expr: variable
+ ;
+
+
+
+Another situation where shift/reduce conflicts appear is in arithmetic
+expressions. Here shifting is not always the preferred resolution; the
+Bison declarations for operator precedence allow you to specify when to
+shift and when to reduce.
+
+Suppose the parser has seen the tokens `1', `-' and `2';
+should it reduce them via the rule for the addition operator? It depends
+on the next token. Of course, if the next token is `)', we must
+reduce; shifting is invalid because no single rule can reduce the token
+sequence `- 2 )' or anything starting with that. But if the next
+token is `*' or `<', we have a choice: either shifting or
+reduction would allow the parse to complete, but with different
+results.
+To decide which one Bison should do, we must consider the
+results. If the next operator token op is shifted, then it
+must be reduced first in order to permit another opportunity to
+reduce the sum. The result is (in effect) `1 - (2
+op 3)'. On the other hand, if the subtraction is reduced
+before shifting op, the result is `(1 - 2) op
+3'. Clearly, then, the choice of shift or reduce should depend
+on the relative precedence of the operators `-' and
+op: `*' should be shifted first, but not `<'.
+
+What about input such as `1 - 2 - 5'; should this be
+`(1 - 2) - 5' or should it be `1 - (2 - 5)'? For
+most operators we prefer the former, which is called left
+association. The latter alternative, right association, is
+desirable for assignment operators. The choice of left or right
+association is a matter of whether the parser chooses to shift or
+reduce when the stack contains `1 - 2' and the look-ahead
+token is `-': shifting makes right-associativity.
+
+
+
+
+Bison allows you to specify these choices with the operator precedence
+declarations %left and %right. Each such declaration
+contains a list of tokens, which are operators whose precedence and
+associativity is being declared. The %left declaration makes all
+those operators left-associative and the %right declaration makes
+them right-associative. A third alternative is %nonassoc, which
+declares that it is a syntax error to find the same operator twice "in a
+row".
+The relative precedence of different operators is controlled by the
+order in which they are declared. The first %left or
+%right declaration in the file declares the operators whose
+precedence is lowest, the next such declaration declares the operators
+whose precedence is a little higher, and so on.
+
+In our example, we would want the following declarations:
+
+%left '<'
+%left '-'
+%left '*'
+
+
+In a more complete example, which supports other operators as well, we
+would declare them in groups of equal precedence. For example, '+' is
+declared with '-':
+
+%left '<' '>' '=' NE LE GE
+%left '+' '-'
+%left '*' '/'
+
+
+(Here NE and so on stand for the operators for "not equal"
+and so on. We assume that these tokens are more than one character long
+and therefore are represented by names, not character literals.)
+
+The first effect of the precedence declarations is to assign precedence
+levels to the terminal symbols declared. The second effect is to assign
+precedence levels to certain rules: each rule gets its precedence from the
+last terminal symbol mentioned in the components. (You can also specify
+explicitly the precedence of a rule. See section Context-Dependent Precedence.)
+Finally, the resolution of conflicts works by comparing the
+precedence of the rule being considered with that of the
+look-ahead token. If the token's precedence is higher, the
+choice is to shift. If the rule's precedence is higher, the
+choice is to reduce. If they have equal precedence, the choice
+is made based on the associativity of that precedence level. The
+verbose output file made by `-v' (see section Invoking Bison) says
+how each conflict was resolved.
+Not all rules and not all tokens have precedence. If either the rule or
+the look-ahead token has no precedence, then the default is to shift.
+
+
+
+
+
+
+Often the precedence of an operator depends on the context. This sounds
+outlandish at first, but it is really very common. For example, a minus
+sign typically has a very high precedence as a unary operator, and a
+somewhat lower precedence (lower than multiplication) as a binary operator.
+The Bison precedence declarations, %left, %right and
+%nonassoc, can only be used once for a given token; so a token has
+only one precedence declared in this way. For context-dependent
+precedence, you need to use an additional mechanism: the %prec
+modifier for rules.
+The %prec modifier declares the precedence of a particular rule by
+specifying a terminal symbol whose precedence should be used for that rule.
+It's not necessary for that symbol to appear otherwise in the rule. The
+modifier's syntax is:
+
+%prec terminal-symbol
+
+
+and it is written after the components of the rule. Its effect is to
+assign the rule the precedence of terminal-symbol, overriding
+the precedence that would be deduced for it in the ordinary way. The
+altered rule precedence then affects how conflicts involving that rule
+are resolved (see section Operator Precedence).
+Here is how %prec solves the problem of unary minus. First, declare
+a precedence for a fictitious terminal symbol named UMINUS. There
+are no tokens of this type, but the symbol serves to stand for its
+precedence:
+
+...
+%left '+' '-'
+%left '*'
+%left UMINUS
+
+
+Now the precedence of UMINUS can be used in specific rules:
+
+
+
+
+The function yyparse is implemented using a finite-state machine.
+The values pushed on the parser stack are not simply token type codes; they
+represent the entire sequence of terminal and nonterminal symbols at or
+near the top of the stack. The current state collects all the information
+about previous input which is relevant to deciding what to do next.
+Each time a look-ahead token is read, the current parser state together
+with the type of look-ahead token are looked up in a table. This table
+entry can say, "Shift the look-ahead token." In this case, it also
+specifies the new parser state, which is pushed onto the top of the
+parser stack. Or it can say, "Reduce using rule number n."
+This means that a certain number of tokens or groupings are taken off
+the top of the stack, and replaced by one grouping. In other words,
+that number of states are popped from the stack, and one new state is
+pushed.
+There is one other alternative: the table can say that the look-ahead token
+is erroneous in the current state. This causes error processing to begin
+(see section Error Recovery).
+
+
+
+A reduce/reduce conflict occurs if there are two or more rules that apply
+to the same sequence of input. This usually indicates a serious error
+in the grammar.
+For example, here is an erroneous attempt to define a sequence
+of zero or more word groupings.
+
+The error is an ambiguity: there is more than one way to parse a single
+word into a sequence. It could be reduced to a
+maybeword and then into a sequence via the second rule.
+Alternatively, nothing-at-all could be reduced into a sequence
+via the first rule, and this could be combined with the word
+using the third rule for sequence.
+There is also more than one way to reduce nothing-at-all into a
+sequence. This can be done directly via the first rule,
+or indirectly via maybeword and then the second rule.
+You might think that this is a distinction without a difference, because it
+does not change whether any particular input is valid or not. But it does
+affect which actions are run. One parsing order runs the second rule's
+action; the other runs the first rule's action and the third rule's action.
+In this example, the output of the program changes.
+Bison resolves a reduce/reduce conflict by choosing to use the rule that
+appears first in the grammar, but it is very risky to rely on this. Every
+reduce/reduce conflict must be studied and usually eliminated. Here is the
+proper way to define sequence:
+
+The intention here is to define a sequence which can contain either
+word or redirect groupings. The individual definitions of
+sequence, words and redirects are error-free, but the
+three together make a subtle ambiguity: even an empty input can be parsed
+in infinitely many ways!
+Consider: nothing-at-all could be a words. Or it could be two
+words in a row, or three, or any number. It could equally well be a
+redirects, or two, or any number. Or it could be a words
+followed by three redirects and another words. And so on.
+Here are two ways to correct these rules. First, to make it a single level
+of sequence:
+
+Sometimes reduce/reduce conflicts can occur that don't look warranted.
+Here is an example:
+
+%token ID
+%%
+def: param_spec return_spec ','
+ ;
+param_spec:
+ type
+ | name_list ':' type
+ ;
+return_spec:
+ type
+ | name ':' type
+ ;
+type: ID
+ ;
+name: ID
+ ;
+name_list:
+ name
+ | name ',' name_list
+ ;
+
+
+It would seem that this grammar can be parsed with only a single token
+of look-ahead: when a param_spec is being read, an ID is
+a name if a comma or colon follows, or a type if another
+ID follows. In other words, this grammar is LR(1).
+
+
+However, Bison, like most parser generators, cannot actually handle all
+LR(1) grammars. In this grammar, two contexts, that after an ID
+at the beginning of a param_spec and likewise at the beginning of
+a return_spec, are similar enough that Bison assumes they are the
+same. They appear similar because the same set of rules would be
+active--the rule for reducing to a name and that for reducing to
+a type. Bison is unable to determine at that stage of processing
+that the rules would require different look-ahead tokens in the two
+contexts, so it makes a single parser state for them both. Combining
+the two contexts causes a conflict later. In parser terminology, this
+occurrence means that the grammar is not LALR(1).
+In general, it is better to fix deficiencies than to document them. But
+this particular deficiency is intrinsically hard to fix; parser
+generators that can handle LR(1) grammars are hard to write and tend to
+produce parsers that are very large. In practice, Bison is more useful
+as it is now.
+When the problem arises, you can often fix it by identifying the two
+parser states that are being confused, and adding something to make them
+look distinct. In the above example, adding one rule to
+return_spec as follows makes the problem go away:
+
+%token BOGUS
+...
+%%
+...
+return_spec:
+ type
+ | name ':' type
+ /* This rule is never used. */
+ | ID BOGUS
+ ;
+
+
+This corrects the problem because it introduces the possibility of an
+additional active rule in the context after the ID at the beginning of
+return_spec. This rule is not active in the corresponding context
+in a param_spec, so the two contexts receive distinct parser states.
+As long as the token BOGUS is never generated by yylex,
+the added rule cannot alter the way actual input is parsed.
+In this particular example, there is another way to solve the problem:
+rewrite the rule for return_spec to use ID directly
+instead of via name. This also causes the two confusing
+contexts to have different sets of active rules, because the one for
+return_spec activates the altered rule for return_spec
+rather than the one for name.
+
+param_spec:
+ type
+ | name_list ':' type
+ ;
+return_spec:
+ type
+ | ID ':' type
+ ;
+
+
+
+
+The Bison parser stack can overflow if too many tokens are shifted and
+not reduced. When this happens, the parser function yyparse
+returns a nonzero value, pausing only to call yyerror to report
+the overflow.
+
+By defining the macro YYMAXDEPTH, you can control how deep the
+parser stack can become before a stack overflow occurs. Define the
+macro with a value that is an integer. This value is the maximum number
+of tokens that can be shifted (and not reduced) before overflow.
+It must be a constant expression whose value is known at compile time.
+The stack space allowed is not necessarily allocated. If you specify a
+large value for YYMAXDEPTH, the parser actually allocates a small
+stack at first, and then makes it bigger by stages as needed. This
+increasing allocation happens automatically and silently. Therefore,
+you do not need to make YYMAXDEPTH painfully small merely to save
+space for ordinary inputs that do not need much stack.
+
+The default value of YYMAXDEPTH, if you do not define it, is
+10000.
+
+You can control how much stack is allocated initially by defining the
+macro YYINITDEPTH. This value too must be a compile-time
+constant integer. The default is 200.
+
+
+
+It is not usually acceptable to have a program terminate on a parse
+error. For example, a compiler should recover sufficiently to parse the
+rest of the input file and check it for errors; a calculator should accept
+another expression.
+In a simple interactive command parser where each input is one line, it may
+be sufficient to allow yyparse to return 1 on error and have the
+caller ignore the rest of the input line when that happens (and then call
+yyparse again). But this is inadequate for a compiler, because it
+forgets all the syntactic context leading up to the error. A syntax error
+deep within a function in the compiler input should not cause the compiler
+to treat the following line like the beginning of a source file.
+
+You can define how to recover from a syntax error by writing rules to
+recognize the special token error. This is a terminal symbol that
+is always defined (you need not declare it) and reserved for error
+handling. The Bison parser generates an error token whenever a
+syntax error happens; if you have provided a rule to recognize this token
+in the current context, the parse can continue.
+For example:
+
+The fourth rule in this example says that an error followed by a newline
+makes a valid addition to any stmnts.
+What happens if a syntax error occurs in the middle of an exp? The
+error recovery rule, interpreted strictly, applies to the precise sequence
+of a stmnts, an error and a newline. If an error occurs in
+the middle of an exp, there will probably be some additional tokens
+and subexpressions on the stack after the last stmnts, and there
+will be tokens to read before the next newline. So the rule is not
+applicable in the ordinary way.
+But Bison can force the situation to fit the rule, by discarding part of
+the semantic context and part of the input. First it discards states and
+objects from the stack until it gets back to a state in which the
+error token is acceptable. (This means that the subexpressions
+already parsed are discarded, back to the last complete stmnts.) At
+this point the error token can be shifted. Then, if the old
+look-ahead token is not acceptable to be shifted next, the parser reads
+tokens and discards them until it finds a token which is acceptable. In
+this example, Bison reads and discards input until the next newline
+so that the fourth rule can apply.
+The choice of error rules in the grammar is a choice of strategies for
+error recovery. A simple and useful strategy is simply to skip the rest of
+the current input line or current statement if an error is detected:
+
+stmnt: error ';' /* on error, skip until ';' is read */
+
+
+It is also useful to recover to the matching close-delimiter of an
+opening-delimiter that has already been parsed. Otherwise the
+close-delimiter will probably appear to be unmatched, and generate another,
+spurious error message:
+
+Error recovery strategies are necessarily guesses. When they guess wrong,
+one syntax error often leads to another. In the above example, the error
+recovery rule guesses that an error is due to bad input within one
+stmnt. Suppose that instead a spurious semicolon is inserted in the
+middle of a valid stmnt. After the error recovery rule recovers
+from the first error, another syntax error will be found straightaway,
+since the text following the spurious semicolon is also an invalid
+stmnt.
+To prevent an outpouring of error messages, the parser will output no error
+message for another syntax error that happens shortly after the first; only
+after three consecutive input tokens have been successfully shifted will
+error messages resume.
+Note that rules which accept the error token may have actions, just
+as any other rules can.
+
+You can make error messages resume immediately by using the macro
+yyerrok in an action. If you do this in the error rule's action, no
+error messages will be suppressed. This macro requires no arguments;
+`yyerrok;' is a valid C statement.
+
+The previous look-ahead token is reanalyzed immediately after an error. If
+this is unacceptable, then the macro yyclearin may be used to clear
+this token. Write the statement `yyclearin;' in the error rule's
+action.
+For example, suppose that on a parse error, an error handling routine is
+called that advances the input stream to some point where parsing should
+once again commence. The next symbol returned by the lexical scanner is
+probably correct. The previous look-ahead token ought to be discarded
+with `yyclearin;'.
+
+The macro YYRECOVERING stands for an expression that has the
+value 1 when the parser is recovering from a syntax error, and 0 the
+rest of the time. A value of 1 indicates that error messages are
+currently suppressed for new syntax errors.
+
+The Bison paradigm is to parse tokens first, then group them into larger
+syntactic units. In many languages, the meaning of a token is affected by
+its context. Although this violates the Bison paradigm, certain techniques
+(known as kludges) may enable you to write Bison parsers for such
+languages.
+(Actually, "kludge" means any technique that gets its job done but is
+neither clean nor robust.)
+
+The C language has a context dependency: the way an identifier is used
+depends on what its current meaning is. For example, consider this:
+
+foo (x);
+
+
+This looks like a function call statement, but if foo is a typedef
+name, then this is actually a declaration of x. How can a Bison
+parser for C decide how to parse this input?
+The method used in GNU C is to have two different token types,
+IDENTIFIER and TYPENAME. When yylex finds an
+identifier, it looks up the current declaration of the identifier in order
+to decide which token type to return: TYPENAME if the identifier is
+declared as a typedef, IDENTIFIER otherwise.
+The grammar rules can then express the context dependency by the choice of
+token type to recognize. IDENTIFIER is accepted as an expression,
+but TYPENAME is not. TYPENAME can start a declaration, but
+IDENTIFIER cannot. In contexts where the meaning of the identifier
+is not significant, such as in declarations that can shadow a
+typedef name, either TYPENAME or IDENTIFIER is
+accepted--there is one rule for each of the two token types.
+This technique is simple to use if the decision of which kinds of
+identifiers to allow is made at a place close to where the identifier is
+parsed. But in C this is not always so: C allows a declaration to
+redeclare a typedef name provided an explicit type has been specified
+earlier:
+
+typedef int foo, bar, lose;
+static foo (bar); /* redeclare bar as static variable */
+static int foo (lose); /* redeclare foo as function */
+
+
+Unfortunately, the name being declared is separated from the declaration
+construct itself by a complicated syntactic structure--the "declarator".
+As a result, the part of Bison parser for C needs to be duplicated, with
+all the nonterminal names changed: once for parsing a declaration in which
+a typedef name can be redefined, and once for parsing a declaration in
+which that can't be done. Here is a part of the duplication, with actions
+omitted for brevity:
+
+Here initdcl can redeclare a typedef name, but notype_initdcl
+cannot. The distinction between declarator and
+notype_declarator is the same sort of thing.
+There is some similarity between this technique and a lexical tie-in
+(described next), in that information which alters the lexical analysis is
+changed during parsing by other parts of the program. The difference is
+here the information is global, and is used for other purposes in the
+program. A true lexical tie-in has a special-purpose flag controlled by
+the syntactic context.
+
+
+One way to handle context-dependency is the lexical tie-in: a flag
+which is set by Bison actions, whose purpose is to alter the way tokens are
+parsed.
+For example, suppose we have a language vaguely like C, but with a special
+construct `hex (hex-expr)'. After the keyword hex comes
+an expression in parentheses in which all integers are hexadecimal. In
+particular, the token `a1b' must be treated as an integer rather than
+as an identifier if it appears in that context. Here is how you can do it:
+
+Here we assume that yylex looks at the value of hexflag; when
+it is nonzero, all integers are parsed in hexadecimal, and tokens starting
+with letters are parsed as integers if possible.
+The declaration of hexflag shown in the C declarations section of
+the parser file is needed to make it accessible to the actions
+(see section The C Declarations Section). You must also write the code in yylex
+to obey the flag.
+
+Lexical tie-ins make strict demands on any error recovery rules you have.
+See section Error Recovery.
+The reason for this is that the purpose of an error recovery rule is to
+abort the parsing of one construct and resume in some larger construct.
+For example, in C-like languages, a typical error recovery rule is to skip
+tokens until the next semicolon, and then start a new statement, like this:
+
+If there is a syntax error in the middle of a `hex (expr)'
+construct, this error rule will apply, and then the action for the
+completed `hex (expr)' will never run. So hexflag would
+remain set for the entire rest of the input, or until the next hex
+keyword, causing identifiers to be misinterpreted as integers.
+To avoid this problem the error recovery rule itself clears hexflag.
+There may also be an error recovery rule that works within expressions.
+For example, there could be a rule which applies within parentheses
+and skips to the close-parenthesis:
+
+If this rule acts within the hex construct, it is not going to abort
+that construct (since it applies to an inner level of parentheses within
+the construct). Therefore, it should not clear the flag: the rest of
+the hex construct should be parsed with the flag still in effect.
+What if there is an error recovery rule which might abort out of the
+hex construct or might not, depending on circumstances? There is no
+way you can write the action to determine whether a hex construct is
+being aborted or not. So if you are using a lexical tie-in, you had better
+make sure your error recovery rules are not of this kind. Each rule must
+be such that you can be sure that it always will, or always won't, have to
+clear the flag.
+
+
+
+
+
+If a Bison grammar compiles properly but doesn't do what you want when it
+runs, the yydebug parser-trace feature can help you figure out why.
+To enable compilation of trace facilities, you must define the macro
+YYDEBUG when you compile the parser. You could use
+`-DYYDEBUG=1' as a compiler option or you could put `#define
+YYDEBUG 1' in the C declarations section of the grammar file
+(see section The C Declarations Section). Alternatively, use the `-t' option when
+you run Bison (see section Invoking Bison). We always define YYDEBUG so that
+debugging is always possible.
+The trace facility uses stderr, so you must add #include
+<stdio.h> to the C declarations section unless it is already there.
+Once you have compiled the program with trace facilities, the way to
+request a trace is to store a nonzero value in the variable yydebug.
+You can do this by making the C code do it (in main, perhaps), or
+you can alter the value with a C debugger.
+Each step taken by the parser when yydebug is nonzero produces a
+line or two of trace information, written on stderr. The trace
+messages tell you these things:
+
+
+Each time the parser calls yylex, what kind of token was read.
+
+Each time a token is shifted, the depth and complete contents of the
+state stack (see section Parser States).
+
+Each time a rule is reduced, which rule it is, and the complete contents
+of the state stack afterward.
+
+
+To make sense of this information, it helps to refer to the listing file
+produced by the Bison `-v' option (see section Invoking Bison). This file
+shows the meaning of each state in terms of positions in various rules, and
+also what each state will do with each possible input token. As you read
+the successive trace messages, you can see that the parser is functioning
+according to its specification in the listing file. Eventually you will
+arrive at the place where something undesirable happens, and you will see
+which parts of the grammar are to blame.
+The parser file is a C program and you can use C debuggers on it, but it's
+not easy to interpret what it is doing. The parser function is a
+finite-state machine interpreter, and aside from the actions it executes
+the same code over and over. Only the values of variables show where in
+the grammar it is working.
+
+The debugging information normally gives the token type of each token
+read, but not its semantic value. You can optionally define a macro
+named YYPRINT to provide a way to print the value. If you define
+YYPRINT, it should take three arguments. The parser will pass a
+standard I/O stream, the numeric code for the token type, and the token
+value (from yylval).
+Here is an example of YYPRINT suitable for the multi-function
+calculator (see section Declarations for mfcalc):
+
+
+
+
+The usual way to invoke Bison is as follows:
+
+bison infile
+
+
+Here infile is the grammar file name, which usually ends in
+`.y'. The parser file's name is made by replacing the `.y'
+with `.tab.c'. Thus, the `bison foo.y' filename yields
+`foo.tab.c', and the `bison hack/foo.y' filename yields
+`hack/foo.tab.c'.
+
+Bison supports both traditional single-letter options and mnemonic long
+option names. Long option names are indicated with `--' instead of
+`-'. Abbreviations for option names are allowed as long as they
+are unique. When a long option takes an argument, like
+`--file-prefix', connect the option name and the argument with
+`='.
+Here is a list of options that can be used with Bison, alphabetized by
+short option. It is followed by a cross key alphabetized by long
+option.
+
+
`-b file-prefix'
+
+
`--file-prefix=prefix'
+
+Specify a prefix to use for all Bison output file names. The names are
+chosen as if the input file were named `prefix.c'.
+
`-d'
+
+
`--defines'
+
+Write an extra output file containing macro definitions for the token
+type names defined in the grammar and the semantic value type
+YYSTYPE, as well as a few extern variable declarations.
+If the parser output file is named `name.c' then this file
+is named `name.h'.
+This output file is essential if you wish to put the definition of
+yylex in a separate source file, because yylex needs to
+be able to refer to token type codes and the variable
+yylval. See section Semantic Values of Tokens.
+
`-l'
+
+
`--no-lines'
+
+Don't put any #line preprocessor commands in the parser file.
+Ordinarily Bison puts them in the parser file so that the C compiler
+and debuggers will associate errors with your source file, the
+grammar file. This option causes them to associate errors with the
+parser file, treating it an independent source file in its own right.
+
`-o outfile'
+
+
`--output-file=outfile'
+
+Specify the name outfile for the parser file.
+The other output files' names are constructed from outfile
+as described under the `-v' and `-d' switches.
+
`-p prefix'
+
+
`--name-prefix=prefix'
+
+Rename the external symbols used in the parser so that they start with
+prefix instead of `yy'. The precise list of symbols renamed
+is yyparse, yylex, yyerror, yynerrs,
+yylval, yychar and yydebug.
+For example, if you use `-p c', the names become cparse,
+clex, and so on.
+See section Multiple Parsers in the Same Program.
+
`-t'
+
+
`--debug'
+
+Output a definition of the macro YYDEBUG into the parser file,
+so that the debugging facilities are compiled. See section Debugging Your Parser.
+
`-v'
+
+
`--verbose'
+
+Write an extra output file containing verbose descriptions of the
+parser states and what is done for each type of look-ahead token in
+that state.
+This file also describes all the conflicts, both those resolved by
+operator precedence and the unresolved ones.
+The file's name is made by removing `.tab.c' or `.c' from
+the parser output file name, and adding `.output' instead.
+Therefore, if the input file is `foo.y', then the parser file is
+called `foo.tab.c' by default. As a consequence, the verbose
+output file is called `foo.output'.
+
`-V'
+
+
`--version'
+
+Print the version number of Bison and exit.
+
`-h'
+
+
`--help'
+
+Print a summary of the command-line options to Bison and exit.
+
`-y'
+
+
`--yacc'
+
+
`--fixed-output-files'
+
+Equivalent to `-o y.tab.c'; the parser output file is called
+`y.tab.c', and the other outputs are called `y.output' and
+`y.tab.h'. The purpose of this switch is to imitate Yacc's output
+file name conventions. Thus, the following shell script can substitute
+for Yacc:
+
+
+
+The command line syntax for Bison on VMS is a variant of the usual
+Bison command syntax--adapted to fit VMS conventions.
+To find the VMS equivalent for any Bison option, start with the long
+option, and substitute a `/' for the leading `--', and
+substitute a `_' for each `-' in the name of the long option.
+For example, the following invocation under VMS:
+
+bison /debug/name_prefix=bar foo.y
+
+
+is equivalent to the following command under POSIX.
+
+bison --debug --name-prefix=bar foo.y
+
+
+The VMS file system does not permit filenames such as
+`foo.tab.c'. In the above example, the output file
+would instead be named `foo_tab.c'.
+
+A token name reserved for error recovery. This token may be used in
+grammar rules so as to allow the Bison parser to recognize an error in
+the grammar without halting the process. In effect, a sentence
+containing an error may be recognized as valid. On a parse error, the
+token error becomes the current look-ahead token. Actions
+corresponding to error are then executed, and the look-ahead
+token is reset to the token that originally caused the violation.
+See section Error Recovery.
+
YYABORT
+
+Macro to pretend that an unrecoverable syntax error has occurred, by
+making yyparse return 1 immediately. The error reporting
+function yyerror is not called. See section The Parser Function yyparse.
+
YYACCEPT
+
+Macro to pretend that a complete utterance of the language has been
+read, by making yyparse return 0 immediately.
+See section The Parser Function yyparse.
+
+Macro to pretend that a syntax error has just been detected: call
+yyerror and then perform normal error recovery if possible
+(see section Error Recovery), or (if recovery is impossible) make
+yyparse return 1. See section Error Recovery.
+
YYERROR_VERBOSE
+
+Macro that you define with #define in the Bison declarations
+section to request verbose, specific error message strings when
+yyerror is called.
+
+Macro for specifying an extra argument (or list of extra arguments) for
+yyparse to pass to yylex. See section Calling Conventions for Pure Parsers.
+
YYLTYPE
+
+Macro for the data type of yylloc; a structure with four
+members. See section Textual Positions of Tokens.
+
+External integer variable that contains the integer value of the
+current look-ahead token. (In a pure parser, it is a local variable
+within yyparse.) Error-recovery rule actions may examine this
+variable. See section Special Features for Use in Actions.
+
yyclearin
+
+Macro used in error-recovery rule actions. It clears the previous
+look-ahead token. See section Error Recovery.
+
yydebug
+
+External integer variable set to zero by default. If yydebug
+is given a nonzero value, the parser will output information on input
+symbols and parser action. See section Debugging Your Parser.
+
yyerrok
+
+Macro to cause parser to recover immediately to its normal mode
+after a parse error. See section Error Recovery.
+
yyerror
+
+User-supplied function to be called by yyparse on error. The
+function receives one argument, a pointer to a character string
+containing an error message. See section The Error Reporting Function yyerror.
+
yylex
+
+User-supplied lexical analyzer function, called with no arguments
+to get the next token. See section The Lexical Analyzer Function yylex.
+
yylval
+
+External variable in which yylex should place the semantic
+value associated with a token. (In a pure parser, it is a local
+variable within yyparse, and its address is passed to
+yylex.) See section Semantic Values of Tokens.
+
yylloc
+
+External variable in which yylex should place the line and
+column numbers associated with a token. (In a pure parser, it is a
+local variable within yyparse, and its address is passed to
+yylex.) You can ignore this variable if you don't use the
+`@' feature in the grammar actions. See section Textual Positions of Tokens.
+
yynerrs
+
+Global variable which Bison increments each time there is a parse
+error. (In a pure parser, it is a local variable within
+yyparse.) See section The Error Reporting Function yyerror.
+
yyparse
+
+The parser function produced by Bison; call this function to start
+parsing. See section The Parser Function yyparse.
+
%left
+
+Bison declaration to assign left associativity to token(s).
+See section Operator Precedence.
+
%nonassoc
+
+Bison declaration to assign nonassociativity to token(s).
+See section Operator Precedence.
+
+Bison declaration to assign right associativity to token(s).
+See section Operator Precedence.
+
%start
+
+Bison declaration to specify the start symbol. See section The Start-Symbol.
+
%token
+
+Bison declaration to declare token(s) without specifying precedence.
+See section Token Type Names.
+
%type
+
+Bison declaration to declare nonterminals. See section Nonterminal Symbols.
+
%union
+
+Bison declaration to specify several possible data types for semantic
+values. See section The Collection of Value Types.
+
+
+These are the punctuation and delimiters used in Bison input:
+
+
`%%'
+
+Delimiter used to separate the grammar rule section from the
+Bison declarations section or the additional C code section.
+See section The Overall Layout of a Bison Grammar.
+
`%{ %}'
+
+All code listed between `%{' and `%}' is copied directly
+to the output file uninterpreted. Such code forms the "C
+declarations" section of the input file. See section Outline of a Bison Grammar.
+
+Formal method of specifying context-free grammars. BNF was first used
+in the ALGOL-60 report, 1963. See section Languages and Context-Free Grammars.
+
Context-free grammars
+
+Grammars specified as rules that can be applied regardless of context.
+Thus, if there is a rule which says that an integer can be used as an
+expression, integers are allowed anywhere an expression is
+permitted. See section Languages and Context-Free Grammars.
+
Dynamic allocation
+
+Allocation of memory that occurs during execution, rather than at
+compile time or on entry to a function.
+
Empty string
+
+Analogous to the empty set in set theory, the empty string is a
+character string of length zero.
+
Finite-state stack machine
+
+A "machine" that has discrete states in which it is said to exist at
+each instant in time. As input to the machine is processed, the
+machine moves from state to state as specified by the logic of the
+machine. In the case of the parser, the input is the language being
+parsed, and the states correspond to various stages in the grammar
+rules. See section The Bison Parser Algorithm.
+
Grouping
+
+A language construct that is (in general) grammatically divisible;
+for example, `expression' or `declaration' in C.
+See section Languages and Context-Free Grammars.
+
Infix operator
+
+An arithmetic operator that is placed between the operands on which it
+performs some operation.
+
Input stream
+
+A continuous flow of data between devices or programs.
+
Language construct
+
+One of the typical usage schemas of the language. For example, one of
+the constructs of the C language is the if statement.
+See section Languages and Context-Free Grammars.
+
Left associativity
+
+Operators having left associativity are analyzed from left to right:
+`a+b+c' first computes `a+b' and then combines with
+`c'. See section Operator Precedence.
+
Left recursion
+
+A rule whose result symbol is also its first component symbol;
+for example, `expseq1 : expseq1 ',' exp;'. See section Recursive Rules.
+
Left-to-right parsing
+
+Parsing a sentence of a language by analyzing it token by token from
+left to right. See section The Bison Parser Algorithm.
+
+A flag, set by actions in the grammar rules, which alters the way
+tokens are parsed. See section Lexical Tie-ins.
+
Look-ahead token
+
+A token already read but not yet shifted. See section Look-Ahead Tokens.
+
LALR(1)
+
+The class of context-free grammars that Bison (like most other parser
+generators) can handle; a subset of LR(1). See section Mysterious Reduce/Reduce Conflicts.
+
LR(1)
+
+The class of context-free grammars in which at most one token of
+look-ahead is needed to disambiguate the parsing of any piece of input.
+
Nonterminal symbol
+
+A grammar symbol standing for a grammatical construct that can
+be expressed through rules in terms of smaller constructs; in other
+words, a construct that is not a token. See section Symbols, Terminal and Nonterminal.
+
Parse error
+
+An error encountered during parsing of an input stream due to invalid
+syntax. See section Error Recovery.
+
Parser
+
+A function that recognizes valid sentences of a language by analyzing
+the syntax structure of a set of tokens passed to it from a lexical
+analyzer.
+
Postfix operator
+
+An arithmetic operator that is placed after the operands upon which it
+performs some operation.
+
Reduction
+
+Replacing a string of nonterminals and/or terminals with a single
+nonterminal, according to a grammar rule. See section The Bison Parser Algorithm.
+
Reentrant
+
+A reentrant subprogram is a subprogram which can be in invoked any
+number of times in parallel, without interference between the various
+invocations. See section A Pure (Reentrant) Parser.
+
Reverse polish notation
+
+A language in which all operators are postfix operators.
+
Right recursion
+
+A rule whose result symbol is also its last component symbol;
+for example, `expseq1: exp ',' expseq1;'. See section Recursive Rules.
+
Semantics
+
+In computer languages, the semantics are specified by the actions
+taken for each instance of the language, i.e., the meaning of
+each statement. See section Defining Language Semantics.
+
Shift
+
+A parser is said to shift when it makes the choice of analyzing
+further input from the stream rather than reducing immediately some
+already-recognized rule. See section The Bison Parser Algorithm.
+
+The nonterminal symbol that stands for a complete valid utterance in
+the language being parsed. The start symbol is usually listed as the
+first nonterminal symbol in a language specification.
+See section The Start-Symbol.
+
Symbol table
+
+A data structure where symbol names and associated data are stored
+during parsing to allow for recognition and use of existing
+information in repeated uses of a symbol. See section Multi-Function Calculator: mfcalc.
+
Token
+
+A basic, grammatically indivisible unit of a language. The symbol
+that describes a token in the grammar is a terminal symbol.
+The input of the Bison parser is a stream of tokens which comes from
+the lexical analyzer. See section Symbols, Terminal and Nonterminal.
+
Terminal symbol
+
+A grammar symbol that has no rules in the grammar and therefore
+is grammatically indivisible. The piece of text it represents
+is a token. See section Languages and Context-Free Grammars.
+
+This document was generated on 2 October 1998 using the
+texi2html
+translator version 1.52.
+
+
diff --git a/bin/bison/bison.simple b/bin/bison/bison.simple
new file mode 100755
index 0000000..f45278f
--- /dev/null
+++ b/bin/bison/bison.simple
@@ -0,0 +1,686 @@
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+ 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, 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. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 192 "bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+$ /* the action file gets copied in in place of this dollarsign */
+#line 487 "bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
diff --git a/bin/flex/flex.exe b/bin/flex/flex.exe
new file mode 100755
index 0000000..30bfa29
Binary files /dev/null and b/bin/flex/flex.exe differ
diff --git a/bin/flex/flex_1.html b/bin/flex/flex_1.html
new file mode 100755
index 0000000..5370c92
--- /dev/null
+++ b/bin/flex/flex_1.html
@@ -0,0 +1,4079 @@
+flex
+
+
+This manual describes
+flex,
+a tool for generating programs that perform pattern-matching on text. The
+manual includes both tutorial and reference sections:
+
+
+
Description
+ a brief overview of the tool
+
+
Some Simple Examples
+
+
Format Of The Input File
+
+
Patterns
+ the extended regular expressions used by flex
+
+
How The Input Is Matched
+ the rules for determining what has been matched
+
+
Actions
+ how to specify what to do when a pattern is matched
+
+
The Generated Scanner
+ details regarding the scanner that flex produces;
+ how to control the input source
+
+
Start Conditions
+ introducing context into your scanners, and
+ managing "mini-scanners"
+
+
Multiple Input Buffers
+ how to manipulate multiple input sources; how to
+ scan from strings instead of files
+
+
End-of-file Rules
+ special rules for matching the end of the input
+
+
Miscellaneous Macros
+ a summary of macros available to the actions
+
+
Values Available To The User
+ a summary of values available to the actions
+
+
Interfacing With Yacc
+ connecting flex scanners together with yacc parsers
+
+
Options
+ flex command-line options, and the "%option"
+ directive
+
+
Performance Considerations
+ how to make your scanner go as fast as possible
+
+
Generating C++ Scanners
+ the (experimental) facility for generating C++
+ scanner classes
+
+
Incompatibilities With Lex And POSIX
+ how flex differs from AT&T lex and the POSIX lex
+ standard
+
+
Diagnostics
+ those error messages produced by flex (or scanners
+ it generates) whose meanings might not be apparent
+
+
Files
+ files used by flex
+
+
Deficiencies / Bugs
+ known problems with flex
+
+
See Also
+ other documentation, related tools
+
+
Author
+ includes contact information
+
+
+
+
DESCRIPTION
+
+flex
+is a tool for generating
+scanners:
+programs which recognized lexical patterns in text.
+flex
+reads
+the given input files, or its standard input if no file names are given,
+for a description of a scanner to generate. The description is in
+the form of pairs
+of regular expressions and C code, called
+rules.flex
+generates as output a C source file,
+lex.yy.c,
+which defines a routine
+yylex().
+This file is compiled and linked with the
+-lfl
+library to produce an executable. When the executable is run,
+it analyzes its input for occurrences
+of the regular expressions. Whenever it finds one, it executes
+the corresponding C code.
+
+
SOME SIMPLE EXAMPLES
+
+
+
+First some simple examples to get the flavor of how one uses
+flex.
+The following
+flex
+input specifies a scanner which whenever it encounters the string
+"username" will replace it with the user's login name:
+
+
+
%%
+ username printf( "%s", getlogin() );
+
+
+By default, any text not matched by a
+flex
+scanner
+is copied to the output, so the net effect of this scanner is
+to copy its input file to its output with each occurrence
+of "username" expanded.
+In this input, there is just one rule. "username" is the
+pattern
+and the "printf" is the
+action.
+The "%%" marks the beginning of the rules.
+
+
+This scanner counts the number of characters and the number
+of lines in its input (it produces no output other than the
+final report on the counts). The first line
+declares two globals, "num_lines" and "num_chars", which are accessible
+both inside
+yylex()
+and in the
+main()
+routine declared after the second "%%". There are two rules, one
+which matches a newline ("\n") and increments both the line count and
+the character count, and one which matches any character other than
+a newline (indicated by the "." regular expression).
+
+
+A somewhat more complicated example:
+
+
+
/* scanner for a toy Pascal-like language */
+
+
%{
+ /* need this for the call to atof() below */
+ #include <math.h>
+ %}
+
+
main( argc, argv )
+ int argc;
+ char **argv;
+ {
+ ++argv, --argc; /* skip over program name */
+ if ( argc > 0 )
+ yyin = fopen( argv[0], "r" );
+ else
+ yyin = stdin;
+
+ yylex();
+ }
+
+
+This is the beginnings of a simple scanner for a language like
+Pascal. It identifies different types of
+tokens
+and reports on what it has seen.
+
+
+The details of this example will be explained in the following
+sections.
+
+
FORMAT OF THE INPUT FILE
+
+The
+flex
+input file consists of three sections, separated by a line with just
+%%
+in it:
+
+
+
definitions
+ %%
+ rules
+ %%
+ user code
+
+
+The
+definitions
+section contains declarations of simple
+name
+definitions to simplify the scanner specification, and declarations of
+startconditions,
+which are explained in a later section.
+
+
+Name definitions have the form:
+
+
+
name definition
+
+
+The "name" is a word beginning with a letter or an underscore ('_')
+followed by zero or more letters, digits, '_', or '-' (dash).
+The definition is taken to begin at the first non-white-space character
+following the name and continuing to the end of the line.
+The definition can subsequently be referred to using "{name}", which
+will expand to "(definition)". For example,
+
+
+
DIGIT [0-9]
+ ID [a-z][a-z0-9]*
+
+
+defines "DIGIT" to be a regular expression which matches a
+single digit, and
+"ID" to be a regular expression which matches a letter
+followed by zero-or-more letters-or-digits.
+A subsequent reference to
+
+
+
{DIGIT}+"."{DIGIT}*
+
+
+is identical to
+
+
+
([0-9])+"."([0-9])*
+
+
+and matches one-or-more digits followed by a '.' followed
+by zero-or-more digits.
+
+
+The
+rules
+section of the
+flex
+input contains a series of rules of the form:
+
+
+
pattern action
+
+
+where the pattern must be unindented and the action must begin
+on the same line.
+
+
+See below for a further description of patterns and actions.
+
+
+Finally, the user code section is simply copied to
+lex.yy.c
+verbatim.
+It is used for companion routines which call or are called
+by the scanner. The presence of this section is optional;
+if it is missing, the second
+%%
+in the input file may be skipped, too.
+
+
+In the definitions and rules sections, any
+indented
+text or text enclosed in
+%{
+and
+%}
+is copied verbatim to the output (with the %{}'s removed).
+The %{}'s must appear unindented on lines by themselves.
+
+
+In the rules section,
+any indented or %{} text appearing before the
+first rule may be used to declare variables
+which are local to the scanning routine and (after the declarations)
+code which is to be executed whenever the scanning routine is entered.
+Other indented or %{} text in the rule section is still copied to the output,
+but its meaning is not well-defined and it may well cause compile-time
+errors (this feature is present for
+POSIX
+compliance; see below for other such features).
+
+
+In the definitions section (but not in the rules section),
+an unindented comment (i.e., a line
+beginning with "/*") is also copied verbatim to the output up
+to the next "*/".
+
+
PATTERNS
+
+The patterns in the input are written using an extended set of regular
+expressions. These are:
+
+
+
x match the character 'x'
+ . any character (byte) except newline
+ [xyz] a "character class"; in this case, the pattern
+ matches either an 'x', a 'y', or a 'z'
+ [abj-oZ] a "character class" with a range in it; matches
+ an 'a', a 'b', any letter from 'j' through 'o',
+ or a 'Z'
+ [^A-Z] a "negated character class", i.e., any character
+ but those in the class. In this case, any
+ character EXCEPT an uppercase letter.
+ [^A-Z\n] any character EXCEPT an uppercase letter or
+ a newline
+ r* zero or more r's, where r is any regular expression
+ r+ one or more r's
+ r? zero or one r's (that is, "an optional r")
+ r{2,5} anywhere from two to five r's
+ r{2,} two or more r's
+ r{4} exactly 4 r's
+ {name} the expansion of the "name" definition
+ (see above)
+ "[xyz]\"foo"
+ the literal string: [xyz]"foo
+ \X if X is an 'a', 'b', 'f', 'n', 'r', 't', or 'v',
+ then the ANSI-C interpretation of \x.
+ Otherwise, a literal 'X' (used to escape
+ operators such as '*')
+ \0 a NUL character (ASCII code 0)
+ \123 the character with octal value 123
+ \x2a the character with hexadecimal value 2a
+ (r) match an r; parentheses are used to override
+ precedence (see below)
+
+
+
rs the regular expression r followed by the
+ regular expression s; called "concatenation"
+
+
+
r|s either an r or an s
+
+
+
r/s an r but only if it is followed by an s. The
+ text matched by s is included when determining
+ whether this rule is the "longest match",
+ but is then returned to the input before
+ the action is executed. So the action only
+ sees the text matched by r. This type
+ of pattern is called trailing context".
+ (There are some combinations of r/s that flex
+ cannot match correctly; see notes in the
+ Deficiencies / Bugs section below regarding
+ "dangerous trailing context".)
+ ^r an r, but only at the beginning of a line (i.e.,
+ which just starting to scan, or right after a
+ newline has been scanned).
+ r$ an r, but only at the end of a line (i.e., just
+ before a newline). Equivalent to "r/\n".
+
+
Note that flex's notion of "newline" is exactly
+ whatever the C compiler used to compile flex
+ interprets '\n' as; in particular, on some DOS
+ systems you must either filter out \r's in the
+ input yourself, or explicitly use r/\r\n for "r$".
+
+
+
<s>r an r, but only in start condition s (see
+ below for discussion of start conditions)
+ <s1,s2,s3>r
+ same, but in any of start conditions s1,
+ s2, or s3
+ <*>r an r in any start condition, even an exclusive one.
+
+
+
<<EOF>> an end-of-file
+ <s1,s2><<EOF>>
+ an end-of-file when in start condition s1 or s2
+
+
+Note that inside of a character class, all regular expression operators
+lose their special meaning except escape ('\') and the character class
+operators, '-', ']', and, at the beginning of the class, '^'.
+
+
+The regular expressions listed above are grouped according to
+precedence, from highest precedence at the top to lowest at the bottom.
+Those grouped together have equal precedence. For example,
+
+
+
foo|bar*
+
+
+is the same as
+
+
+
(foo)|(ba(r*))
+
+
+since the '*' operator has higher precedence than concatenation,
+and concatenation higher than alternation ('|'). This pattern
+therefore matches
+either
+the string "foo"
+or
+the string "ba" followed by zero-or-more r's.
+To match "foo" or zero-or-more "bar"'s, use:
+
+
+
foo|(bar)*
+
+
+and to match zero-or-more "foo"'s-or-"bar"'s:
+
+
+
(foo|bar)*
+
+
+
+
+In addition to characters and ranges of characters, character classes
+can also contain character class
+expressions.
+These are expressions enclosed inside
+[:
+and
+:]
+delimiters (which themselves must appear between the '[' and ']' of the
+character class; other elements may occur inside the character class, too).
+The valid expressions are:
+
+These expressions all designate a set of characters equivalent to
+the corresponding standard C
+isXXX
+function. For example,
+[:alnum:]
+designates those characters for which
+isalnum()
+returns true - i.e., any alphabetic or numeric.
+Some systems don't provide
+isblank(),
+so flex defines
+[:blank:]
+as a blank or a tab.
+
+
+For example, the following character classes are all equivalent:
+
+If your scanner is case-insensitive (the
+-i
+flag), then
+[:upper:]
+and
+[:lower:]
+are equivalent to
+[:alpha:].
+
+
+Some notes on patterns:
+
-
A negated character class such as the example "[^A-Z]"
+above
+willmatchanewline
+unless "\n" (or an equivalent escape sequence) is one of the
+characters explicitly present in the negated character class
+(e.g., "[^A-Z\n]"). This is unlike how many other regular
+expression tools treat negated character classes, but unfortunately
+the inconsistency is historically entrenched.
+Matching newlines means that a pattern like [^"]* can match the entire
+input unless there's another quote in the input.
+
-
A rule can have at most one instance of trailing context (the '/' operator
+or the '$' operator). The start condition, '^', and "<<EOF>>" patterns
+can only occur at the beginning of a pattern, and, as well as with '/' and '$',
+cannot be grouped inside parentheses. A '^' which does not occur at
+the beginning of a rule or a '$' which does not occur at the end of
+a rule loses its special properties and is treated as a normal character.
+
The following are illegal:
+
+
+
foo/bar$
+ <sc1>foo<sc2>bar
+
+
+Note that the first of these, can be written "foo/bar\n".
+
The following will result in '$' or '^' being treated as a normal character:
+
+
+
foo|(bar$)
+ foo|^bar
+
+
+If what's wanted is a "foo" or a bar-followed-by-a-newline, the following
+could be used (the special '|' action is explained below):
+
+
+
foo |
+ bar$ /* action goes here */
+
+
+A similar trick will work for matching a foo or a
+bar-at-the-beginning-of-a-line.
+
+
HOW THE INPUT IS MATCHED
+
+When the generated scanner is run, it analyzes its input looking
+for strings which match any of its patterns. If it finds more than
+one match, it takes the one matching the most text (for trailing
+context rules, this includes the length of the trailing part, even
+though it will then be returned to the input). If it finds two
+or more matches of the same length, the
+rule listed first in the
+flex
+input file is chosen.
+
+
+Once the match is determined, the text corresponding to the match
+(called the
+token)
+is made available in the global character pointer
+yytext,
+and its length in the global integer
+yyleng.
+The
+action
+corresponding to the matched pattern is then executed (a more
+detailed description of actions follows), and then the remaining
+input is scanned for another match.
+
+
+If no match is found, then the
+defaultrule
+is executed: the next character in the input is considered matched and
+copied to the standard output. Thus, the simplest legal
+flex
+input is:
+
+
+
%%
+
+
+which generates a scanner that simply copies its input (one character
+at a time) to its output.
+
+
+Note that
+yytext
+can be defined in two different ways: either as a character
+pointer
+or as a character
+array.
+You can control which definition
+flex
+uses by including one of the special directives
+%pointer
+or
+%array
+in the first (definitions) section of your flex input. The default is
+%pointer,
+unless you use the
+-l
+lex compatibility option, in which case
+yytext
+will be an array.
+The advantage of using
+%pointer
+is substantially faster scanning and no buffer overflow when matching
+very large tokens (unless you run out of dynamic memory). The disadvantage
+is that you are restricted in how your actions can modify
+yytext
+(see the next section), and calls to the
+unput()
+function destroys the present contents of
+yytext,
+which can be a considerable porting headache when moving between different
+lex
+versions.
+
+
+The advantage of
+%array
+is that you can then modify
+yytext
+to your heart's content, and calls to
+unput()
+do not destroy
+yytext
+(see below). Furthermore, existing
+lex
+programs sometimes access
+yytext
+externally using declarations of the form:
+
+ extern char yytext[];
+
+This definition is erroneous when used with
+%pointer,
+but correct for
+%array.
+
+
+%array
+defines
+yytext
+to be an array of
+YYLMAX
+characters, which defaults to a fairly large value. You can change
+the size by simply #define'ing
+YYLMAX
+to a different value in the first section of your
+flex
+input. As mentioned above, with
+%pointer
+yytext grows dynamically to accommodate large tokens. While this means your
+%pointer
+scanner can accommodate very large tokens (such as matching entire blocks
+of comments), bear in mind that each time the scanner must resize
+yytext
+it also must rescan the entire token from the beginning, so matching such
+tokens can prove slow.
+yytext
+presently does
+not
+dynamically grow if a call to
+unput()
+results in too much text being pushed back; instead, a run-time error results.
+
+
+Also note that you cannot use
+%array
+with C++ scanner classes
+(the
+c++
+option; see below).
+
+
ACTIONS
+
+Each pattern in a rule has a corresponding action, which can be any
+arbitrary C statement. The pattern ends at the first non-escaped
+whitespace character; the remainder of the line is its action. If the
+action is empty, then when the pattern is matched the input token
+is simply discarded. For example, here is the specification for a program
+which deletes all occurrences of "zap me" from its input:
+
+
+
%%
+ "zap me"
+
+
+(It will copy all other characters in the input to the output since
+they will be matched by the default rule.)
+
+
+Here is a program which compresses multiple blanks and tabs down to
+a single blank, and throws away whitespace found at the end of a line:
+
+If the action contains a '{', then the action spans till the balancing '}'
+is found, and the action may cross multiple lines.
+flex
+knows about C strings and comments and won't be fooled by braces found
+within them, but also allows actions to begin with
+%{
+and will consider the action to be all the text up to the next
+%}
+(regardless of ordinary braces inside the action).
+
+
+An action consisting solely of a vertical bar ('|') means "same as
+the action for the next rule." See below for an illustration.
+
+
+Actions can include arbitrary C code, including
+return
+statements to return a value to whatever routine called
+yylex().
+Each time
+yylex()
+is called it continues processing tokens from where it last left
+off until it either reaches
+the end of the file or executes a return.
+
+
+Actions are free to modify
+yytext
+except for lengthening it (adding
+characters to its end--these will overwrite later characters in the
+input stream). This however does not apply when using
+%array
+(see above); in that case,
+yytext
+may be freely modified in any way.
+
+
+Actions are free to modify
+yyleng
+except they should not do so if the action also includes use of
+yymore()
+(see below).
+
+
+There are a number of special directives which can be included within
+an action:
+
-
ECHO
+copies yytext to the scanner's output.
+
-
BEGIN
+followed by the name of a start condition places the scanner in the
+corresponding start condition (see below).
+
-
REJECT
+directs the scanner to proceed on to the "second best" rule which matched the
+input (or a prefix of the input). The rule is chosen as described
+above in "How the Input is Matched", and
+yytext
+and
+yyleng
+set up appropriately.
+It may either be one which matched as much text
+as the originally chosen rule but came later in the
+flex
+input file, or one which matched less text.
+For example, the following will both count the
+words in the input and call the routine special() whenever "frob" is seen:
+
+Without the
+REJECT,
+any "frob"'s in the input would not be counted as words, since the
+scanner normally executes only one action per token.
+Multiple
+REJECT's
+are allowed, each one finding the next best choice to the currently
+active rule. For example, when the following scanner scans the token
+"abcd", it will write "abcdabcaba" to the output:
+
+
+
%%
+ a |
+ ab |
+ abc |
+ abcd ECHO; REJECT;
+ .|\n /* eat up any unmatched character */
+
+
+(The first three rules share the fourth's action since they use
+the special '|' action.)
+REJECT
+is a particularly expensive feature in terms of scanner performance;
+if it is used in
+any
+of the scanner's actions it will slow down
+all
+of the scanner's matching. Furthermore,
+REJECT
+cannot be used with the
+-Cf
+or
+-CF
+options (see below).
+
Note also that unlike the other special actions,
+REJECT
+is a
+branch;
+code immediately following it in the action will
+not
+be executed.
+
-
yymore()
+tells the scanner that the next time it matches a rule, the corresponding
+token should be
+appended
+onto the current value of
+yytext
+rather than replacing it. For example, given the input "mega-kludge"
+the following will write "mega-mega-kludge" to the output:
+
+
+
%%
+ mega- ECHO; yymore();
+ kludge ECHO;
+
+
+First "mega-" is matched and echoed to the output. Then "kludge"
+is matched, but the previous "mega-" is still hanging around at the
+beginning of
+yytext
+so the
+ECHO
+for the "kludge" rule will actually write "mega-kludge".
+
+
+Two notes regarding use of
+yymore().
+First,
+yymore()
+depends on the value of
+yyleng
+correctly reflecting the size of the current token, so you must not
+modify
+yyleng
+if you are using
+yymore().
+Second, the presence of
+yymore()
+in the scanner's action entails a minor performance penalty in the
+scanner's matching speed.
+
-
yyless(n)
+returns all but the first
+n
+characters of the current token back to the input stream, where they
+will be rescanned when the scanner looks for the next match.
+yytext
+and
+yyleng
+are adjusted appropriately (e.g.,
+yyleng
+will now be equal to
+n
+). For example, on the input "foobar" the following will write out
+"foobarbar":
+
+
+
%%
+ foobar ECHO; yyless(3);
+ [a-z]+ ECHO;
+
+
+An argument of 0 to
+yyless
+will cause the entire current input string to be scanned again. Unless you've
+changed how the scanner will subsequently process its input (using
+BEGIN,
+for example), this will result in an endless loop.
+
+
+Note that
+yyless
+is a macro and can only be used in the flex input file, not from
+other source files.
+
-
unput(c)
+puts the character
+c
+back onto the input stream. It will be the next character scanned.
+The following action will take the current token and cause it
+to be rescanned enclosed in parentheses.
+
+Note that since each
+unput()
+puts the given character back at the
+beginning
+of the input stream, pushing back strings must be done back-to-front.
+
+
+An important potential problem when using
+unput()
+is that if you are using
+%pointer
+(the default), a call to
+unput()
+destroys
+the contents of
+yytext,
+starting with its rightmost character and devouring one character to
+the left with each call. If you need the value of yytext preserved
+after a call to
+unput()
+(as in the above example),
+you must either first copy it elsewhere, or build your scanner using
+%array
+instead (see How The Input Is Matched).
+
+
+Finally, note that you cannot put back
+EOF
+to attempt to mark the input stream with an end-of-file.
+
-
input()
+reads the next character from the input stream. For example,
+the following is one way to eat up C comments:
+
+
+
%%
+ "/*" {
+ register int c;
+
+
for ( ; ; )
+ {
+ while ( (c = input()) != '*' &&
+ c != EOF )
+ ; /* eat up text of comment */
+
+
if ( c == '*' )
+ {
+ while ( (c = input()) == '*' )
+ ;
+ if ( c == '/' )
+ break; /* found the end */
+ }
+
+
if ( c == EOF )
+ {
+ error( "EOF in comment" );
+ break;
+ }
+ }
+ }
+
+
+(Note that if the scanner is compiled using
+C++,
+then
+input()
+is instead referred to as
+yyinput(),
+in order to avoid a name clash with the
+C++
+stream by the name of
+input.)
+
-
YY_FLUSH_BUFFER
+flushes the scanner's internal buffer
+so that the next time the scanner attempts to match a token, it will
+first refill the buffer using
+YY_INPUT
+(see The Generated Scanner, below). This action is a special case
+of the more general
+yy_flush_buffer()
+function, described below in the section Multiple Input Buffers.
+
-
yyterminate()
+can be used in lieu of a return statement in an action. It terminates
+the scanner and returns a 0 to the scanner's caller, indicating "all done".
+By default,
+yyterminate()
+is also called when an end-of-file is encountered. It is a macro and
+may be redefined.
+
+
THE GENERATED SCANNER
+
+The output of
+flex
+is the file
+lex.yy.c,
+which contains the scanning routine
+yylex(),
+a number of tables used by it for matching tokens, and a number
+of auxiliary routines and macros. By default,
+yylex()
+is declared as follows:
+
+
+
int yylex()
+ {
+ ... various definitions and the actions in here ...
+ }
+
+
+(If your environment supports function prototypes, then it will
+be "int yylex( void )".) This definition may be changed by defining
+the "YY_DECL" macro. For example, you could use:
+
+
+
#define YY_DECL float lexscan( a, b ) float a, b;
+
+
+to give the scanning routine the name
+lexscan,
+returning a float, and taking two floats as arguments. Note that
+if you give arguments to the scanning routine using a
+K&R-style/non-prototyped function declaration, you must terminate
+the definition with a semi-colon (;).
+
+
+Whenever
+yylex()
+is called, it scans tokens from the global input file
+yyin
+(which defaults to stdin). It continues until it either reaches
+an end-of-file (at which point it returns the value 0) or
+one of its actions executes a
+return
+statement.
+
+
+If the scanner reaches an end-of-file, subsequent calls are undefined
+unless either
+yyin
+is pointed at a new input file (in which case scanning continues from
+that file), or
+yyrestart()
+is called.
+yyrestart()
+takes one argument, a
+FILE*
+pointer (which can be nil, if you've set up
+YY_INPUT
+to scan from a source other than
+yyin),
+and initializes
+yyin
+for scanning from that file. Essentially there is no difference between
+just assigning
+yyin
+to a new input file or using
+yyrestart()
+to do so; the latter is available for compatibility with previous versions
+of
+flex,
+and because it can be used to switch input files in the middle of scanning.
+It can also be used to throw away the current input buffer, by calling
+it with an argument of
+yyin;
+but better is to use
+YY_FLUSH_BUFFER
+(see above).
+Note that
+yyrestart()
+does
+not
+reset the start condition to
+INITIAL
+(see Start Conditions, below).
+
+
+If
+yylex()
+stops scanning due to executing a
+return
+statement in one of the actions, the scanner may then be called again and it
+will resume scanning where it left off.
+
+
+By default (and for purposes of efficiency), the scanner uses
+block-reads rather than simple
+getc()
+calls to read characters from
+yyin.
+The nature of how it gets its input can be controlled by defining the
+YY_INPUT
+macro.
+YY_INPUT's calling sequence is "YY_INPUT(buf,result,max_size)". Its
+action is to place up to
+max_size
+characters in the character array
+buf
+and return in the integer variable
+result
+either the
+number of characters read or the constant YY_NULL (0 on Unix systems)
+to indicate EOF. The default YY_INPUT reads from the
+global file-pointer "yyin".
+
+
+A sample definition of YY_INPUT (in the definitions
+section of the input file):
+
+This definition will change the input processing to occur
+one character at a time.
+
+
+When the scanner receives an end-of-file indication from YY_INPUT,
+it then checks the
+yywrap()
+function. If
+yywrap()
+returns false (zero), then it is assumed that the
+function has gone ahead and set up
+yyin
+to point to another input file, and scanning continues. If it returns
+true (non-zero), then the scanner terminates, returning 0 to its
+caller. Note that in either case, the start condition remains unchanged;
+it does
+not
+revert to
+INITIAL.
+
+
+If you do not supply your own version of
+yywrap(),
+then you must either use
+%optionnoyywrap
+(in which case the scanner behaves as though
+yywrap()
+returned 1), or you must link with
+-lfl
+to obtain the default version of the routine, which always returns 1.
+
+
+Three routines are available for scanning from in-memory buffers rather
+than files:
+yy_scan_string(),yy_scan_bytes(),
+and
+yy_scan_buffer().
+See the discussion of them below in the section Multiple Input Buffers.
+
+
+The scanner writes its
+ECHO
+output to the
+yyout
+global (default, stdout), which may be redefined by the user simply
+by assigning it to some other
+FILE
+pointer.
+
+
START CONDITIONS
+
+flex
+provides a mechanism for conditionally activating rules. Any rule
+whose pattern is prefixed with "<sc>" will only be active when
+the scanner is in the start condition named "sc". For example,
+
+
+
<STRING>[^"]* { /* eat up the string body ... */
+ ...
+ }
+
+
+will be active only when the scanner is in the "STRING" start
+condition, and
+
+will be active only when the current start condition is
+either "INITIAL", "STRING", or "QUOTE".
+
+
+Start conditions
+are declared in the definitions (first) section of the input
+using unindented lines beginning with either
+%s
+or
+%x
+followed by a list of names.
+The former declares
+inclusive
+start conditions, the latter
+exclusive
+start conditions. A start condition is activated using the
+BEGIN
+action. Until the next
+BEGIN
+action is executed, rules with the given start
+condition will be active and
+rules with other start conditions will be inactive.
+If the start condition is
+inclusive,
+then rules with no start conditions at all will also be active.
+If it is
+exclusive,
+then
+only
+rules qualified with the start condition will be active.
+A set of rules contingent on the same exclusive start condition
+describe a scanner which is independent of any of the other rules in the
+flex
+input. Because of this,
+exclusive start conditions make it easy to specify "mini-scanners"
+which scan portions of the input that are syntactically different
+from the rest (e.g., comments).
+
+
+If the distinction between inclusive and exclusive start conditions
+is still a little vague, here's a simple example illustrating the
+connection between the two. The set of rules:
+
+
+
%s example
+ %%
+
+
<example>foo do_something();
+
+
bar something_else();
+
+
+is equivalent to
+
+
+
%x example
+ %%
+
+
<example>foo do_something();
+
+
<INITIAL,example>bar something_else();
+
+
+Without the
+<INITIAL,example>
+qualifier, the
+bar
+pattern in the second example wouldn't be active (i.e., couldn't match)
+when in start condition
+example.
+If we just used
+<example>
+to qualify
+bar,
+though, then it would only be active in
+example
+and not in
+INITIAL,
+while in the first example it's active in both, because in the first
+example the
+example
+startion condition is an
+inclusive
+(%s)
+start condition.
+
+
+Also note that the special start-condition specifier
+<*>
+matches every start condition. Thus, the above example could also
+have been written;
+
+
+
%x example
+ %%
+
+
<example>foo do_something();
+
+
<*>bar something_else();
+
+
+
+
+The default rule (to
+ECHO
+any unmatched character) remains active in start conditions. It
+is equivalent to:
+
+
+
<*>.|\n ECHO;
+
+
+
+
+BEGIN(0)
+returns to the original state where only the rules with
+no start conditions are active. This state can also be
+referred to as the start-condition "INITIAL", so
+BEGIN(INITIAL)
+is equivalent to
+BEGIN(0).
+(The parentheses around the start condition name are not required but
+are considered good style.)
+
+
+BEGIN
+actions can also be given as indented code at the beginning
+of the rules section. For example, the following will cause
+the scanner to enter the "SPECIAL" start condition whenever
+yylex()
+is called and the global variable
+enter_special
+is true:
+
+
+
int enter_special;
+
+
%x SPECIAL
+ %%
+ if ( enter_special )
+ BEGIN(SPECIAL);
+
+
+To illustrate the uses of start conditions,
+here is a scanner which provides two different interpretations
+of a string like "123.456". By default it will treat it as
+as three tokens, the integer "123", a dot ('.'), and the integer "456".
+But if the string is preceded earlier in the line by the string
+"expect-floats"
+it will treat it as a single token, the floating-point number
+123.456:
+
+
+
%{
+ #include <math.h>
+ %}
+ %s expect
+
+
%%
+ expect-floats BEGIN(expect);
+
+
<expect>[0-9]+"."[0-9]+ {
+ printf( "found a float, = %f\n",
+ atof( yytext ) );
+ }
+ <expect>\n {
+ /* that's the end of the line, so
+ * we need another "expect-number"
+ * before we'll recognize any more
+ * numbers
+ */
+ BEGIN(INITIAL);
+ }
+
+
+Here is a scanner which recognizes (and discards) C comments while
+maintaining a count of the current input line.
+
+
+
%x comment
+ %%
+ int line_num = 1;
+
+
"/*" BEGIN(comment);
+
+
<comment>[^*\n]* /* eat anything that's not a '*' */
+ <comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+ <comment>\n ++line_num;
+ <comment>"*"+"/" BEGIN(INITIAL);
+
+
+This scanner goes to a bit of trouble to match as much
+text as possible with each rule. In general, when attempting to write
+a high-speed scanner try to match as much possible in each rule, as
+it's a big win.
+
+
+Note that start-conditions names are really integer values and
+can be stored as such. Thus, the above could be extended in the
+following fashion:
+
+
+
%x comment foo
+ %%
+ int line_num = 1;
+ int comment_caller;
+
+
<comment>[^*\n]* /* eat anything that's not a '*' */
+ <comment>"*"+[^*/\n]* /* eat up '*'s not followed by '/'s */
+ <comment>\n ++line_num;
+ <comment>"*"+"/" BEGIN(comment_caller);
+
+
+Furthermore, you can access the current start condition using
+the integer-valued
+YY_START
+macro. For example, the above assignments to
+comment_caller
+could instead be written
+
+
+
comment_caller = YY_START;
+
+
+Flex provides
+YYSTATE
+as an alias for
+YY_START
+(since that is what's used by AT&T
+lex).
+
+
+Note that start conditions do not have their own name-space; %s's and %x's
+declare names in the same fashion as #define's.
+
+
+Finally, here's an example of how to match C-style quoted strings using
+exclusive start conditions, including expanded escape sequences (but
+not including checking for a string that's too long):
+
+Often, such as in some of the examples above, you wind up writing a
+whole bunch of rules all preceded by the same start condition(s). Flex
+makes this a little easier and cleaner by introducing a notion of
+start condition
+scope.
+A start condition scope is begun with:
+
+
+
<SCs>{
+
+
+where
+SCs
+is a list of one or more start conditions. Inside the start condition
+scope, every rule automatically has the prefix
+<SCs>
+applied to it, until a
+'}'
+which matches the initial
+'{'.
+So, for example,
+
+Three routines are available for manipulating stacks of start conditions:
+
voidyy_push_state(intnew_state)
+
pushes the current start condition onto the top of the start condition
+stack and switches to
+new_state
+as though you had used
+BEGINnew_state
+(recall that start condition names are also integers).
+
voidyy_pop_state()
+
pops the top of the stack and switches to it via
+BEGIN.
+
intyy_top_state()
+
returns the top of the stack without altering the stack's contents.
+
+
+The start condition stack grows dynamically and so has no built-in
+size limitation. If memory is exhausted, program execution aborts.
+
+
+To use start condition stacks, your scanner must include a
+%optionstack
+directive (see Options below).
+
+
MULTIPLE INPUT BUFFERS
+
+Some scanners (such as those which support "include" files)
+require reading from several input streams. As
+flex
+scanners do a large amount of buffering, one cannot control
+where the next input will be read from by simply writing a
+YY_INPUT
+which is sensitive to the scanning context.
+YY_INPUT
+is only called when the scanner reaches the end of its buffer, which
+may be a long time after scanning a statement such as an "include"
+which requires switching the input source.
+
+
+To negotiate these sorts of problems,
+flex
+provides a mechanism for creating and switching between multiple
+input buffers. An input buffer is created by using:
+
+
+
YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+
+
+which takes a
+FILE
+pointer and a size and creates a buffer associated with the given
+file and large enough to hold
+size
+characters (when in doubt, use
+YY_BUF_SIZE
+for the size). It returns a
+YY_BUFFER_STATE
+handle, which may then be passed to other routines (see below). The
+YY_BUFFER_STATE
+type is a pointer to an opaque
+structyy_buffer_state
+structure, so you may safely initialize YY_BUFFER_STATE variables to
+((YY_BUFFER_STATE)0)
+if you wish, and also refer to the opaque structure in order to
+correctly declare input buffers in source files other than that
+of your scanner. Note that the
+FILE
+pointer in the call to
+yy_create_buffer
+is only used as the value of
+yyin
+seen by
+YY_INPUT;
+if you redefine
+YY_INPUT
+so it no longer uses
+yyin,
+then you can safely pass a nil
+FILE
+pointer to
+yy_create_buffer.
+You select a particular buffer to scan from using:
+
+switches the scanner's input buffer so subsequent tokens will
+come from
+new_buffer.
+Note that
+yy_switch_to_buffer()
+may be used by yywrap() to set things up for continued scanning, instead
+of opening a new file and pointing
+yyin
+at it. Note also that switching input sources via either
+yy_switch_to_buffer()
+or
+yywrap()
+does
+not
+change the start condition.
+
+This function discards the buffer's contents,
+so the next time the scanner attempts to match a token from the
+buffer, it will first fill the buffer anew using
+YY_INPUT.
+
+
+yy_new_buffer()
+is an alias for
+yy_create_buffer(),
+provided for compatibility with the C++ use of
+new
+and
+delete
+for creating and destroying dynamic objects.
+
+
+Finally, the
+YY_CURRENT_BUFFER
+macro returns a
+YY_BUFFER_STATE
+handle to the current buffer.
+
+
+Here is an example of using these features for writing a scanner
+which expands include files (the
+<<EOF>>
+feature is discussed below):
+
+
+
/* the "incl" state is used for picking up the name
+ * of an include file
+ */
+ %x incl
+
+
+Three routines are available for setting up input buffers for
+scanning in-memory strings instead of files. All of them create
+a new input buffer for scanning the string, and return a corresponding
+YY_BUFFER_STATE
+handle (which you should delete with
+yy_delete_buffer()
+when done with it). They also switch to the new buffer using
+yy_switch_to_buffer(),
+so the next call to
+yylex()
+will start scanning the string.
+
+Note that both of these functions create and scan a
+copy
+of the string or bytes. (This may be desirable, since
+yylex()
+modifies the contents of the buffer it is scanning.) You can avoid the
+copy by using:
+
yy_scan_buffer(char*base,yy_size_tsize)
+
which scans in place the buffer starting at
+base,
+consisting of
+size
+bytes, the last two bytes of which
+must
+be
+YY_END_OF_BUFFER_CHAR
+(ASCII NUL).
+These last two bytes are not scanned; thus, scanning
+consists of
+base[0]
+through
+base[size-2],
+inclusive.
+
If you fail to set up
+base
+in this manner (i.e., forget the final two
+YY_END_OF_BUFFER_CHAR
+bytes), then
+yy_scan_buffer()
+returns a nil pointer instead of creating a new input buffer.
+
The type
+yy_size_t
+is an integral type to which you can cast an integer expression
+reflecting the size of the buffer.
+
+
END-OF-FILE RULES
+
+The special rule "<<EOF>>" indicates
+actions which are to be taken when an end-of-file is
+encountered and yywrap() returns non-zero (i.e., indicates
+no further files to process). The action must finish
+by doing one of four things:
+
-
assigning
+yyin
+to a new input file (in previous versions of flex, after doing the
+assignment you had to call the special action
+YY_NEW_FILE;
+this is no longer necessary);
+
-
executing a
+return
+statement;
+
-
executing the special
+yyterminate()
+action;
+
-
or, switching to a new buffer using
+yy_switch_to_buffer()
+as shown in the example above.
+
+
+<<EOF>> rules may not be used with other
+patterns; they may only be qualified with a list of start
+conditions. If an unqualified <<EOF>> rule is given, it
+applies to
+all
+start conditions which do not already have <<EOF>> actions. To
+specify an <<EOF>> rule for only the initial start condition, use
+
+
+
<INITIAL><<EOF>>
+
+
+
+
+These rules are useful for catching things like unclosed comments.
+An example:
+
+
+The macro
+YY_USER_ACTION
+can be defined to provide an action
+which is always executed prior to the matched rule's action. For example,
+it could be #define'd to call a routine to convert yytext to lower-case.
+When
+YY_USER_ACTION
+is invoked, the variable
+yy_act
+gives the number of the matched rule (rules are numbered starting with 1).
+Suppose you want to profile how often each of your rules is matched. The
+following would do the trick:
+
+
+
#define YY_USER_ACTION ++ctr[yy_act]
+
+
+where
+ctr
+is an array to hold the counts for the different rules. Note that
+the macro
+YY_NUM_RULES
+gives the total number of rules (including the default rule, even if
+you use
+-s),
+so a correct declaration for
+ctr
+is:
+
+
+
int ctr[YY_NUM_RULES];
+
+
+
+
+The macro
+YY_USER_INIT
+may be defined to provide an action which is always executed before
+the first scan (and before the scanner's internal initializations are done).
+For example, it could be used to call a routine to read
+in a data table or open a logging file.
+
+
+The macro
+yy_set_interactive(is_interactive)
+can be used to control whether the current buffer is considered
+interactive.
+An interactive buffer is processed more slowly,
+but must be used when the scanner's input source is indeed
+interactive to avoid problems due to waiting to fill buffers
+(see the discussion of the
+-I
+flag below). A non-zero value
+in the macro invocation marks the buffer as interactive, a zero
+value as non-interactive. Note that use of this macro overrides
+%optionalways-interactive
+or
+%optionnever-interactive
+(see Options below).
+yy_set_interactive()
+must be invoked prior to beginning to scan the buffer that is
+(or is not) to be considered interactive.
+
+
+The macro
+yy_set_bol(at_bol)
+can be used to control whether the current buffer's scanning
+context for the next token match is done as though at the
+beginning of a line. A non-zero macro argument makes rules anchored with
+'^' active, while a zero argument makes '^' rules inactive.
+
+
+The macro
+YY_AT_BOL()
+returns true if the next token scanned from the current buffer
+will have '^' rules active, false otherwise.
+
+
+In the generated scanner, the actions are all gathered in one large
+switch statement and separated using
+YY_BREAK,
+which may be redefined. By default, it is simply a "break", to separate
+each rule's action from the following rule's.
+Redefining
+YY_BREAK
+allows, for example, C++ users to
+#define YY_BREAK to do nothing (while being very careful that every
+rule ends with a "break" or a "return"!) to avoid suffering from
+unreachable statement warnings where because a rule's action ends with
+"return", the
+YY_BREAK
+is inaccessible.
+
+
VALUES AVAILABLE TO THE USER
+
+This section summarizes the various values available to the user
+in the rule actions.
+
-
char*yytext
+holds the text of the current token. It may be modified but not lengthened
+(you cannot append characters to the end).
+
If the special directive
+%array
+appears in the first section of the scanner description, then
+yytext
+is instead declared
+charyytext[YYLMAX],
+where
+YYLMAX
+is a macro definition that you can redefine in the first section
+if you don't like the default value (generally 8KB). Using
+%array
+results in somewhat slower scanners, but the value of
+yytext
+becomes immune to calls to
+input()
+and
+unput(),
+which potentially destroy its value when
+yytext
+is a character pointer. The opposite of
+%array
+is
+%pointer,
+which is the default.
+
You cannot use
+%array
+when generating C++ scanner classes
+(the
+-+
+flag).
+
-
intyyleng
+holds the length of the current token.
+
-
FILE*yyin
+is the file which by default
+flex
+reads from. It may be redefined but doing so only makes sense before
+scanning begins or after an EOF has been encountered. Changing it in
+the midst of scanning will have unexpected results since
+flex
+buffers its input; use
+yyrestart()
+instead.
+Once scanning terminates because an end-of-file
+has been seen, you can assign
+yyin
+at the new input file and then call the scanner again to continue scanning.
+
-
voidyyrestart(FILE*new_file)
+may be called to point
+yyin
+at the new input file. The switch-over to the new file is immediate
+(any previously buffered-up input is lost). Note that calling
+yyrestart()
+with
+yyin
+as an argument thus throws away the current input buffer and continues
+scanning the same input file.
+
-
FILE*yyout
+is the file to which
+ECHO
+actions are done. It can be reassigned by the user.
+
-
YY_CURRENT_BUFFER
+returns a
+YY_BUFFER_STATE
+handle to the current buffer.
+
-
YY_START
+returns an integer value corresponding to the current start
+condition. You can subsequently use this value with
+BEGIN
+to return to that start condition.
+
+
INTERFACING WITH YACC
+
+One of the main uses of
+flex
+is as a companion to the
+yacc
+parser-generator.
+yacc
+parsers expect to call a routine named
+yylex()
+to find the next input token. The routine is supposed to
+return the type of the next token as well as putting any associated
+value in the global
+yylval.
+To use
+flex
+with
+yacc,
+one specifies the
+-d
+option to
+yacc
+to instruct it to generate the file
+y.tab.h
+containing definitions of all the
+%tokens
+appearing in the
+yacc
+input. This file is then included in the
+flex
+scanner. For example, if one of the tokens is "TOK_NUMBER",
+part of the scanner might look like:
+
Generate backing-up information to
+lex.backup.
+This is a list of scanner states which require backing up
+and the input characters on which they do so. By adding rules one
+can remove backing-up states. If
+all
+backing-up states are eliminated and
+-Cf
+or
+-CF
+is used, the generated scanner will run faster (see the
+-p
+flag). Only users who wish to squeeze every last cycle out of their
+scanners need worry about this option. (See the section on Performance
+Considerations below.)
+
-c
+
is a do-nothing, deprecated option included for POSIX compliance.
+
-d
+
makes the generated scanner run in
+debug
+mode. Whenever a pattern is recognized and the global
+yy_flex_debug
+is non-zero (which is the default),
+the scanner will write to
+stderr
+a line of the form:
+
+
+
--accepting rule at line 53 ("the matched text")
+
+
+The line number refers to the location of the rule in the file
+defining the scanner (i.e., the file that was fed to flex). Messages
+are also generated when the scanner backs up, accepts the
+default rule, reaches the end of its input buffer (or encounters
+a NUL; at this point, the two look the same as far as the scanner's concerned),
+or reaches an end-of-file.
+
-f
+
specifies
+fastscanner.
+No table compression is done and stdio is bypassed.
+The result is large but fast. This option is equivalent to
+-Cfr
+(see below).
+
-h
+
generates a "help" summary of
+flex's
+options to
+stdout
+and then exits.
+-?
+and
+--help
+are synonyms for
+-h.
+
-i
+
instructs
+flex
+to generate a
+case-insensitive
+scanner. The case of letters given in the
+flex
+input patterns will
+be ignored, and tokens in the input will be matched regardless of case. The
+matched text given in
+yytext
+will have the preserved case (i.e., it will not be folded).
+
-l
+
turns on maximum compatibility with the original AT&T
+lex
+implementation. Note that this does not mean
+full
+compatibility. Use of this option costs a considerable amount of
+performance, and it cannot be used with the
+-+,-f,-F,-Cf,
+or
+-CF
+options. For details on the compatibilities it provides, see the section
+"Incompatibilities With Lex And POSIX" below. This option also results
+in the name
+YY_FLEX_LEX_COMPAT
+being #define'd in the generated scanner.
+
-n
+
is another do-nothing, deprecated option included only for
+POSIX compliance.
+
-p
+
generates a performance report to stderr. The report
+consists of comments regarding features of the
+flex
+input file which will cause a serious loss of performance in the resulting
+scanner. If you give the flag twice, you will also get comments regarding
+features that lead to minor performance losses.
+
Note that the use of
+REJECT,
+%optionyylineno,
+and variable trailing context (see the Deficiencies / Bugs section below)
+entails a substantial performance penalty; use of
+yymore(),
+the
+^
+operator,
+and the
+-I
+flag entail minor performance penalties.
+
-s
+
causes the
+defaultrule
+(that unmatched scanner input is echoed to
+stdout)
+to be suppressed. If the scanner encounters input that does not
+match any of its rules, it aborts with an error. This option is
+useful for finding holes in a scanner's rule set.
+
-t
+
instructs
+flex
+to write the scanner it generates to standard output instead
+of
+lex.yy.c.
+
-v
+
specifies that
+flex
+should write to
+stderr
+a summary of statistics regarding the scanner it generates.
+Most of the statistics are meaningless to the casual
+flex
+user, but the first line identifies the version of
+flex
+(same as reported by
+-V),
+and the next line the flags used when generating the scanner, including
+those that are on by default.
+
-w
+
suppresses warning messages.
+
-B
+
instructs
+flex
+to generate a
+batch
+scanner, the opposite of
+interactive
+scanners generated by
+-I
+(see below). In general, you use
+-B
+when you are
+certain
+that your scanner will never be used interactively, and you want to
+squeeze a
+little
+more performance out of it. If your goal is instead to squeeze out a
+lot
+more performance, you should be using the
+-Cf
+or
+-CF
+options (discussed below), which turn on
+-B
+automatically anyway.
+
-F
+
specifies that the
+fast
+scanner table representation should be used (and stdio
+bypassed). This representation is
+about as fast as the full table representation
+(-f),
+and for some sets of patterns will be considerably smaller (and for
+others, larger). In general, if the pattern set contains both "keywords"
+and a catch-all, "identifier" rule, such as in the set:
+
+then you're better off using the full table representation. If only
+the "identifier" rule is present and you then use a hash table or some such
+to detect the keywords, you're better off using
+-F.
+
This option is equivalent to
+-CFr
+(see below). It cannot be used with
+-+.
+
-I
+
instructs
+flex
+to generate an
+interactive
+scanner. An interactive scanner is one that only looks ahead to decide
+what token has been matched if it absolutely must. It turns out that
+always looking one extra character ahead, even if the scanner has already
+seen enough text to disambiguate the current token, is a bit faster than
+only looking ahead when necessary. But scanners that always look ahead
+give dreadful interactive performance; for example, when a user types
+a newline, it is not recognized as a newline token until they enter
+another
+token, which often means typing in another whole line.
+
Flex
+scanners default to
+interactive
+unless you use the
+-Cf
+or
+-CF
+table-compression options (see below). That's because if you're looking
+for high-performance you should be using one of these options, so if you
+didn't,
+flex
+assumes you'd rather trade off a bit of run-time performance for intuitive
+interactive behavior. Note also that you
+cannot
+use
+-I
+in conjunction with
+-Cf
+or
+-CF.
+Thus, this option is not really needed; it is on by default for all those
+cases in which it is allowed.
+
You can force a scanner to
+not
+be interactive by using
+-B
+(see above).
+
-L
+
instructs
+flex
+not to generate
+#line
+directives. Without this option,
+flex
+peppers the generated scanner
+with #line directives so error messages in the actions will be correctly
+located with respect to either the original
+flex
+input file (if the errors are due to code in the input file), or
+lex.yy.c
+(if the errors are
+flex's
+fault -- you should report these sorts of errors to the email address
+given below).
+
-T
+
makes
+flex
+run in
+trace
+mode. It will generate a lot of messages to
+stderr
+concerning
+the form of the input and the resultant non-deterministic and deterministic
+finite automata. This option is mostly for use in maintaining
+flex.
+
-V
+
prints the version number to
+stdout
+and exits.
+--version
+is a synonym for
+-V.
+
-7
+
instructs
+flex
+to generate a 7-bit scanner, i.e., one which can only recognized 7-bit
+characters in its input. The advantage of using
+-7
+is that the scanner's tables can be up to half the size of those generated
+using the
+-8
+option (see below). The disadvantage is that such scanners often hang
+or crash if their input contains an 8-bit character.
+
Note, however, that unless you generate your scanner using the
+-Cf
+or
+-CF
+table compression options, use of
+-7
+will save only a small amount of table space, and make your scanner
+considerably less portable.
+Flex's
+default behavior is to generate an 8-bit scanner unless you use the
+-Cf
+or
+-CF,
+in which case
+flex
+defaults to generating 7-bit scanners unless your site was always
+configured to generate 8-bit scanners (as will often be the case
+with non-USA sites). You can tell whether flex generated a 7-bit
+or an 8-bit scanner by inspecting the flag summary in the
+-v
+output as described above.
+
Note that if you use
+-Cfe
+or
+-CFe
+(those table compression options, but also using equivalence classes as
+discussed see below), flex still defaults to generating an 8-bit
+scanner, since usually with these compression options full 8-bit tables
+are not much more expensive than 7-bit tables.
+
-8
+
instructs
+flex
+to generate an 8-bit scanner, i.e., one which can recognize 8-bit
+characters. This flag is only needed for scanners generated using
+-Cf
+or
+-CF,
+as otherwise flex defaults to generating an 8-bit scanner anyway.
+
See the discussion of
+-7
+above for flex's default behavior and the tradeoffs between 7-bit
+and 8-bit scanners.
+
-+
+
specifies that you want flex to generate a C++
+scanner class. See the section on Generating C++ Scanners below for
+details.
+
-C[aefFmr]
+
controls the degree of table compression and, more generally, trade-offs
+between small scanners and fast scanners.
+
-Ca
+("align") instructs flex to trade off larger tables in the
+generated scanner for faster performance because the elements of
+the tables are better aligned for memory access and computation. On some
+RISC architectures, fetching and manipulating longwords is more efficient
+than with smaller-sized units such as shortwords. This option can
+double the size of the tables used by your scanner.
+
-Ce
+directs
+flex
+to construct
+equivalenceclasses,
+i.e., sets of characters
+which have identical lexical properties (for example, if the only
+appearance of digits in the
+flex
+input is in the character class
+"[0-9]" then the digits '0', '1', ..., '9' will all be put
+in the same equivalence class). Equivalence classes usually give
+dramatic reductions in the final table/object file sizes (typically
+a factor of 2-5) and are pretty cheap performance-wise (one array
+look-up per character scanned).
+
-Cf
+specifies that the
+full
+scanner tables should be generated -
+flex
+should not compress the
+tables by taking advantages of similar transition functions for
+different states.
+
-CF
+specifies that the alternate fast scanner representation (described
+above under the
+-F
+flag)
+should be used. This option cannot be used with
+-+.
+
-Cm
+directs
+flex
+to construct
+meta-equivalenceclasses,
+which are sets of equivalence classes (or characters, if equivalence
+classes are not being used) that are commonly used together. Meta-equivalence
+classes are often a big win when using compressed tables, but they
+have a moderate performance impact (one or two "if" tests and one
+array look-up per character scanned).
+
-Cr
+causes the generated scanner to
+bypass
+use of the standard I/O library (stdio) for input. Instead of calling
+fread()
+or
+getc(),
+the scanner will use the
+read()
+system call, resulting in a performance gain which varies from system
+to system, but in general is probably negligible unless you are also using
+-Cf
+or
+-CF.
+Using
+-Cr
+can cause strange behavior if, for example, you read from
+yyin
+using stdio prior to calling the scanner (because the scanner will miss
+whatever text your previous reads left in the stdio input buffer).
+
-Cr
+has no effect if you define
+YY_INPUT
+(see The Generated Scanner above).
+
A lone
+-C
+specifies that the scanner tables should be compressed but neither
+equivalence classes nor meta-equivalence classes should be used.
+
The options
+-Cf
+or
+-CF
+and
+-Cm
+do not make sense together - there is no opportunity for meta-equivalence
+classes if the table is not being compressed. Otherwise the options
+may be freely mixed, and are cumulative.
+
The default setting is
+-Cem,
+which specifies that
+flex
+should generate equivalence classes
+and meta-equivalence classes. This setting provides the highest
+degree of table compression. You can trade off
+faster-executing scanners at the cost of larger tables with
+the following generally being true:
+
+Note that scanners with the smallest tables are usually generated and
+compiled the quickest, so
+during development you will usually want to use the default, maximal
+compression.
+
-Cfe
+is often a good compromise between speed and size for production
+scanners.
+
-ooutput
+
directs flex to write the scanner to the file
+output
+instead of
+lex.yy.c.
+If you combine
+-o
+with the
+-t
+option, then the scanner is written to
+stdout
+but its
+#line
+directives (see the
+\-L
+option above) refer to the file
+output.
+
-Pprefix
+
changes the default
+yy
+prefix used by
+flex
+for all globally-visible variable and function names to instead be
+prefix.
+For example,
+-Pfoo
+changes the name of
+yytext
+to
+footext.
+It also changes the name of the default output file from
+lex.yy.c
+to
+lex.foo.c.
+Here are all of the names affected:
+
+(If you are using a C++ scanner, then only
+yywrap
+and
+yyFlexLexer
+are affected.)
+Within your scanner itself, you can still refer to the global variables
+and functions using either version of their name; but externally, they
+have the modified name.
+
This option lets you easily link together multiple
+flex
+programs into the same executable. Note, though, that using this
+option also renames
+yywrap(),
+so you now
+must
+either
+provide your own (appropriately-named) version of the routine for your
+scanner, or use
+%optionnoyywrap,
+as linking with
+-lfl
+no longer provides one for you by default.
+
-Sskeleton_file
+
overrides the default skeleton file from which
+flex
+constructs its scanners. You'll never need this option unless you are doing
+flex
+maintenance or development.
+
+
+flex
+also provides a mechanism for controlling options within the
+scanner specification itself, rather than from the flex command-line.
+This is done by including
+%option
+directives in the first section of the scanner specification.
+You can specify multiple options with a single
+%option
+directive, and multiple directives in the first section of your flex input
+file. Most
+options are given simply as names, optionally preceded by the
+word "no" (with no intervening whitespace) to negate their meaning.
+A number are equivalent to flex flags or their negation:
+
array equivalent to "%array"
+ pointer equivalent to "%pointer" (default)
+
+
+Some
+%option's
+provide features otherwise not available:
+
always-interactive
+
instructs flex to generate a scanner which always considers its input
+"interactive". Normally, on each new input file the scanner calls
+isatty()
+in an attempt to determine whether
+the scanner's input source is interactive and thus should be read a
+character at a time. When this option is used, however, then no
+such call is made.
+
main
+
directs flex to provide a default
+main()
+program for the scanner, which simply calls
+yylex().
+This option implies
+noyywrap
+(see below).
+
never-interactive
+
instructs flex to generate a scanner which never considers its input
+"interactive" (again, no call made to
+isatty()).
+This is the opposite of
+always-interactive.
+
stack
+
enables the use of start condition stacks (see Start Conditions above).
+
stdinit
+
if unset (i.e.,
+%optionnostdinit)
+initializes
+yyin
+and
+yyout
+to nil
+FILE
+pointers, instead of
+stdin
+and
+stdout.
+
yylineno
+
directs
+flex
+to generate a scanner that maintains the number of the current line
+read from its input in the global variable
+yylineno.
+This option is implied by
+%optionlex-compat.
+
yywrap
+
if unset (i.e.,
+%optionnoyywrap),
+makes the scanner not call
+yywrap()
+upon an end-of-file, but simply assume that there are no more
+files to scan (until the user points
+yyin
+at a new file and calls
+yylex()
+again).
+
+
+flex
+scans your rule actions to determine whether you use the
+REJECT
+or
+yymore()
+features. The
+reject
+and
+yymore
+options are available to override its decision as to whether you use the
+options, either by setting them (e.g.,
+%optionreject)
+to indicate the feature is indeed used, or
+unsetting them to indicate it actually is not used
+(e.g.,
+%optionnoyymore).
+
+
+Three options take string-delimited values, offset with '=':
+
+
+
%option outfile="ABC"
+
+
+is equivalent to
+-oABC,
+and
+
+
+
%option prefix="XYZ"
+
+
+is equivalent to
+-PXYZ.
+Finally,
+
+
+
%option yyclass="foo"
+
+
+only applies when generating a C++ scanner (
+-+
+option). It informs
+flex
+that you have derived
+foo
+as a subclass of
+yyFlexLexer,
+so
+flex
+will place your actions in the member function
+foo::yylex()
+instead of
+yyFlexLexer::yylex().
+It also generates a
+yyFlexLexer::yylex()
+member function that emits a run-time error (by invoking
+yyFlexLexer::LexerError())
+if called.
+See Generating C++ Scanners, below, for additional information.
+
+
+A number of options are available for lint purists who want to suppress
+the appearance of unneeded routines in the generated scanner. Each of the
+following, if unset, results in the corresponding routine not appearing in
+the generated scanner:
+
+(though
+yy_push_state()
+and friends won't appear anyway unless you use
+%optionstack).
+
+
PERFORMANCE CONSIDERATIONS
+
+The main design goal of
+flex
+is that it generate high-performance scanners. It has been optimized
+for dealing well with large sets of rules. Aside from the effects on
+scanner speed of the table compression
+-C
+options outlined above,
+there are a number of options/actions which degrade performance. These
+are, from most expensive to least:
+
pattern sets that require backing up
+ %array
+ %option interactive
+ %option always-interactive
+
+
'^' beginning-of-line operator
+ yymore()
+
+
+with the first three all being quite expensive and the last two
+being quite cheap. Note also that
+unput()
+is implemented as a routine call that potentially does quite a bit of
+work, while
+yyless()
+is a quite-cheap macro; so if just putting back some excess text you
+scanned, use
+yyless().
+
+
+REJECT
+should be avoided at all costs when performance is important.
+It is a particularly expensive option.
+
+
+Getting rid of backing up is messy and often may be an enormous
+amount of work for a complicated scanner. In principal, one begins
+by using the
+-b
+flag to generate a
+lex.backup
+file. For example, on the input
+
State #6 is non-accepting -
+ associated rule line numbers:
+ 2 3
+ out-transitions: [ o ]
+ jam-transitions: EOF [ \001-n p-\177 ]
+
+
State #8 is non-accepting -
+ associated rule line numbers:
+ 3
+ out-transitions: [ a ]
+ jam-transitions: EOF [ \001-` b-\177 ]
+
+
State #9 is non-accepting -
+ associated rule line numbers:
+ 3
+ out-transitions: [ r ]
+ jam-transitions: EOF [ \001-q s-\177 ]
+
+
Compressed tables always back up.
+
+
+The first few lines tell us that there's a scanner state in
+which it can make a transition on an 'o' but not on any other
+character, and that in that state the currently scanned text does not match
+any rule. The state occurs when trying to match the rules found
+at lines 2 and 3 in the input file.
+If the scanner is in that state and then reads
+something other than an 'o', it will have to back up to find
+a rule which is matched. With
+a bit of headscratching one can see that this must be the
+state it's in when it has seen "fo". When this has happened,
+if anything other than another 'o' is seen, the scanner will
+have to back up to simply match the 'f' (by the default rule).
+
+
+The comment regarding State #8 indicates there's a problem
+when "foob" has been scanned. Indeed, on any character other
+than an 'a', the scanner will have to back up to accept "foo".
+Similarly, the comment for State #9 concerns when "fooba" has
+been scanned and an 'r' does not follow.
+
+
+The final comment reminds us that there's no point going to
+all the trouble of removing backing up from the rules unless
+we're using
+-Cf
+or
+-CF,
+since there's no performance gain doing so with compressed scanners.
+
+
+The way to remove the backing up is to add "error" rules:
+
+This is usually the best solution when appropriate.
+
+
+Backing up messages tend to cascade.
+With a complicated set of rules it's not uncommon to get hundreds
+of messages. If one can decipher them, though, it often
+only takes a dozen or so rules to eliminate the backing up (though
+it's easy to make a mistake and have an error rule accidentally match
+a valid token. A possible future
+flex
+feature will be to automatically add rules to eliminate backing up).
+
+
+It's important to keep in mind that you gain the benefits of eliminating
+backing up only if you eliminate
+every
+instance of backing up. Leaving just one means you gain nothing.
+
+
+Variable
+trailing context (where both the leading and trailing parts do not have
+a fixed length) entails almost the same performance loss as
+REJECT
+(i.e., substantial). So when possible a rule like:
+
+Note that here the special '|' action does
+not
+provide any savings, and can even make things worse (see
+Deficiencies / Bugs below).
+
+
+Another area where the user can increase a scanner's performance
+(and one that's easier to implement) arises from the fact that
+the longer the tokens matched, the faster the scanner will run.
+This is because with long tokens the processing of most input
+characters takes place in the (short) inner scanning loop, and
+does not often have to go through the additional work of setting up
+the scanning environment (e.g.,
+yytext)
+for the action. Recall the scanner for C comments:
+
+Now instead of each newline requiring the processing of another
+action, recognizing the newlines is "distributed" over the other rules
+to keep the matched text as long as possible. Note that
+adding
+rules does
+not
+slow down the scanner! The speed of the scanner is independent
+of the number of rules or (modulo the considerations given at the
+beginning of this section) how complicated the rules are with
+regard to operators such as '*' and '|'.
+
+
+A final example in speeding up a scanner: suppose you want to scan
+through a file containing identifiers and keywords, one per line
+and with no other extraneous characters, and recognize all the
+keywords. A natural first approach is:
+
+
+
%%
+ asm |
+ auto |
+ break |
+ ... etc ...
+ volatile |
+ while /* it's a keyword */
+
+
.|\n /* it's not a keyword */
+
+
+To eliminate the back-tracking, introduce a catch-all rule:
+
+
+
%%
+ asm |
+ auto |
+ break |
+ ... etc ...
+ volatile |
+ while /* it's a keyword */
+
+
[a-z]+ |
+ .|\n /* it's not a keyword */
+
+
+Now, if it's guaranteed that there's exactly one word per line,
+then we can reduce the total number of matches by a half by
+merging in the recognition of newlines with that of the other
+tokens:
+
+One has to be careful here, as we have now reintroduced backing up
+into the scanner. In particular, while
+we
+know that there will never be any characters in the input stream
+other than letters or newlines,
+flex
+can't figure this out, and it will plan for possibly needing to back up
+when it has scanned a token like "auto" and then the next character
+is something other than a newline or a letter. Previously it would
+then just match the "auto" rule and be done, but now it has no "auto"
+rule, only a "auto\n" rule. To eliminate the possibility of backing up,
+we could either duplicate all rules but without final newlines, or,
+since we never expect to encounter such an input and therefore don't
+how it's classified, we can introduce one more catch-all rule, this
+one which doesn't include a newline:
+
[a-z]+\n |
+ [a-z]+ |
+ .|\n /* it's not a keyword */
+
+
+Compiled with
+-Cf,
+this is about as fast as one can get a
+flex
+scanner to go for this particular problem.
+
+
+A final note:
+flex
+is slow when matching NUL's, particularly when a token contains
+multiple NUL's.
+It's best to write rules which match
+short
+amounts of text if it's anticipated that the text will often include NUL's.
+
+
+Another final note regarding performance: as mentioned above in the section
+How the Input is Matched, dynamically resizing
+yytext
+to accommodate huge tokens is a slow process because it presently requires that
+the (huge) token be rescanned from the beginning. Thus if performance is
+vital, you should attempt to match "large" quantities of text but not
+"huge" quantities, where the cutoff between the two is at about 8K
+characters/token.
+
+
GENERATING C++ SCANNERS
+
+flex
+provides two different ways to generate scanners for use with C++. The
+first way is to simply compile a scanner generated by
+flex
+using a C++ compiler instead of a C compiler. You should not encounter
+any compilations errors (please report any you find to the email address
+given in the Author section below). You can then use C++ code in your
+rule actions instead of C code. Note that the default input source for
+your scanner remains
+yyin,
+and default echoing is still done to
+yyout.
+Both of these remain
+FILE*
+variables and not C++
+streams.
+
+
+You can also use
+flex
+to generate a C++ scanner class, using the
+-+
+option (or, equivalently,
+%optionc++),
+which is automatically specified if the name of the flex
+executable ends in a '+', such as
+flex++.
+When using this option, flex defaults to generating the scanner to the file
+lex.yy.cc
+instead of
+lex.yy.c.
+The generated scanner includes the header file
+FlexLexer.h,
+which defines the interface to two C++ classes.
+
+
+The first class,
+FlexLexer,
+provides an abstract base class defining the general scanner class
+interface. It provides the following member functions:
+
constchar*YYText()
+
returns the text of the most recently matched token, the equivalent of
+yytext.
+
intYYLeng()
+
returns the length of the most recently matched token, the equivalent of
+yyleng.
+
intlineno()const
+
returns the current input line number
+(see
+%optionyylineno),
+or
+1
+if
+%optionyylineno
+was not used.
+
voidset_debug(intflag)
+
sets the debugging flag for the scanner, equivalent to assigning to
+yy_flex_debug
+(see the Options section above). Note that you must build the scanner
+using
+%optiondebug
+to include debugging information in it.
+
intdebug()const
+
returns the current setting of the debugging flag.
+
+
+Also provided are member functions equivalent to
+yy_switch_to_buffer(),
+yy_create_buffer()
+(though the first argument is an
+istream*
+object pointer and not a
+FILE*),
+yy_flush_buffer(),
+yy_delete_buffer(),
+and
+yyrestart()
+(again, the first argument is a
+istream*
+object pointer).
+
+
+The second class defined in
+FlexLexer.h
+is
+yyFlexLexer,
+which is derived from
+FlexLexer.
+It defines the following additional member functions:
+
constructs a
+yyFlexLexer
+object using the given streams for input and output. If not specified,
+the streams default to
+cin
+and
+cout,
+respectively.
+
virtualintyylex()
+
performs the same role is
+yylex()
+does for ordinary flex scanners: it scans the input stream, consuming
+tokens, until a rule's action returns a value. If you derive a subclass
+S
+from
+yyFlexLexer
+and want to access the member functions and variables of
+S
+inside
+yylex(),
+then you need to use
+%optionyyclass=S
+to inform
+flex
+that you will be using that subclass instead of
+yyFlexLexer.
+In this case, rather than generating
+yyFlexLexer::yylex(),
+flex
+generates
+S::yylex()
+(and also generates a dummy
+yyFlexLexer::yylex()
+that calls
+yyFlexLexer::LexerError()
+if called).
+
first switches the input streams via
+switch_streams(new_in,new_out)
+and then returns the value of
+yylex().
+
+
+In addition,
+yyFlexLexer
+defines the following protected virtual functions which you can redefine
+in derived classes to tailor the scanner:
+
virtual int LexerInput( char* buf, int max_size )
+
reads up to
+max_size
+characters into
+buf
+and returns the number of characters read. To indicate end-of-input,
+return 0 characters. Note that "interactive" scanners (see the
+-B
+and
+-I
+flags) define the macro
+YY_INTERACTIVE.
+If you redefine
+LexerInput()
+and need to take different actions depending on whether or not
+the scanner might be scanning an interactive input source, you can
+test for the presence of this name via
+#ifdef.
+
virtual void LexerOutput( const char* buf, int size )
+
writes out
+size
+characters from the buffer
+buf,
+which, while NUL-terminated, may also contain "internal" NUL's if
+the scanner's rules can match text with NUL's in them.
+
virtual void LexerError( const char* msg )
+
reports a fatal error message. The default version of this function
+writes the message to the stream
+cerr
+and exits.
+
+
+Note that a
+yyFlexLexer
+object contains its
+entire
+scanning state. Thus you can use such objects to create reentrant
+scanners. You can instantiate multiple instances of the same
+yyFlexLexer
+class, and you can also combine multiple C++ scanner classes together
+in the same program using the
+-P
+option discussed above.
+
+
+Finally, note that the
+%array
+feature is not available to C++ scanner classes; you must use
+%pointer
+(the default).
+
+
+Here is an example of a simple C++ scanner:
+
+
+
// An example of using the flex C++ scanner class.
+
+
%{
+ int mylineno = 0;
+ %}
+
+
string \"[^\n"]+\"
+
+
ws [ \t]+
+
+
alpha [A-Za-z]
+ dig [0-9]
+ name ({alpha}|{dig}|\$)({alpha}|{dig}|[_.\-/$])*
+ num1 [-+]?{dig}+\.?([eE][-+]?{dig}+)?
+ num2 [-+]?{dig}*\.{dig}+([eE][-+]?{dig}+)?
+ number {num1}|{num2}
+
+
+If you want to create multiple (different) lexer classes, you use the
+-P
+flag (or the
+prefix=
+option) to rename each
+yyFlexLexer
+to some other
+xxFlexLexer.
+You then can include
+<FlexLexer.h>
+in your other sources once per lexer class, first renaming
+yyFlexLexer
+as follows:
+
+if, for example, you used
+%optionprefix=xx
+for one of your scanners and
+%optionprefix=zz
+for the other.
+
+
+IMPORTANT: the present form of the scanning class is
+experimental
+and may change considerably between major releases.
+
+
INCOMPATIBILITIES WITH LEX AND POSIX
+
+flex
+is a rewrite of the AT&T Unix
+lex
+tool (the two implementations do not share any code, though),
+with some extensions and incompatibilities, both of which
+are of concern to those who wish to write scanners acceptable
+to either implementation. Flex is fully compliant with the POSIX
+lex
+specification, except that when using
+%pointer
+(the default), a call to
+unput()
+destroys the contents of
+yytext,
+which is counter to the POSIX specification.
+
+
+In this section we discuss all of the known areas of incompatibility
+between flex, AT&T lex, and the POSIX specification.
+
+
+flex's
+-l
+option turns on maximum compatibility with the original AT&T
+lex
+implementation, at the cost of a major loss in the generated scanner's
+performance. We note below which incompatibilities can be overcome
+using the
+-l
+option.
+
+
+flex
+is fully compatible with
+lex
+with the following exceptions:
+
-
The undocumented
+lex
+scanner internal variable
+yylineno
+is not supported unless
+-l
+or
+%optionyylineno
+is used.
+
yylineno
+should be maintained on a per-buffer basis, rather than a per-scanner
+(single global variable) basis.
+
yylineno
+is not part of the POSIX specification.
+
-
The
+input()
+routine is not redefinable, though it may be called to read characters
+following whatever has been matched by a rule. If
+input()
+encounters an end-of-file the normal
+yywrap()
+processing is done. A ``real'' end-of-file is returned by
+input()
+as
+EOF.
+
Input is instead controlled by defining the
+YY_INPUT
+macro.
+
The
+flex
+restriction that
+input()
+cannot be redefined is in accordance with the POSIX specification,
+which simply does not specify any way of controlling the
+scanner's input other than by making an initial assignment to
+yyin.
+
-
The
+unput()
+routine is not redefinable. This restriction is in accordance with POSIX.
+
-
flex
+scanners are not as reentrant as
+lex
+scanners. In particular, if you have an interactive scanner and
+an interrupt handler which long-jumps out of the scanner, and
+the scanner is subsequently called again, you may get the following
+message:
+
+
+
fatal flex scanner internal error--end of buffer missed
+
+
+To reenter the scanner, first use
+
+
+
yyrestart( yyin );
+
+
+Note that this call will throw away any buffered input; usually this
+isn't a problem with an interactive scanner.
+
Also note that flex C++ scanner classes
+are
+reentrant, so if using C++ is an option for you, you should use
+them instead. See "Generating C++ Scanners" above for details.
+
-
output()
+is not supported.
+Output from the
+ECHO
+macro is done to the file-pointer
+yyout
+(default
+stdout).
+
output()
+is not part of the POSIX specification.
+
-
lex
+does not support exclusive start conditions (%x), though they
+are in the POSIX specification.
+
-
When definitions are expanded,
+flex
+encloses them in parentheses.
+With lex, the following:
+
+will not match the string "foo" because when the macro
+is expanded the rule is equivalent to "foo[A-Z][A-Z0-9]*?"
+and the precedence is such that the '?' is associated with
+"[A-Z0-9]*". With
+flex,
+the rule will be expanded to
+"foo([A-Z][A-Z0-9]*)?" and so the string "foo" will match.
+
Note that if the definition begins with
+^
+or ends with
+$
+then it is
+not
+expanded with parentheses, to allow these operators to appear in
+definitions without losing their special meanings. But the
+<s>,/,
+and
+<<EOF>>
+operators cannot be used in a
+flex
+definition.
+
Using
+-l
+results in the
+lex
+behavior of no parentheses around the definition.
+
The POSIX specification is that the definition be enclosed in parentheses.
+
-
Some implementations of
+lex
+allow a rule's action to begin on a separate line, if the rule's pattern
+has trailing whitespace:
+
The
+lex
+%r
+(generate a Ratfor scanner) option is not supported. It is not part
+of the POSIX specification.
+
-
After a call to
+unput(),
+yytext
+is undefined until the next token is matched, unless the scanner
+was built using
+%array.
+This is not the case with
+lex
+or the POSIX specification. The
+-l
+option does away with this incompatibility.
+
-
The precedence of the
+{}
+(numeric range) operator is different.
+lex
+interprets "abc{1,3}" as "match one, two, or
+three occurrences of 'abc'", whereas
+flex
+interprets it as "match 'ab'
+followed by one, two, or three occurrences of 'c'". The latter is
+in agreement with the POSIX specification.
+
-
The precedence of the
+^
+operator is different.
+lex
+interprets "^foo|bar" as "match either 'foo' at the beginning of a line,
+or 'bar' anywhere", whereas
+flex
+interprets it as "match either 'foo' or 'bar' if they come at the beginning
+of a line". The latter is in agreement with the POSIX specification.
+
-
The special table-size declarations such as
+%a
+supported by
+lex
+are not required by
+flex
+scanners;
+flex
+ignores them.
+
-
The name
+FLEX_SCANNER
+is #define'd so scanners may be written for use with either
+flex
+or
+lex.
+Scanners also include
+YY_FLEX_MAJOR_VERSION
+and
+YY_FLEX_MINOR_VERSION
+indicating which version of
+flex
+generated the scanner
+(for example, for the 2.5 release, these defines would be 2 and 5
+respectively).
+
+
+The following
+flex
+features are not included in
+lex
+or the POSIX specification:
+
+
+
C++ scanners
+ %option
+ start condition scopes
+ start condition stacks
+ interactive/non-interactive scanners
+ yy_scan_string() and friends
+ yyterminate()
+ yy_set_interactive()
+ yy_set_bol()
+ YY_AT_BOL()
+ <<EOF>>
+ <*>
+ YY_DECL
+ YY_START
+ YY_USER_ACTION
+ YY_USER_INIT
+ #line directives
+ %{}'s around actions
+ multiple actions on a line
+
+
+plus almost all of the flex flags.
+The last feature in the list refers to the fact that with
+flex
+you can put multiple actions on the same line, separated with
+semi-colons, while with
+lex,
+the following
+
+
+
foo handle_foo(); ++num_foos_seen;
+
+
+is (rather surprisingly) truncated to
+
+
+
foo handle_foo();
+
+
+flex
+does not truncate the action. Actions that are not enclosed in
+braces are simply terminated at the end of the line.
+
+
DIAGNOSTICS
+
+
+
+warning,rulecannotbematched
+indicates that the given rule
+cannot be matched because it follows other rules that will
+always match the same text as it. For
+example, in the following "foo" cannot be matched because it comes after
+an identifier "catch-all" rule:
+
+
+
[a-z]+ got_identifier();
+ foo got_foo();
+
+
+Using
+REJECT
+in a scanner suppresses this warning.
+
+
+warning,
+-s
+option given but default rule can be matched
+means that it is possible (perhaps only in a particular start condition)
+that the default rule (match any single character) is the only one
+that will match a particular input. Since
+-s
+was given, presumably this is not intended.
+
+
+reject_used_but_not_detectedundefined
+or
+yymore_used_but_not_detectedundefined-
+These errors can occur at compile time. They indicate that the
+scanner uses
+REJECT
+or
+yymore()
+but that
+flex
+failed to notice the fact, meaning that
+flex
+scanned the first two sections looking for occurrences of these actions
+and failed to find any, but somehow you snuck some in (via a #include
+file, for example). Use
+%optionreject
+or
+%optionyymore
+to indicate to flex that you really do use these features.
+
+
+flexscannerjammed-
+a scanner compiled with
+-s
+has encountered an input string which wasn't matched by
+any of its rules. This error can also occur due to internal problems.
+
+
+tokentoolarge,exceedsYYLMAX-
+your scanner uses
+%array
+and one of its rules matched a string longer than the
+YYLMAX
+constant (8K bytes by default). You can increase the value by
+#define'ing
+YYLMAX
+in the definitions section of your
+flex
+input.
+
+
+scannerrequires-8flagto
+usethecharacter'x'-
+Your scanner specification includes recognizing the 8-bit character
+'x'
+and you did not specify the -8 flag, and your scanner defaulted to 7-bit
+because you used the
+-Cf
+or
+-CF
+table compression options. See the discussion of the
+-7
+flag for details.
+
+
+flexscannerpush-backoverflow-
+you used
+unput()
+to push back so much text that the scanner's buffer could not hold
+both the pushed-back text and the current token in
+yytext.
+Ideally the scanner should dynamically resize the buffer in this case, but at
+present it does not.
+
+
+input buffer overflow, can't enlarge buffer because scanner uses REJECT -
+the scanner was working on matching an extremely large token and needed
+to expand the input buffer. This doesn't work with scanners that use
+REJECT.
+
+
+fatal flex scanner internal error--end of buffer missed -
+This can occur in an scanner which is reentered after a long-jump
+has jumped out (or over) the scanner's activation frame. Before
+reentering the scanner, use:
+
+
+
yyrestart( yyin );
+
+
+or, as noted above, switch to using the C++ scanner class.
+
+
+toomanystartconditionsin<>construct!-
+you listed more start conditions in a <> construct than exist (so
+you must have listed at least one of them twice).
+
+
FILES
+
+
-lfl
+
library with which scanners must be linked.
+
lex.yy.c
+
generated scanner (called
+lexyy.c
+on some systems).
+
lex.yy.cc
+
generated C++ scanner class, when using
+-+.
+
<FlexLexer.h>
+
header file defining the C++ scanner base class,
+FlexLexer,
+and its derived class,
+yyFlexLexer.
+
flex.skl
+
skeleton scanner. This file is only used when building flex, not when
+flex executes.
+
lex.backup
+
backing-up information for
+-b
+flag (called
+lex.bck
+on some systems).
+
+
DEFICIENCIES / BUGS
+
+
+
+Some trailing context
+patterns cannot be properly matched and generate
+warning messages ("dangerous trailing context"). These are
+patterns where the ending of the
+first part of the rule matches the beginning of the second
+part, such as "zx*/xy*", where the 'x*' matches the 'x' at
+the beginning of the trailing context. (Note that the POSIX draft
+states that the text matched by such patterns is undefined.)
+
+
+For some trailing context rules, parts which are actually fixed-length are
+not recognized as such, leading to the abovementioned performance loss.
+In particular, parts using '|' or {n} (such as "foo{3}") are always
+considered variable-length.
+
+
+Combining trailing context with the special '|' action can result in
+fixed
+trailing context being turned into the more expensive
+variable
+trailing context. For example, in the following:
+
+
+
%%
+ abc |
+ xyz/def
+
+
+
+
+Use of
+unput()
+invalidates yytext and yyleng, unless the
+%array
+directive
+or the
+-l
+option has been used.
+
+
+Pattern-matching of NUL's is substantially slower than matching other
+characters.
+
+
+Dynamic resizing of the input buffer is slow, as it entails rescanning
+all the text matched so far by the current (generally huge) token.
+
+
+Due to both buffering of input and read-ahead, you cannot intermix
+calls to <stdio.h> routines, such as, for example,
+getchar(),
+with
+flex
+rules and expect it to work. Call
+input()
+instead.
+
+
+The total table entries listed by the
+-v
+flag excludes the number of table entries needed to determine
+what rule has been matched. The number of entries is equal
+to the number of DFA states if the scanner does not use
+REJECT,
+and somewhat greater than the number of states if it does.
+
+
+REJECT
+cannot be used with the
+-f
+or
+-F
+options.
+
+
+The
+flex
+internal algorithms need documentation.
+
+
SEE ALSO
+
+
+
+lex(1), yacc(1), sed(1), awk(1).
+
+
+John Levine, Tony Mason, and Doug Brown,
+Lex&Yacc,
+O'Reilly and Associates. Be sure to get the 2nd edition.
+
+
+M. E. Lesk and E. Schmidt,
+LEX-LexicalAnalyzerGenerator
+
+
+Alfred Aho, Ravi Sethi and Jeffrey Ullman,
+Compilers:Principles,TechniquesandTools,
+Addison-Wesley (1986). Describes the pattern-matching techniques used by
+flex
+(deterministic finite automata).
+
+
AUTHOR
+
+Vern Paxson, with the help of many ideas and much inspiration from
+Van Jacobson. Original version by Jef Poskanzer. The fast table
+representation is a partial implementation of a design done by Van
+Jacobson. The implementation was done by Kevin Gong and Vern Paxson.
+
+
+Thanks to the many
+flex
+beta-testers, feedbackers, and contributors, especially Francois Pinard,
+Casey Leedom,
+Stan Adermann, Terry Allen, David Barker-Plummer, John Basrai,
+Nelson H.F. Beebe, benson@odi.com,
+Karl Berry, Peter A. Bigot, Simon Blanchard,
+Keith Bostic, Frederic Brehm, Ian Brockbank, Kin Cho, Nick Christopher,
+Brian Clapper, J.T. Conklin,
+Jason Coughlin, Bill Cox, Nick Cropper, Dave Curtis, Scott David
+Daniels, Chris G. Demetriou, Theo Deraadt,
+Mike Donahue, Chuck Doucette, Tom Epperly, Leo Eskin,
+Chris Faylor, Chris Flatters, Jon Forrest, Joe Gayda, Kaveh R. Ghazi,
+Eric Goldman, Christopher M. Gould, Ulrich Grepel, Peer Griebel,
+Jan Hajic, Charles Hemphill, NORO Hideo,
+Jarkko Hietaniemi, Scott Hofmann,
+Jeff Honig, Dana Hudes, Eric Hughes, John Interrante,
+Ceriel Jacobs, Michal Jaegermann, Sakari Jalovaara, Jeffrey R. Jones,
+Henry Juengst, Klaus Kaempf, Jonathan I. Kamens, Terrence O Kane,
+Amir Katz, ken@ken.hilco.com, Kevin B. Kenny,
+Steve Kirsch, Winfried Koenig, Marq Kole, Ronald Lamprecht,
+Greg Lee, Rohan Lenard, Craig Leres, John Levine, Steve Liddle, Mike Long,
+Mohamed el Lozy, Brian Madsen, Malte, Joe Marshall,
+Bengt Martensson, Chris Metcalf,
+Luke Mewburn, Jim Meyering, R. Alexander Milowski, Erik Naggum,
+G.T. Nicol, Landon Noll, James Nordby, Marc Nozell,
+Richard Ohnemus, Karsten Pahnke,
+Sven Panne, Roland Pesch, Walter Pelissero, Gaumond
+Pierre, Esmond Pitt, Jef Poskanzer, Joe Rahmeh, Jarmo Raiha,
+Frederic Raimbault, Pat Rankin, Rick Richardson,
+Kevin Rodgers, Kai Uwe Rommel, Jim Roskind, Alberto Santini,
+Andreas Scherer, Darrell Schiebel, Raf Schietekat,
+Doug Schmidt, Philippe Schnoebelen, Andreas Schwab,
+Alex Siegel, Eckehard Stolz, Jan-Erik Strvmquist,
+Mike Stump, Paul Stuart, Dave Tallman, Ian Lance Taylor,
+Chris Thewalt, Richard M. Timoney, Jodi Tsai,
+Paul Tuinenga, Gary Weik, Frank Whaley, Gerhard Wilhelms, Kent Williams, Ken
+Yap, Ron Zellar, Nathan Zelle, David Zuhn,
+and those whose names have slipped my marginal
+mail-archiving skills but whose contributions are appreciated all the
+same.
+
+
+Thanks to Keith Bostic, Jon Forrest, Noah Friedman,
+John Gilmore, Craig Leres, John Levine, Bob Mulcahy, G.T.
+Nicol, Francois Pinard, Rich Salz, and Richard Stallman for help with various
+distribution headaches.
+
+
+Thanks to Esmond Pitt and Earle Horton for 8-bit character support; to
+Benson Margulies and Fred Burke for C++ support; to Kent Williams and Tom
+Epperly for C++ class support; to Ove Ewerlid for support of NUL's; and to
+Eric Hughes for support of multiple buffers.
+
+
+This work was primarily done when I was with the Real Time Systems Group
+at the Lawrence Berkeley Laboratory in Berkeley, CA. Many thanks to all there
+for the support I received.
+
+
+Send comments to vern@ee.lbl.gov.
+
\ No newline at end of file
diff --git a/bin/nasm/Licence b/bin/nasm/Licence
new file mode 100755
index 0000000..84dfc22
--- /dev/null
+++ b/bin/nasm/Licence
@@ -0,0 +1,357 @@
+
+
+
+Terms and Conditions for the use of the Netwide Assembler
+
+
+=========================================================
+
+
+
+
+
+Can I have the gist without reading the legalese?
+
+
+-------------------------------------------------
+
+
+
+
+
+Basically, NASM is free. You can't charge for it. You can copy it as
+
+
+much as you like. You can incorporate it, or bits of it, into other
+
+
+free programs if you want. (But we want to know about it if you do,
+
+
+and we want to be mentioned in the credits.) We may well allow you
+
+
+to incorporate it into commercial software too, but we'll probably
+
+
+demand some money for it, and we'll certainly demand to be given
+
+
+credit. And in extreme cases (although I can't immediately think of
+
+
+a reason we might actually want to do this) we may refuse to let you
+
+
+do it at all.
+
+
+
+
+
+NASM LICENCE AGREEMENT
+
+
+======================
+
+
+
+
+
+By "the Software" this licence refers to the complete contents of
+
+
+the NASM archive, excluding this licence document itself, and
+
+
+excluding the contents of the `test' directory. The Netwide
+
+
+Disassembler, NDISASM, is specifically included under this licence.
+
+
+
+
+
+I. The Software is freely redistributable; anyone may copy the
+
+
+Software, or parts of the Software, and give away as many copies as
+
+
+they like to anyone, as long as this licence document is kept with
+
+
+the Software. Charging a fee for the Software is prohibited,
+
+
+although a fee may be charged for the act of transferring a copy,
+
+
+and you can offer warranty protection and charge a fee for that.
+
+
+
+
+
+II. The Software, or parts thereof, may be incorporated into other
+
+
+freely redistributable software (by which we mean software that may
+
+
+be obtained free of charge) without requiring permission from the
+
+
+authors, as long as due credit is given to the authors of the
+
+
+Software in the resulting work, as long as the authors are informed
+
+
+of this action if possible, and as long as those parts of the
+
+
+Software that are used remain under this licence.
+
+
+
+
+
+III. Modified forms of the Software may be created and distributed
+
+
+as long as the authors are informed of this action if possible, as
+
+
+long as the resulting work remains under this licence, as long as
+
+
+the modified form of the Software is distributed with documentation
+
+
+which still gives credit to the original authors of the Software,
+
+
+and as long as the modified form of the Software is distributed with
+
+
+a clear statement that it is not the original form of the Software
+
+
+in the form that it was distributed by the authors.
+
+
+
+
+
+IV. The Software, or parts thereof, may be incorporated into other
+
+
+software which is not freely redistributable (i.e. software for
+
+
+which a fee is charged), as long as permission is granted from the
+
+
+authors of the Software. The authors reserve the right to grant this
+
+
+permission only for a fee, which may at our option take the form of
+
+
+royalty payments. The authors also reserve the right to refuse to
+
+
+grant permission if they deem it necessary. For further information
+
+
+about who exactly the authors are, see clause XI below.
+
+
+
+
+
+V. The Software may be incorporated, in its original archive form,
+
+
+into software collections or archives which are not freely
+
+
+redistributable, as long as it is clearly stated that the Software
+
+
+itself remains freely redistributable and remains under this licence
+
+
+and no other. Such collections are deemed not to fall under article
+
+
+IV of this licence.
+
+
+
+
+
+VI. Object files or programs generated by the Software as output do
+
+
+not fall under this licence at all, and may be placed under any
+
+
+licence the author wishes. The authors explicitly lay no claim to,
+
+
+and assert no rights over, any programs written by other people and
+
+
+assembled into object form by the Software.
+
+
+
+
+
+VII. You may not copy, modify or distribute the Software except
+
+
+under the terms given in this licence document. You may not
+
+
+sublicense the Software or in any way place it under any other
+
+
+licence than this one. Since you have not signed this licence, you
+
+
+are not of course required to accept it; however, no other licence
+
+
+applies to the Software, and nothing else grants you any permission
+
+
+to copy, modify, sublicense or distribute the Software in any way.
+
+
+These actions are therefore prohibited if you do not accept this
+
+
+licence.
+
+
+
+
+
+VIII. There is no warranty for the Software, to the extent permitted
+
+
+by applicable law. The authors provide the Software "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 Software is with you. Should the Software prove
+
+
+defective, you assume the cost of all necessary servicing, repair or
+
+
+correction.
+
+
+
+
+
+IX. In no event, unless required by applicable law or agreed to in
+
+
+writing, will any of the authors be liable to you for damages,
+
+
+including any general, special, incidental or consequential damages,
+
+
+arising out of the use or the inability to use the Software,
+
+
+including but not limited to loss of data or data being rendered
+
+
+inaccurate or a failure of the Software to operate with any other
+
+
+programs, even if you have been advised of the possibility of such
+
+
+damages.
+
+
+
+
+
+X. In addition to what this Licence otherwise provides, the Software
+
+
+may be distributed in such a way as to be compliant with the GNU
+
+
+General Public Licence, as published by the Free Software Foundation,
+
+
+Cambridge, MA, USA; version 2, or, at your option, any later version;
+
+
+incorporated herein by reference. You must include a copy of this
+
+
+Licence with such distribution. Furthermore, patches sent to the
+
+
+authors for the purpose of inclusion in the official release version
+
+
+are considered cleared for release under the full terms of this
+
+
+Licence.
+
+
+
+
+
+XI. The authors of NASM are the original authors (Simon Tatham and
+
+
+Julian Hall) and all those who the original authors feel have
+
+
+contributed significantly to the overall project. If you wish to
+
+
+contact the authors, Julian Hall (jules@earthcorp.com) should be your
+
+
+first port of call.
+
+
+
+
+
+XII. Should any part of this agreement be deemed unenforcable, it is
+
+
+intended that the remainder of the agreement be held in force.
+
+
+
+
+
+END OF LICENCE AGREEMENT
+
+
diff --git a/bin/nasm/Readme b/bin/nasm/Readme
new file mode 100755
index 0000000..d5ebe7f
--- /dev/null
+++ b/bin/nasm/Readme
@@ -0,0 +1,179 @@
+This is a distribution of NASM, the Netwide Assembler. NASM is a
+prototype general-purpose x86 assembler. It will currently output
+flat-form binary files, a.out, COFF and ELF Unix object files,
+Microsoft Win32 and 16-bit DOS object files, OS/2 object files, the
+as86 object format, and a home-grown format called RDOFF.
+
+Also included is NDISASM, a prototype x86 binary-file disassembler
+which uses the same instruction table as NASM.
+
+To install NASM on UNIX or Linux, type `./configure', then `make', and
+then either `make install', or copy the file `nasm' (and maybe
+`ndisasm') to a directory on your search path (maybe /usr/local/bin,
+or ~/bin if you don't have root access). You may also want to copy the
+man page `nasm.1' (and maybe `ndisasm.1') to somewhere sensible. Note
+that source and binaries are also available in RPM format; to install
+an RPM on a system which uses this packaging format (mostly Linux
+distributions) simply do "rpm -Uivh filename.rpm".
+
+To install under DOS, if you don't need to rebuild from the sources,
+you can just copy either nasm.exe and ndisasm.exe (32-bit DOS-extended
+versions), nasmr.exe and ndisasmr.exe (16-bit classical DOS
+executables), or nasmw.exe and ndisasmw.exe (Win32 console
+applications - less likely to run out of memory), to somewhere on your
+PATH.
+
+The 32-bit applications require a DPMI server. If you're running
+under plain DOS and don't have a DPMI server already, you can get
+CSDPMI from ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2misc/.
+
+To rebuild the DOS sources, various makefiles are provided:
+
+- Makefile.dos, the one I build the standard 16-bit releases from,
+ designed for a hybrid system using Microsoft C and Borland Make
+ (don't ask why :-)
+- Makefile.vc, for Microsoft Visual C++ compiling to a Win32
+ command-line application. This is the one I build the standard
+ Win32 release binaries from.
+
+- Makefile.bor, for Borland C.
+- Makefile.bc2, also for Borland C, contributed by Fox Cutter.
+ Reported to work better than Makefile.bor on some systems.
+
+- Makefile.sc, for Symantec C++, compiling to a 32-bit extended DOS
+ executable.. Contributed by Mark Junker.
+- Makefile.scw, also for Symantec C++, compiling to a Win32 command-
+ line application. Also contributed by Mark Junker.
+
+- Makefile.wc, for Watcom C, compiling to a 32-bit extended DOS
+ executable. Contributed by Dominik Behr.
+- Makefile.wcw, also for Watcom C, compiling to a Win32 command-
+ line application. Also contributed by Dominik Behr.
+
+- Makefile.dj, for DJGPP, compiling to a 32-bit extended DOS
+ executable. Contributed by Dominik Behr.
+
+- Makefile.lcc, for lcc-win32, compiling to a Win32 command line
+ application. (The lcc-win32 compiler and tools are available from
+ http://www.remcomp.com/lcc-win32/)
+
+I can't guarantee that all of those makefiles work, because I don't
+have all of those compilers. However, Makefile.dos and Makefile.vc
+work on my system, and so do Makefile.bor and Makefile.bc2.
+
+Be careful with Borland C: there have been various conflicting
+reports about how reliable the Huge memory model is. If you try to
+compile NASM in Large model, you may get DGROUP overflows due to the
+vast quantity of data in the instruction tables. I've had reports
+from some people that Huge model doesn't work at all (and also
+reports from others that it works fine), so if you don't want to try
+moving to Huge, you could try adding the option `-dc' to the
+compiler command line instead, which causes string literals to be
+moved from DGROUP to the code segments and might make Large model
+start working. (Either solution works for me.)
+
+To rebuild truly from scratch, or to make any changes to insns.dat or
+standard.mac, you need a Perl interpreter installed. Perl
+interpreters are available for a number of platforms, from:
+
+ http://www.cpan.org/ports/
+
+For DOS you can also get one from:
+
+ ftp://ftp.simtel.net/pub/simtelnet/gnu/djgpp/v2gnu/
+
+Dominik Behr has also contributed the file misc/pmw.bat, which is a
+batch file to turn the output from Makefile.wc (NASM.EXE and
+NDISASM.EXE) into standalone executables incorporating Tran's
+PMODE/W DOS extender, rather than depending on an external extender
+program.
+
+Some of the Windows makefiles produce executables called nasmw.exe
+and ndisasmw.exe, and some don't. Be prepared for either...
+
+If you want to build a restricted version of NASM containing only
+some of the object file formats, you can achieve this by adding
+#defines to `outform.h' (see the file itself for documentation), or
+equivalently by adding compiler command line options in the
+Makefile.
+
+There is a machine description file for the `LCC' retargetable C
+compiler (version 4.0), in the directory `lcc', along with
+instructions for its use. This means that NASM can now be used as
+the code-generator back end for a useful C compiler.
+
+Michael `Wuschel' Tippach has ported his DOS extender `WDOSX' to
+enable it to work with the 32-bit binary files NASM can output: the
+original extender and his port `WDOSX/N' are available from his web
+page, http://www.geocities.com/SiliconValley/Park/4493.
+
+Matt Mastracci has written a document explaining how to write
+assembly language modules in DJGPP programs using NASM: it's on his
+web site at http://www.ucalgary.ca/~mmastrac/djgppasm.doc.
+
+The `misc' directory contains `nasm.sl', a NASM editing mode for the
+JED programmers' editor (see http://space.mit.edu/~davis/jed.html
+for details about JED). The comment at the start of the file gives
+instructions on how to install the mode. This directory also
+contains a file (`magic') containing lines to add to /etc/magic on
+Unix systems to allow the `file' command to recognise RDF files, and
+a zip file (`exasm.zip') containing the necessary files for syntax
+highlighting in the Aurora DOS editor. (The Aurora files were
+contributed by ; I haven't tested them as I
+don't have Aurora.)
+
+The `rdoff' directory contains sources for a linker and loader for
+the RDF object file format, to run under Linux, and also
+documentation on the internal structure of RDF files.
+
+For information about how you can distribute and use NASM, see the
+file Licence. We were tempted to put NASM under the GPL, but decided
+that in many ways it was too restrictive for developers.
+
+For release 0.98 and later, the NASM source distribution contains the
+following components:
+
+ Unix Windows, OS/2 DOS
+
+ nasm-X.XX.tar.gz nasm-X.XX.zip nasmXXXs.zip
+
+ Main archive: Contain all sources you need to build NASM,
+ plus the documentation source code. If you have a Perl
+ interpreter and (possibly) Winhelp compiler installed, you
+ don't need any additional files.
+
+ nasm-X.XX-xdoc.tar.gz nasm-X.XX-xdoc.zip nasmXXXd.zip
+
+ Documentation in HTML, INFO, text, and PostScript format.
+ You can compile these files yourself from the main archive
+ if you have a Perl interpreter installed.
+
+ N/A nasm-X.XX-whlp.zip N/A
+
+ Documentation in Windows help format. You can compile this
+ file yourself from the main archive if you have a Perl
+ interpreter and a Winhelp compiler installed.
+
+The Unix, Windows-OS/2, and DOS versions differ in the following ways:
+
+The Unix version uses the Unix line ending convention (LF), and long file
+names with upper and lower case.
+
+The Windows-OS/2 version uses the Microsoft line ending convention
+(CR+LF), and long file names with upper and lower case.
+
+The DOS version uses the Microsoft line ending convention (CR+LF),
+with all filenames in 8.3 monocase.
+
+For information about how to use NASM, see the various forms of
+documentation in the `doc' directory: documentation is provided in
+HTML, PostScript, plain text, Texinfo, and Windows Help formats. For
+information about how to use NDISASM, see `ndisasm.doc'. For
+information about the internal structure of NASM, see
+`internal.doc'. (In particular, _please_ read `internal.doc' before
+writing any code for us...)
+
+The NASM web page is at http://www.cryogen.com/Nasm/
+
+Bug reports (and patches if you can) should be sent to
+.
diff --git a/bin/nasm/nasmw.exe b/bin/nasm/nasmw.exe
new file mode 100755
index 0000000..dc6b0cc
Binary files /dev/null and b/bin/nasm/nasmw.exe differ
diff --git a/bin/nasm/ndisasmw.exe b/bin/nasm/ndisasmw.exe
new file mode 100755
index 0000000..b38bdf4
Binary files /dev/null and b/bin/nasm/ndisasmw.exe differ
diff --git a/engine/Makefile b/engine/Makefile
new file mode 100755
index 0000000..0d1599a
--- /dev/null
+++ b/engine/Makefile
@@ -0,0 +1,33 @@
+
+#--------------------------------------
+# include and verify the users mk/conf.mk
+
+-include ../mk/conf.mk
+
+ifndef CONFIG_STATUS
+doConfigure:
+ $(error Configuration file not defined. Please run $(MAKE) -f mk/configure.mk)
+ #@$(MAKE) --no-print-directory -f ../mk/configure.mk
+else
+ifeq ($(CONFIG_STATUS),INVALID)
+doConfigure:
+ $(error Invalid Configuration file. Please run $(MAKE) -f mk/configure.mk)
+ #@$(MAKE) --no-print-directory -f mk/configure.mk
+else
+ include ../mk/conf.$(COMPILER).$(OS).mk
+ include ../mk/conf.$(COMPILER).mk
+endif
+endif
+
+include targets.torque.mk
+
+include ../mk/conf.common.mk
+
+
+#default:
+# echo default.
+
+ifneq ($(MAKECMDGOALS),clean)
+-include $(addprefix $(DIR.OBJ)/, $(addsuffix $(EXT.DEP), $(basename $(filter %.cc %.c,$(SOURCE.ALL)))))
+endif
+
diff --git a/engine/audio/audio.cc b/engine/audio/audio.cc
new file mode 100755
index 0000000..d46870a
--- /dev/null
+++ b/engine/audio/audio.cc
@@ -0,0 +1,2487 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "audio/audio.h"
+#include "audio/audioDataBlock.h"
+#include "core/tVector.h"
+#include "console/console.h"
+#include "console/consoleTypes.h"
+#include "game/gameConnection.h"
+#include "core/fileStream.h"
+#include "audio/audioStreamSourceFactory.h"
+
+#ifdef TORQUE_OS_MAC
+//#define REL_WORKAROUND
+#endif
+
+//-------------------------------------------------------------------------
+#define MAX_AUDIOSOURCES 16 // maximum number of concurrent sources
+#define MIN_GAIN 0.05f // anything with lower gain will not be started
+#define MIN_UNCULL_PERIOD 500 // time before buffer is checked to be unculled
+#define MIN_UNCULL_GAIN 0.1f // min gain of source to be unculled
+
+#define ALX_DEF_SAMPLE_RATE 44100 // default values for mixer
+#define ALX_DEF_SAMPLE_BITS 16
+#define ALX_DEF_CHANNELS 2
+
+#define FORCED_OUTER_FALLOFF 10000.f // forced falloff distance
+static bool mDisableOuterFalloffs = false; // forced max falloff?
+static F32 mInnerFalloffScale = 1.f; // amount to scale inner falloffs
+
+static ALCdevice *mDevice = NULL; // active OpenAL device
+static ALCcontext *mContext = NULL; // active OpenAL context
+F32 mAudioTypeVolume[Audio::NumAudioTypes]; // the attenuation for each of the channel types
+
+//-------------------------------------------------------------------------
+struct LoopingImage
+{
+ AUDIOHANDLE mHandle;
+ Resource mBuffer;
+ Audio::Description mDescription;
+ AudioSampleEnvironment *mEnvironment;
+
+ Point3F mPosition;
+ Point3F mDirection;
+ F32 mPitch;
+ F32 mScore;
+ U32 mCullTime;
+
+ LoopingImage() { clear(); }
+
+ void clear()
+ {
+ mHandle = NULL_AUDIOHANDLE;
+ mBuffer = NULL;
+ dMemset(&mDescription, 0, sizeof(Audio::Description));
+ mEnvironment = 0;
+ mPosition.set(0.f,0.f,0.f);
+ mDirection.set(0.f,1.f,0.f);
+ mPitch = 1.f;
+ mScore = 0.f;
+ mCullTime = 0;
+ }
+};
+
+//-------------------------------------------------------------------------
+static F32 mMasterVolume = 1.f; // traped from AL_LISTENER gain (miles has difficulties with 3d sources)
+
+static ALuint mSource[MAX_AUDIOSOURCES]; // ALSources
+static AUDIOHANDLE mHandle[MAX_AUDIOSOURCES]; // unique handles
+static Resource mBuffer[MAX_AUDIOSOURCES]; // each of the playing buffers (needed for AudioThread)
+static F32 mScore[MAX_AUDIOSOURCES]; // for figuring out which sources to cull/uncull
+static F32 mSourceVolume[MAX_AUDIOSOURCES]; // the samples current un-attenuated gain (not scaled by master/channel gains)
+static U32 mType[MAX_AUDIOSOURCES]; // the channel which this source belongs
+
+static AudioSampleEnvironment* mSampleEnvironment[MAX_AUDIOSOURCES]; // currently playing sample environments
+static bool mEnvironmentEnabled = false; // environment enabled?
+static SimObjectPtr mCurrentEnvironment; // the last environment set
+
+static ALuint mEnvironment = 0; // al environment handle
+
+struct LoopingList : VectorPtr
+{
+ LoopingList() : VectorPtr(__FILE__, __LINE__) { }
+
+ LoopingList::iterator findImage(AUDIOHANDLE handle);
+ void sort();
+};
+
+struct StreamingList : VectorPtr
+{
+ StreamingList() : VectorPtr(__FILE__, __LINE__) { }
+
+ StreamingList::iterator findImage(AUDIOHANDLE handle);
+ void sort();
+};
+
+// LoopingList and LoopingFreeList own the images
+static LoopingList mLoopingList; // all the looping sources
+static LoopingList mLoopingFreeList; // free store
+static LoopingList mLoopingInactiveList; // sources which have not been played yet
+static LoopingList mLoopingCulledList; // sources which have been culled (alxPlay called)
+
+// StreamingList and StreamingFreeList own the images
+static StreamingList mStreamingList; // all the streaming sources
+//static StreamingList mStreamingFreeList; // free store
+static StreamingList mStreamingInactiveList; // sources which have not been played yet
+static StreamingList mStreamingCulledList; // sources which have been culled (alxPlay called)
+
+#define AUDIOHANDLE_LOOPING_BIT (0x80000000)
+#define AUDIOHANDLE_STREAMING_BIT (0x40000000)
+#define AUDIOHANDLE_INACTIVE_BIT (0x20000000)
+#define AUDIOHANDLE_LOADING_BIT (0x10000000)
+#define HANDLE_MASK ~(AUDIOHANDLE_LOOPING_BIT | AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT)
+
+// keep the 'AUDIOHANDLE_LOOPING_BIT' on the handle returned to the caller so that
+// the handle can quickly be rejected from looping list queries
+#define RETURN_MASK ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT)
+static AUDIOHANDLE mLastHandle = NULL_AUDIOHANDLE;
+
+static bool mForceMaxDistanceUpdate = false; // force gain setting for 3d distances
+static U32 mNumSources = 0; // total number of sources to work with
+static U32 mRequestSources = MAX_AUDIOSOURCES; // number of sources to request from openAL
+
+#define INVALID_SOURCE 0xffffffff
+
+inline bool areEqualHandles(AUDIOHANDLE a, AUDIOHANDLE b)
+{
+ return((a & HANDLE_MASK) == (b & HANDLE_MASK));
+}
+
+//-------------------------------------------------------------------------
+// Looping image
+//-------------------------------------------------------------------------
+inline LoopingList::iterator LoopingList::findImage(AUDIOHANDLE handle)
+{
+ if(handle & AUDIOHANDLE_LOOPING_BIT)
+ {
+ LoopingList::iterator itr = begin();
+ while(itr != end())
+ {
+ if(areEqualHandles((*itr)->mHandle, handle))
+ return(itr);
+ itr++;
+ }
+ }
+ return(0);
+}
+
+inline int QSORT_CALLBACK loopingImageSort(const void * p1, const void * p2)
+{
+ const LoopingImage * ip1 = *(const LoopingImage**)p1;
+ const LoopingImage * ip2 = *(const LoopingImage**)p2;
+
+ // min->max
+ return ip2->mScore - ip1->mScore;
+}
+
+void LoopingList::sort()
+{
+ dQsort(address(), size(), sizeof(LoopingImage*), loopingImageSort);
+}
+
+//-------------------------------------------------------------------------
+// StreamingList
+//-------------------------------------------------------------------------
+inline StreamingList::iterator StreamingList::findImage(AUDIOHANDLE handle)
+{
+ if(handle & AUDIOHANDLE_STREAMING_BIT)
+ {
+ StreamingList::iterator itr = begin();
+ while(itr != end())
+ {
+ if(areEqualHandles((*itr)->mHandle, handle))
+ return(itr);
+ itr++;
+ }
+ }
+ return(0);
+}
+
+inline int QSORT_CALLBACK streamingSourceSort(const void * p1, const void * p2)
+{
+ const AudioStreamSource * ip1 = *(const AudioStreamSource**)p1;
+ const AudioStreamSource * ip2 = *(const AudioStreamSource**)p2;
+
+ // min->max
+ return ip2->mScore - ip1->mScore;
+}
+
+void StreamingList::sort()
+{
+ dQsort(address(), size(), sizeof(AudioStreamSource*), streamingSourceSort);
+}
+
+//-------------------------------------------------------------------------
+LoopingImage * createLoopingImage()
+{
+ LoopingImage *image;
+ if (mLoopingFreeList.size())
+ {
+ image = mLoopingFreeList.last();
+ mLoopingFreeList.pop_back();
+ }
+ else
+ image = new LoopingImage;
+ return(image);
+}
+
+//-------------------------------------------------------------------------
+AudioStreamSource * createStreamingSource(const char* filename)
+{
+ AudioStreamSource *streamSource = AudioStreamSourceFactory::getNewInstance(filename);
+ return(streamSource);
+}
+
+//-------------------------------------------------------------------------
+static AUDIOHANDLE getNewHandle()
+{
+ mLastHandle++;
+ mLastHandle &= HANDLE_MASK;
+ if (mLastHandle == NULL_AUDIOHANDLE)
+ mLastHandle++;
+ return mLastHandle;
+}
+
+
+//-------------------------------------------------------------------------
+// function declarations
+void alxLoopingUpdate();
+void alxStreamingUpdate();
+void alxUpdateScores(bool);
+
+static bool findFreeSource(U32 *index)
+{
+ for(U32 i = 0; i < mNumSources; i++)
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ {
+ *index = i;
+ return(true);
+ }
+ return(false);
+}
+
+//--------------------------------------------------------------------------
+// - cull out the min source that is below volume
+// - streams/voice/loading streams are all scored > 2
+// - volumes are attenuated by channel only
+static bool cullSource(U32 *index, F32 volume)
+{
+ alGetError();
+
+ F32 minVolume = volume;
+ S32 best = -1;
+ for(S32 i = 0; i < mNumSources; i++)
+ {
+ if(mScore[i] < minVolume)
+ {
+ minVolume = mScore[i];
+ best = i;
+ }
+ }
+
+ if(best == -1)
+ return(false);
+
+ // check if culling a looper
+ LoopingList::iterator itr = mLoopingList.findImage(mHandle[best]);
+ if(itr)
+ {
+ // check if culling an inactive looper
+ if(mHandle[best] & AUDIOHANDLE_INACTIVE_BIT)
+ {
+ AssertFatal(!mLoopingInactiveList.findImage(mHandle[best]), "cullSource: image already in inactive list");
+ AssertFatal(!mLoopingCulledList.findImage(mHandle[best]), "cullSource: image should not be in culled list");
+ mLoopingInactiveList.push_back(*itr);
+ }
+ else
+ {
+ (*itr)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
+ AssertFatal(!mLoopingCulledList.findImage(mHandle[best]), "cullSource: image already in culled list");
+ AssertFatal(!mLoopingInactiveList.findImage(mHandle[best]), "cullSource: image should no be in inactive list");
+ (*itr)->mCullTime = Platform::getRealMilliseconds();
+ mLoopingCulledList.push_back(*itr);
+ }
+ }
+
+ // check if culling a streamer
+ StreamingList::iterator itr2 = mStreamingList.findImage(mHandle[best]);
+ if(itr2)
+ {
+ // check if culling an inactive streamer
+ if(mHandle[best] & AUDIOHANDLE_INACTIVE_BIT)
+ {
+ AssertFatal(!mStreamingInactiveList.findImage(mHandle[best]), "cullSource: image already in inactive list");
+ AssertFatal(!mStreamingCulledList.findImage(mHandle[best]), "cullSource: image should not be in culled list");
+ mStreamingInactiveList.push_back(*itr2);
+ }
+ else
+ {
+ (*itr2)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
+ AssertFatal(!mStreamingCulledList.findImage(mHandle[best]), "cullSource: image already in culled list");
+ AssertFatal(!mStreamingInactiveList.findImage(mHandle[best]), "cullSource: image should no be in inactive list");
+ (*itr2)->freeStream();
+ (*itr2)->mCullTime = Platform::getRealMilliseconds();
+ mStreamingCulledList.push_back(*itr2);
+ }
+ }
+
+ alSourceStop(mSource[best]);
+ mHandle[best] = NULL_AUDIOHANDLE;
+ mBuffer[best] = 0;
+ *index = best;
+
+ return(true);
+}
+
+//--------------------------------------------------------------------------
+/** Compute approximate max volume at a particular distance
+ ignore cone volume influnces
+*/
+static F32 approximate3DVolume(const Audio::Description *desc, const Point3F &position)
+{
+ Point3F p1;
+ alGetListener3f(AL_POSITION, &p1.x, &p1.y, &p1.z);
+
+ p1 -= position;
+ F32 distance = p1.magnitudeSafe();
+
+ if(distance >= desc->mMaxDistance)
+ return(0.f);
+ else if(distance < desc->mReferenceDistance)
+ return 1.0f;
+ else
+ return 1.0 - (distance - desc->mReferenceDistance) / (desc->mMaxDistance - desc->mReferenceDistance);
+}
+
+//--------------------------------------------------------------------------
+inline U32 alxFindIndex(AUDIOHANDLE handle)
+{
+ for (U32 i=0; imDirect);
+ alSourcei(source, AL_ENV_SAMPLE_DIRECT_HF_EXT, env->mDirectHF);
+ alSourcei(source, AL_ENV_SAMPLE_ROOM_EXT, env->mRoom);
+ alSourcei(source, AL_ENV_SAMPLE_ROOM_HF_EXT, env->mRoomHF);
+ alSourcei(source, AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, env->mOutsideVolumeHF);
+ alSourcei(source, AL_ENV_SAMPLE_FLAGS_EXT, env->mFlags);
+
+ alSourcef(source, AL_ENV_SAMPLE_OBSTRUCTION_EXT, env->mObstruction);
+ alSourcef(source, AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, env->mObstructionLFRatio);
+ alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_EXT, env->mOcclusion);
+ alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, env->mOcclusionLFRatio);
+ alSourcef(source, AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, env->mOcclusionRoomRatio);
+ alSourcef(source, AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, env->mRoomRolloff);
+ alSourcef(source, AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, env->mAirAbsorption);
+*/
+}
+
+static void alxSourceEnvironment(ALuint source, LoopingImage * image)
+{
+ AssertFatal(image, "alxSourceEnvironment: invalid looping image");
+ if(image->mDescription.mIs3D)
+ alxSourceEnvironment(source, image->mDescription.mEnvironmentLevel, image->mEnvironment);
+}
+
+static void alxSourceEnvironment(ALuint source, AudioStreamSource * image)
+{
+ AssertFatal(image, "alxSourceEnvironment: invalid looping image");
+ if(image->mDescription.mIs3D)
+ alxSourceEnvironment(source, image->mDescription.mEnvironmentLevel, image->mEnvironment);
+}
+
+//--------------------------------------------------------------------------
+// setup a source to play... loopers have pitch cached
+// - by default, pitch is 1x (settings not defined in description)
+// - all the settings are cached by openAL (miles version), so no worries setting them here
+static void alxSourcePlay(ALuint source, Resource buffer, const Audio::Description *desc, const MatrixF *transform)
+{
+ alSourcei(source, AL_BUFFER, buffer->getALBuffer());
+ alSourcef(source, AL_GAIN, Audio::linearToDB(desc->mVolume * mAudioTypeVolume[desc->mType] * mMasterVolume));
+ alSourcei(source, AL_LOOPING, desc->mIsLooping ? AL_TRUE : AL_FALSE);
+ alSourcef(source, AL_PITCH, 1.f);
+
+ alSourcei(source, AL_CONE_INNER_ANGLE, desc->mConeInsideAngle);
+ alSourcei(source, AL_CONE_OUTER_ANGLE, desc->mConeOutsideAngle);
+ alSourcef(source, AL_CONE_OUTER_GAIN, desc->mConeOutsideVolume);
+
+ if(transform != NULL)
+ {
+#ifdef REL_WORKAROUND
+ alSourcei(source, AL_SOURCE_ABSOLUTE, AL_TRUE);
+#else
+ alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
+#endif
+
+ Point3F p;
+ transform->getColumn(3, &p);
+ alSource3f(source, AL_POSITION, p.x, p.y, p.z);
+
+ //Always use ConeVector (which is tied to transform)
+ alSource3f(source, AL_DIRECTION, desc->mConeVector.x, desc->mConeVector.y, desc->mConeVector.z);
+
+ }
+ else
+ {
+ // 2D sound
+ alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
+ alSource3f(source, AL_POSITION, 0.0f, 0.0f, 1.0f);
+ }
+
+ alSourcef(source, AL_REFERENCE_DISTANCE, desc->mReferenceDistance);
+ alSourcef(source, AL_MAX_DISTANCE, desc->mMaxDistance);
+
+/* todo
+ // environmental audio stuff:
+ alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, desc->mEnvironmentLevel);
+ if(desc->mEnvironmentLevel != 0.f)
+ alSourceResetEnvironment_EXT(source);
+*/
+}
+
+// helper for looping images
+static void alxSourcePlay(ALuint source, LoopingImage * image)
+{
+ AssertFatal(image, "alxSourcePlay: invalid looping image");
+
+ // 3d source? need position/direction
+ if(image->mDescription.mIs3D)
+ {
+ MatrixF transform(true);
+ transform.setColumn(3, image->mPosition);
+ transform.setRow(1, image->mDirection);
+ alxSourcePlay(source, image->mBuffer, &image->mDescription, &transform);
+ }
+ else
+ {
+ // 2d source
+ alxSourcePlay(source, image->mBuffer, &image->mDescription, 0);
+ }
+}
+
+//--------------------------------------------------------------------------
+// setup a streaming source to play
+static void alxSourcePlay(AudioStreamSource *streamSource)
+{
+ ALuint source = streamSource->mSource;
+ Audio::Description *desc = &streamSource->mDescription;
+
+ bool ret = streamSource->initStream();
+
+ alSourcef(source, AL_GAIN, Audio::linearToDB(desc->mVolume * mAudioTypeVolume[desc->mType] * mMasterVolume));
+// alSourcei(source, AL_LOOPING, AL_FALSE);
+ alSourcef(source, AL_PITCH, 1.f);
+
+ alSourcei(source, AL_CONE_INNER_ANGLE, desc->mConeInsideAngle);
+ alSourcei(source, AL_CONE_OUTER_ANGLE, desc->mConeOutsideAngle);
+ alSourcef(source, AL_CONE_OUTER_GAIN, desc->mConeOutsideVolume);
+
+ if(streamSource->mDescription.mIs3D)
+ {
+ MatrixF transform(true);
+ transform.setColumn(3, streamSource->mPosition);
+ transform.setRow(1, streamSource->mDirection);
+
+#ifdef REL_WORKAROUND
+ alSourcei(source, AL_SOURCE_ABSOLUTE, AL_TRUE);
+#else
+ alSourcei(source, AL_SOURCE_RELATIVE, AL_FALSE);
+#endif
+
+ Point3F p;
+ transform.getColumn(3, &p);
+ alSource3f(source, AL_POSITION, p.x, p.y, p.z);
+
+ //Always use ConeVector (which is tied to transform)
+ alSource3f(source, AL_DIRECTION, desc->mConeVector.x, desc->mConeVector.y, desc->mConeVector.z);
+ }
+ else
+ {
+ // 2D sound
+ // JMQ: slam the stream source's position to our desired value
+ streamSource->mPosition = Point3F(0.0f, 0.0f, 1.0f);
+ alSourcei(source, AL_SOURCE_RELATIVE, AL_TRUE);
+ alSource3f(source, AL_POSITION,
+ streamSource->mPosition.x,
+ streamSource->mPosition.y,
+ streamSource->mPosition.z);
+ }
+
+ alSourcef(source, AL_REFERENCE_DISTANCE, desc->mReferenceDistance);
+ alSourcef(source, AL_MAX_DISTANCE, desc->mMaxDistance);
+
+/* todo
+ // environmental audio stuff:
+ alSourcef(source, AL_ENV_SAMPLE_REVERB_MIX_EXT, desc->mEnvironmentLevel);
+ if(desc->mEnvironmentLevel != 0.f)
+ alSourceResetEnvironment_EXT(source);
+*/
+}
+
+//--------------------------------------------------------------------------
+AUDIOHANDLE alxCreateSource(const Audio::Description *desc,
+ const char *filename,
+ const MatrixF *transform,
+ AudioSampleEnvironment *sampleEnvironment)
+{
+ if (!mContext)
+ return NULL_AUDIOHANDLE;
+
+ if(desc == NULL || filename == NULL || *filename == '\0')
+ return NULL_AUDIOHANDLE;
+
+ F32 volume = desc->mVolume;
+
+ // calculate an approximate attenuation for 3d sounds
+ if(transform && desc->mIs3D)
+ {
+ Point3F position;
+ transform->getColumn(3, &position);
+ volume *= approximate3DVolume(desc, position);
+ }
+
+ // check the type specific volume
+ AssertFatal(desc->mType < Audio::NumAudioTypes, "alxCreateSource: invalid type for source");
+ if(desc->mType >= Audio::NumAudioTypes)
+ return(NULL_AUDIOHANDLE);
+
+ // done if channel is muted (and not a looper)
+ if(!desc->mIsLooping && !desc->mIsStreaming && (mAudioTypeVolume[desc->mType] == 0.f))
+ return(NULL_AUDIOHANDLE);
+
+ // scale volume by channel attenuation
+ volume *= mAudioTypeVolume[desc->mType];
+
+ // non-loopers don't add if < minvolume
+ if(!desc->mIsLooping && !desc->mIsStreaming && (volume <= MIN_GAIN))
+ return(NULL_AUDIOHANDLE);
+
+ U32 index = MAX_AUDIOSOURCES;
+
+ // try and find an available source: 0 volume loopers get added to inactive list
+ if(volume > MIN_GAIN)
+ {
+ if(!findFreeSource(&index))
+ {
+ alxUpdateScores(true);
+
+ // scores do not include master volume
+ if(!cullSource(&index, volume))
+ index = MAX_AUDIOSOURCES;
+ }
+ }
+
+ // make sure that loopers are added
+ if(index == MAX_AUDIOSOURCES)
+ {
+ if(desc->mIsLooping && !(desc->mIsStreaming))
+ {
+ Resource buffer = AudioBuffer::find(filename);
+ if(!(bool)buffer)
+ return(NULL_AUDIOHANDLE);
+
+ // create the inactive looping image
+ LoopingImage * image = createLoopingImage();
+
+ image->mHandle = getNewHandle() | AUDIOHANDLE_LOOPING_BIT | AUDIOHANDLE_INACTIVE_BIT;
+ image->mBuffer = buffer;
+ image->mDescription = *desc;
+ image->mScore = volume;
+ image->mEnvironment = sampleEnvironment;
+
+ // grab position/direction if 3d source
+ if(transform)
+ {
+ transform->getColumn(3, &image->mPosition);
+ transform->getColumn(1, &image->mDirection);
+ }
+
+ AssertFatal(!mLoopingInactiveList.findImage(image->mHandle), "alxCreateSource: handle in inactive list");
+ AssertFatal(!mLoopingCulledList.findImage(image->mHandle), "alxCreateSource: handle in culled list");
+
+ // add to the looping and inactive lists
+ mLoopingList.push_back(image);
+ mLoopingInactiveList.push_back(image);
+ return(image->mHandle & RETURN_MASK);
+ }
+ else
+ return(NULL_AUDIOHANDLE);
+ }
+
+ // make sure that streamers are added
+ if(index == MAX_AUDIOSOURCES)
+ {
+ if(desc->mIsStreaming)
+ {
+ // create the inactive audio stream
+ AudioStreamSource * streamSource = createStreamingSource(filename);
+ if (streamSource)
+ {
+ streamSource->mHandle = getNewHandle() | AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_INACTIVE_BIT;
+ streamSource->mSource = NULL;
+ streamSource->mDescription = *desc;
+ streamSource->mScore = volume;
+ streamSource->mEnvironment = sampleEnvironment;
+
+ // grab position/direction if 3d source
+ if(transform)
+ {
+ transform->getColumn(3, &streamSource->mPosition);
+ transform->getColumn(1, &streamSource->mDirection);
+ }
+
+ AssertFatal(!mStreamingInactiveList.findImage(streamSource->mHandle), "alxCreateSource: handle in inactive list");
+ AssertFatal(!mStreamingCulledList.findImage(streamSource->mHandle), "alxCreateSource: handle in culled list");
+
+ // add to the streaming and inactive lists
+ mStreamingList.push_back(streamSource);
+ mStreamingInactiveList.push_back(streamSource);
+ return(streamSource->mHandle & RETURN_MASK);
+ }
+ else
+ return NULL_AUDIOHANDLE;
+ }
+ else
+ return(NULL_AUDIOHANDLE);
+ }
+
+ // clear the error state
+ alGetError();
+
+ // grab the buffer
+ Resource buffer;
+ if(!(desc->mIsStreaming)) {
+ buffer = AudioBuffer::find(filename);
+ if((bool)buffer == false)
+ return NULL_AUDIOHANDLE;
+ }
+
+ // init the source (created inactive) and store needed values
+ mHandle[index] = getNewHandle() | AUDIOHANDLE_INACTIVE_BIT;
+ mType[index] = desc->mType;
+ if(!(desc->mIsStreaming)) {
+ mBuffer[index] = buffer;
+ }
+ mScore[index] = volume;
+ mSourceVolume[index] = desc->mVolume;
+ mSampleEnvironment[index] = sampleEnvironment;
+
+ ALuint source = mSource[index];
+
+ // setup play info
+ if(!desc->mIsStreaming)
+ alxSourcePlay(source, buffer, desc, desc->mIs3D ? transform : 0);
+
+ if(mEnvironmentEnabled)
+ alxSourceEnvironment(source, desc->mEnvironmentLevel, sampleEnvironment);
+
+ // setup a LoopingImage ONLY if the sound is a looper:
+ if(desc->mIsLooping && !(desc->mIsStreaming))
+ {
+ mHandle[index] |= AUDIOHANDLE_LOOPING_BIT;
+
+ LoopingImage * image = createLoopingImage();
+ image->mHandle = mHandle[index];
+ image->mBuffer = buffer;
+ image->mDescription = *desc;
+ image->mScore = volume;
+ image->mEnvironment = sampleEnvironment;
+
+ // grab position/direction
+ if(transform)
+ {
+ transform->getColumn(3, &image->mPosition);
+ transform->getColumn(1, &image->mDirection);
+ }
+
+ AssertFatal(!mLoopingInactiveList.findImage(image->mHandle), "alxCreateSource: handle in inactive list");
+ AssertFatal(!mLoopingCulledList.findImage(image->mHandle), "alxCreateSource: handle in culled list");
+
+ // add to the looping list
+ mLoopingList.push_back(image);
+ }
+
+ // setup a AudioStreamSource ONLY if the sound is a streamer:
+ if(desc->mIsStreaming)
+ {
+ // Intangir> why is loading bit never used anywhere else?
+ // comes in handy for my oggmixedstream
+ // (prevents it from being deleted before it is loaded)
+ mHandle[index] |= AUDIOHANDLE_STREAMING_BIT | AUDIOHANDLE_LOADING_BIT;
+
+ AudioStreamSource * streamSource = createStreamingSource(filename);
+ if (streamSource)
+ {
+ streamSource->mHandle = mHandle[index];
+ streamSource->mSource = mSource[index];
+ streamSource->mDescription = *desc;
+ streamSource->mScore = volume;
+ streamSource->mEnvironment = sampleEnvironment;
+
+ // grab position/direction
+ if(transform)
+ {
+ transform->getColumn(3, &streamSource->mPosition);
+ transform->getColumn(1, &streamSource->mDirection);
+ }
+
+ AssertFatal(!mStreamingInactiveList.findImage(streamSource->mHandle), "alxCreateSource: handle in inactive list");
+ AssertFatal(!mStreamingCulledList.findImage(streamSource->mHandle), "alxCreateSource: handle in culled list");
+
+ alxSourcePlay(streamSource);
+
+ // add to the looping list
+ mStreamingList.push_back(streamSource);
+ }
+ else
+ {
+ mSampleEnvironment[index] = 0;
+ mHandle[index] = NULL_AUDIOHANDLE;
+ mBuffer[index] = 0;
+ return NULL_AUDIOHANDLE;
+ }
+ }
+
+ // clear off all but looping bit
+ return(mHandle[index] & RETURN_MASK);
+}
+
+//------------------------------------------------------------------------------
+AUDIOHANDLE alxCreateSource(AudioDescription *descObject,
+ const char *filename,
+ const MatrixF *transform,
+ AudioSampleEnvironment * sampleEnvironment )
+{
+ if(!descObject || !descObject->getDescription())
+ return(NULL_AUDIOHANDLE);
+ return (alxCreateSource(descObject->getDescription(), filename, transform, sampleEnvironment));
+}
+
+AUDIOHANDLE alxCreateSource(const AudioProfile *profile, const MatrixF *transform)
+{
+ if (profile == NULL)
+ return NULL_AUDIOHANDLE;
+
+ return alxCreateSource(profile->mDescriptionObject, profile->mFilename, transform, profile->mSampleEnvironment);
+}
+
+//--------------------------------------------------------------------------
+extern void threadPlay(AudioBuffer * buffer, AUDIOHANDLE handle);
+
+AUDIOHANDLE alxPlay(AUDIOHANDLE handle)
+{
+ U32 index = alxFindIndex(handle);
+
+ if(index != MAX_AUDIOSOURCES)
+ {
+ // play if not already playing
+ if(mHandle[index] & AUDIOHANDLE_INACTIVE_BIT)
+ {
+ mHandle[index] &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
+
+ // make sure the looping image also clears it's inactive bit
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ (*itr)->mHandle &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
+
+ // make sure the streaming image also clears it's inactive bit
+ StreamingList::iterator itr2 = mStreamingList.findImage(handle);
+ if(itr2)
+ (*itr2)->mHandle &= ~(AUDIOHANDLE_INACTIVE_BIT | AUDIOHANDLE_LOADING_BIT);
+
+ alSourcePlay(mSource[index]);
+
+ return(handle);
+ }
+ }
+ else
+ {
+ // move inactive loopers to the culled list, try to start the sound
+ LoopingList::iterator itr = mLoopingInactiveList.findImage(handle);
+ if(itr)
+ {
+ AssertFatal(!mLoopingCulledList.findImage(handle), "alxPlay: image already in culled list");
+ mLoopingCulledList.push_back(*itr);
+ mLoopingInactiveList.erase_fast(itr);
+ alxLoopingUpdate();
+ return(handle);
+ }
+ else if(mLoopingCulledList.findImage(handle))
+ {
+ alxLoopingUpdate();
+ return(handle);
+ }
+ else
+ return(NULL_AUDIOHANDLE);
+
+ // move inactive streamers to the culled list, try to start the sound
+ StreamingList::iterator itr2 = mStreamingInactiveList.findImage(handle);
+ if(itr2)
+ {
+ AssertFatal(!mStreamingCulledList.findImage(handle), "alxPlay: image already in culled list");
+ (*itr2)->freeStream();
+ mStreamingCulledList.push_back(*itr2);
+ mStreamingInactiveList.erase_fast(itr2);
+ alxStreamingUpdate();
+ return(handle);
+ }
+ else if(mStreamingCulledList.findImage(handle))
+ {
+ alxStreamingUpdate();
+ return(handle);
+ }
+ else
+ return(NULL_AUDIOHANDLE);
+ }
+
+ return(handle);
+}
+
+//--------------------------------------------------------------------------
+// helper function.. create a source and play it
+AUDIOHANDLE alxPlay(const AudioProfile *profile, const MatrixF *transform, const Point3F* /*velocity*/)
+{
+ if(profile == NULL)
+ return NULL_AUDIOHANDLE;
+
+ AUDIOHANDLE handle = alxCreateSource(profile->mDescriptionObject, profile->mFilename, transform, profile->mSampleEnvironment);
+ if(handle != NULL_AUDIOHANDLE)
+ return(alxPlay(handle));
+ return(handle);
+}
+
+//--------------------------------------------------------------------------
+void alxStop(AUDIOHANDLE handle)
+{
+ U32 index = alxFindIndex(handle);
+
+ // stop it
+ if(index != MAX_AUDIOSOURCES)
+ {
+ if(!(mHandle[index] & AUDIOHANDLE_INACTIVE_BIT))
+ {
+ alSourceStop(mSource[index]);
+ }
+
+ mSampleEnvironment[index] = 0;
+ mHandle[index] = NULL_AUDIOHANDLE;
+ mBuffer[index] = 0;
+ }
+
+ // remove loopingImage and add it to the free list
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ // remove from inactive/culled list
+ if((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
+ {
+ LoopingList::iterator tmp = mLoopingInactiveList.findImage(handle);
+
+ // inactive?
+ if(tmp)
+ mLoopingInactiveList.erase_fast(tmp);
+ else
+ {
+ //culled?
+ tmp = mLoopingCulledList.findImage(handle);
+ AssertFatal(tmp, "alxStop: failed to find inactive looping source");
+ mLoopingCulledList.erase_fast(tmp);
+ }
+ }
+
+ AssertFatal(!mLoopingInactiveList.findImage((*itr)->mHandle), "alxStop: handle in inactive list");
+ AssertFatal(!mLoopingCulledList.findImage((*itr)->mHandle), "alxStop: handle in culled list");
+
+ // remove it
+ (*itr)->clear();
+ mLoopingFreeList.push_back(*itr);
+ mLoopingList.erase_fast(itr);
+ }
+
+ // remove streamingImage and add it to the free list
+ StreamingList::iterator itr2 = mStreamingList.findImage(handle);
+ if(itr2)
+ {
+ // remove from inactive/culled list
+ if((*itr2)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
+ {
+ StreamingList::iterator tmp = mStreamingInactiveList.findImage(handle);
+
+ // inactive?
+ if(tmp)
+ mStreamingInactiveList.erase_fast(tmp);
+ else
+ {
+ //culled?
+ tmp = mStreamingCulledList.findImage(handle);
+ AssertFatal(tmp, "alxStop: failed to find inactive looping source");
+ mStreamingCulledList.erase_fast(tmp);
+ }
+ }
+
+ AssertFatal(!mStreamingInactiveList.findImage((*itr2)->mHandle), "alxStop: handle in inactive list");
+ AssertFatal(!mStreamingCulledList.findImage((*itr2)->mHandle), "alxStop: handle in culled list");
+
+ // remove it
+ (*itr2)->freeStream();
+ delete(*itr2);
+ mStreamingList.erase_fast(itr2);
+ }
+}
+
+//--------------------------------------------------------------------------
+void alxStopAll()
+{
+ // stop all open sources
+ for(S32 i = mNumSources - 1; i >= 0; i--)
+ if(mHandle[i] != NULL_AUDIOHANDLE)
+ alxStop(mHandle[i]);
+
+ // stop all looping sources
+ while(mLoopingList.size())
+ alxStop(mLoopingList.last()->mHandle);
+
+// stop all streaming sources
+ while(mStreamingList.size())
+ alxStop(mStreamingList.last()->mHandle);
+}
+
+void alxLoopSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
+{
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_GAIN:
+ (*itr)->mDescription.mVolume = Audio::DBToLinear(value);
+ break;
+ case AL_GAIN_LINEAR:
+ (*itr)->mDescription.mVolume = value;
+ break;
+ case AL_PITCH:
+ (*itr)->mPitch = value;
+ break;
+ case AL_REFERENCE_DISTANCE:
+ (*itr)->mDescription.mReferenceDistance = value;
+ break;
+ case AL_CONE_OUTER_GAIN:
+ (*itr)->mDescription.mMaxDistance = value;
+ break;
+ }
+ }
+}
+
+void alxLoopSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
+{
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_POSITION:
+ (*itr)->mPosition.x = value1;
+ (*itr)->mPosition.y = value2;
+ (*itr)->mPosition.z = value3;
+ break;
+
+ case AL_DIRECTION:
+ (*itr)->mDirection.x = value1;
+ (*itr)->mDirection.y = value2;
+ (*itr)->mDirection.z = value3;
+ break;
+ }
+ }
+}
+
+void alxLoopSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
+{
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ //case AL_SOURCE_AMBIENT:
+ // (*itr)->mDescription.mIs3D = value;
+ // break;
+ case AL_CONE_INNER_ANGLE:
+ (*itr)->mDescription.mConeInsideAngle = value;
+ break;
+ case AL_CONE_OUTER_ANGLE:
+ (*itr)->mDescription.mConeOutsideAngle = value;
+ break;
+ }
+ }
+}
+
+void alxLoopGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
+{
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_GAIN:
+ *value = Audio::linearToDB((*itr)->mDescription.mVolume);
+ break;
+ case AL_GAIN_LINEAR:
+ *value = (*itr)->mDescription.mVolume;
+ break;
+ case AL_PITCH:
+ *value = (*itr)->mPitch;
+ break;
+ case AL_REFERENCE_DISTANCE:
+ *value = (*itr)->mDescription.mReferenceDistance;
+ break;
+ case AL_CONE_OUTER_GAIN:
+ *value = (*itr)->mDescription.mMaxDistance;
+ break;
+ }
+ }
+}
+
+void alxLoopGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
+{
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_POSITION:
+ *value1 = (*itr)->mPosition.x;
+ *value2 = (*itr)->mPosition.y;
+ *value3 = (*itr)->mPosition.z;
+ break;
+
+ case AL_DIRECTION:
+ *value1 = (*itr)->mDirection.x;
+ *value2 = (*itr)->mDirection.y;
+ *value3 = (*itr)->mDirection.z;
+ break;
+ }
+ }
+}
+
+void alxLoopGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
+{
+ LoopingList::iterator itr = mLoopingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ //case AL_SOURCE_AMBIENT:
+ // *value = (*itr)->mDescription.mIs3D;
+ // break;
+ case AL_LOOPING:
+ *value = true;
+ break;
+ case AL_CONE_INNER_ANGLE:
+ *value = (*itr)->mDescription.mConeInsideAngle;
+ break;
+ case AL_CONE_OUTER_ANGLE:
+ *value = (*itr)->mDescription.mConeOutsideAngle;
+ break;
+ }
+ }
+}
+
+//------------------------------------------------------
+
+void alxStreamSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_GAIN:
+ (*itr)->mDescription.mVolume = Audio::DBToLinear(value);
+ break;
+ case AL_GAIN_LINEAR:
+ (*itr)->mDescription.mVolume = value;
+ break;
+ case AL_PITCH:
+ (*itr)->mPitch = value;
+ break;
+ case AL_REFERENCE_DISTANCE:
+ (*itr)->mDescription.mReferenceDistance = value;
+ break;
+ case AL_CONE_OUTER_GAIN:
+ (*itr)->mDescription.mMaxDistance = value;
+ break;
+ }
+ }
+}
+
+void alxStreamSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_POSITION:
+ (*itr)->mPosition.x = value1;
+ (*itr)->mPosition.y = value2;
+ (*itr)->mPosition.z = value3;
+ break;
+
+ case AL_DIRECTION:
+ (*itr)->mDirection.x = value1;
+ (*itr)->mDirection.y = value2;
+ (*itr)->mDirection.z = value3;
+ break;
+ }
+ }
+}
+
+void alxStreamSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ //case AL_SOURCE_AMBIENT:
+ // (*itr)->mDescription.mIs3D = value;
+ // break;
+ case AL_CONE_INNER_ANGLE:
+ (*itr)->mDescription.mConeInsideAngle = value;
+ break;
+ case AL_CONE_OUTER_ANGLE:
+ (*itr)->mDescription.mConeOutsideAngle = value;
+ break;
+ }
+ }
+}
+
+void alxStreamGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_GAIN:
+ *value = Audio::linearToDB((*itr)->mDescription.mVolume);
+ break;
+ case AL_GAIN_LINEAR:
+ *value = (*itr)->mDescription.mVolume;
+ break;
+ case AL_PITCH:
+ *value = (*itr)->mPitch;
+ break;
+ case AL_REFERENCE_DISTANCE:
+ *value = (*itr)->mDescription.mReferenceDistance;
+ break;
+ case AL_CONE_OUTER_GAIN:
+ *value = (*itr)->mDescription.mMaxDistance;
+ break;
+ }
+ }
+}
+
+void alxStreamGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ case AL_POSITION:
+ *value1 = (*itr)->mPosition.x;
+ *value2 = (*itr)->mPosition.y;
+ *value3 = (*itr)->mPosition.z;
+ break;
+
+ case AL_DIRECTION:
+ *value1 = (*itr)->mDirection.x;
+ *value2 = (*itr)->mDirection.y;
+ *value3 = (*itr)->mDirection.z;
+ break;
+ }
+ }
+}
+
+void alxStreamGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if(itr)
+ {
+ switch(pname)
+ {
+ //case AL_SOURCE_AMBIENT:
+ // *value = (*itr)->mDescription.mIs3D;
+ // break;
+ case AL_LOOPING:
+ *value = true;
+ break;
+ case AL_CONE_INNER_ANGLE:
+ *value = (*itr)->mDescription.mConeInsideAngle;
+ break;
+ case AL_CONE_OUTER_ANGLE:
+ *value = (*itr)->mDescription.mConeOutsideAngle;
+ break;
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+// AL get/set methods: Source
+//--------------------------------------------------------------------------
+// - only need to worry about playing sources.. proper volume gets set on
+// create source (so, could get out of sync if someone changes volume between
+// a createSource and playSource call...)
+void alxUpdateTypeGain(U32 type)
+{
+ for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
+ {
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ continue;
+
+ if(type != mType[i])
+ continue;
+
+ ALint state = AL_STOPPED;
+ alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
+
+ if(state == AL_PLAYING)
+ {
+ // volume = SourceVolume * ChannelVolume * MasterVolume
+ F32 vol = mClampF(mSourceVolume[i] * mAudioTypeVolume[mType[i]] * mMasterVolume, 0.f, 1.f);
+ alSourcef(mSource[i], AL_GAIN, Audio::linearToDB(vol) );
+ }
+ }
+}
+
+void alxSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat value)
+{
+ ALuint source = alxFindSource(handle);
+
+ if(source != INVALID_SOURCE)
+ {
+ // ensure gain_linear
+ if(pname == AL_GAIN)
+ {
+ value = Audio::DBToLinear(value);
+ pname = AL_GAIN_LINEAR;
+ }
+
+ // need to process gain settings (so source can be affected by channel/master gains)
+ if(pname == AL_GAIN_LINEAR)
+ {
+ U32 idx = alxFindIndex(handle);
+ AssertFatal(idx != MAX_AUDIOSOURCES, "alxSourcef: handle not located for found source");
+ if(idx == MAX_AUDIOSOURCES)
+ return;
+
+ // update the stored value
+ mSourceVolume[idx] = value;
+
+ // volume = SourceVolume * ChannelVolume * MasterVolume
+
+// #ifdef REL_WORKAROUND
+// ALint val = AL_TRUE;
+// alGetSourcei(source, AL_SOURCE_ABSOLUTE, &val);
+// if(val == AL_FALSE)
+// #else
+// ALint val = AL_FALSE;
+// alGetSourcei(source, AL_SOURCE_RELATIVE, &val);
+// if(val == AL_TRUE)
+// #endif
+ {
+ F32 vol = mClampF(mSourceVolume[idx] * mAudioTypeVolume[mType[idx]] * mMasterVolume, 0.f, 1.f);
+ alSourcef(source, AL_GAIN, Audio::linearToDB(vol) );
+ }
+ }
+ else
+ alSourcef(source, pname, value);
+ }
+ alxLoopSourcef(handle, pname, value);
+ alxStreamSourcef(handle, pname, value);
+}
+
+void alxSourcefv(AUDIOHANDLE handle, ALenum pname, ALfloat *values)
+{
+ ALuint source = alxFindSource(handle);
+ if(source != INVALID_SOURCE)
+ alSourcefv(source, pname, values);
+
+ if((pname == AL_POSITION) || (pname == AL_DIRECTION) || (pname == AL_VELOCITY)) {
+ alxLoopSource3f(handle, pname, values[0], values[1], values[2]);
+ alxStreamSource3f(handle, pname, values[0], values[1], values[2]);
+ }
+}
+
+void alxSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat value1, ALfloat value2, ALfloat value3)
+{
+ ALuint source = alxFindSource(handle);
+ if(source != INVALID_SOURCE)
+ {
+ ALfloat values[3];
+ values[0] = value1;
+ values[1] = value2;
+ values[2] = value3;
+ alSourcefv(source, pname, values);
+ }
+ alxLoopSource3f(handle, pname, value1, value2, value3);
+ alxStreamSource3f(handle, pname, value1, value2, value3);
+}
+
+void alxSourcei(AUDIOHANDLE handle, ALenum pname, ALint value)
+{
+ ALuint source = alxFindSource(handle);
+ if(source != INVALID_SOURCE)
+ alSourcei(source, pname, value);
+ alxLoopSourcei(handle, pname, value);
+ alxStreamSourcei(handle, pname, value);
+}
+
+// sets the position and direction of the source
+void alxSourceMatrixF(AUDIOHANDLE handle, const MatrixF *transform)
+{
+ ALuint source = alxFindSource(handle);
+
+ Point3F pos;
+ transform->getColumn(3, &pos);
+
+ Point3F dir;
+ transform->getColumn(1, &dir);
+
+ if(source != INVALID_SOURCE)
+ {
+ // OpenAL uses a Right-Handed corrdinate system so flip the orientation vector
+ alSource3f(source, AL_POSITION, pos.x, pos.y, pos.z);
+ alSource3f(source, AL_DIRECTION, -dir.x, -dir.y, -dir.z);
+ }
+
+ alxLoopSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
+ alxLoopSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
+ alxStreamSource3f(handle, AL_POSITION, pos.x, pos.y, pos.z);
+ alxStreamSource3f(handle, AL_DIRECTION, dir.x, dir.y, dir.z);
+}
+
+//--------------------------------------------------------------------------
+void alxGetSourcef(AUDIOHANDLE handle, ALenum pname, ALfloat *value)
+{
+ ALuint source = alxFindSource(handle);
+ if(source != INVALID_SOURCE)
+ {
+ // gain queries return unattenuated values
+ if((pname == AL_GAIN) || (pname == AL_GAIN_LINEAR))
+ {
+ U32 idx = alxFindIndex(handle);
+ AssertFatal(idx != MAX_AUDIOSOURCES, "alxGetSourcef: found source but handle is invalid");
+ if(idx == MAX_AUDIOSOURCES)
+ {
+ *value = 0.f;
+ return;
+ }
+
+ if(pname == AL_GAIN)
+ *value = Audio::linearToDB(mSourceVolume[idx]);
+ else
+ *value = mSourceVolume[idx];
+ }
+ else
+ alGetSourcef(source, pname, value);
+ }
+ else if(handle & AUDIOHANDLE_LOOPING_BIT)
+ alxLoopGetSourcef(handle, pname, value);
+ else
+ alxStreamGetSourcef(handle, pname, value);
+}
+
+void alxGetSourcefv(AUDIOHANDLE handle, ALenum pname, ALfloat *values)
+{
+ if((pname == AL_POSITION) || (pname == AL_DIRECTION) || (pname == AL_VELOCITY))
+ alxGetSource3f(handle, pname, &values[0], &values[1], &values[2]);
+}
+
+void alxGetSource3f(AUDIOHANDLE handle, ALenum pname, ALfloat *value1, ALfloat *value2, ALfloat *value3)
+{
+ ALuint source = alxFindSource(handle);
+ if(source != INVALID_SOURCE)
+ {
+ ALfloat values[3];
+ alGetSourcefv(source, pname, values);
+ *value1 = values[0];
+ *value2 = values[1];
+ *value3 = values[2];
+ }
+ else if(handle & AUDIOHANDLE_LOOPING_BIT)
+ alxLoopGetSource3f(handle, pname, value1, value2, value3);
+ else
+ alxStreamGetSource3f(handle, pname, value1, value2, value3);
+}
+
+void alxGetSourcei(AUDIOHANDLE handle, ALenum pname, ALint *value)
+{
+ ALuint source = alxFindSource(handle);
+ if(source != INVALID_SOURCE)
+ alGetSourcei(source, pname, value);
+ else if(handle & AUDIOHANDLE_LOOPING_BIT)
+ alxLoopGetSourcei(handle, pname, value);
+ else
+ alxStreamGetSourcei(handle, pname, value);
+}
+
+
+//--------------------------------------------------------------------------
+/** alListenerfv extension for use with MatrixF's
+ Set the listener's position and orientation using a matrix
+*/
+void alxListenerMatrixF(const MatrixF *transform)
+{
+ Point3F p1, p2;
+ transform->getColumn(3, &p1);
+ alListener3f(AL_POSITION, p1.x, p1.y, p1.z);
+
+ transform->getColumn(2, &p1); // Up Vector
+ transform->getColumn(1, &p2); // Forward Vector
+
+ F32 orientation[6];
+ orientation[0] = -p1.x;
+ orientation[1] = -p1.y;
+ orientation[2] = -p1.z;
+ orientation[3] = p2.x;
+ orientation[4] = p2.y;
+ orientation[5] = p2.z;
+
+ alListenerfv(AL_ORIENTATION, orientation);
+}
+
+//--------------------------------------------------------------------------
+/** alListenerf extension supporting linear gain
+*/
+void alxListenerf(ALenum param, ALfloat value)
+{
+ if (param == AL_GAIN_LINEAR)
+ {
+ value = Audio::linearToDB(value);
+ param = AL_GAIN;
+ }
+ alListenerf(param, value);
+}
+
+//--------------------------------------------------------------------------
+/** alGetListenerf extension supporting linear gain
+*/
+void alxGetListenerf(ALenum param, ALfloat *value)
+{
+ if (param == AL_GAIN_LINEAR)
+ {
+ alGetListenerf(AL_GAIN, value);
+ *value = Audio::DBToLinear(*value);
+ }
+ else
+ alGetListenerf(param, value);
+}
+
+
+//--------------------------------------------------------------------------
+// Simple metrics
+//--------------------------------------------------------------------------
+
+
+#ifdef TORQUE_GATHER_METRICS
+static void alxGatherMetrics()
+{
+ S32 mNumOpenHandles = 0;
+ S32 mNumOpenLoopingHandles = 0;
+ S32 mNumOpenStreamingHandles = 0;
+
+ S32 mNumActiveStreams = 0;
+ S32 mNumNullActiveStreams = 0;
+ S32 mNumActiveLoopingStreams = 0;
+ S32 mNumActiveStreamingStreams = 0;
+
+ S32 mNumLoopingStreams = 0;
+ S32 mNumInactiveLoopingStreams = 0;
+ S32 mNumCulledLoopingStreams = 0;
+ S32 mNumStreamingStreams = 0;
+ S32 mNumInactiveStreamingStreams = 0;
+ S32 mNumCulledStreamingStreams = 0;
+
+ // count installed streams and open handles
+ for(U32 i = 0; i < mNumSources; i++)
+ {
+ if(mHandle[i] != NULL_AUDIOHANDLE)
+ {
+ mNumOpenHandles++;
+ if(mHandle[i] & AUDIOHANDLE_LOOPING_BIT)
+ mNumOpenLoopingHandles++;
+ if(mHandle[i] & AUDIOHANDLE_STREAMING_BIT)
+ mNumOpenStreamingHandles++;
+ }
+
+ ALint state = AL_STOPPED;
+ alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
+ if(state == AL_PLAYING)
+ {
+ mNumActiveStreams++;
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ mNumNullActiveStreams++;
+ if(mHandle[i] & AUDIOHANDLE_LOOPING_BIT)
+ mNumActiveLoopingStreams++;
+ if(mHandle[i] & AUDIOHANDLE_STREAMING_BIT)
+ mNumActiveStreamingStreams++;
+ }
+ }
+
+ for(LoopingList::iterator itr = mLoopingList.begin(); itr != mLoopingList.end(); itr++)
+ mNumLoopingStreams++;
+ for(LoopingList::iterator itr = mLoopingInactiveList.begin(); itr != mLoopingInactiveList.end(); itr++)
+ mNumInactiveLoopingStreams++;
+ for(LoopingList::iterator itr = mLoopingCulledList.begin(); itr != mLoopingCulledList.end(); itr++)
+ mNumCulledLoopingStreams++;
+
+ for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
+ mNumStreamingStreams++;
+ for(StreamingList::iterator itr = mStreamingInactiveList.begin(); itr != mStreamingInactiveList.end(); itr++)
+ mNumInactiveStreamingStreams++;
+ for(StreamingList::iterator itr = mStreamingCulledList.begin(); itr != mStreamingCulledList.end(); itr++)
+ mNumCulledStreamingStreams++;
+
+ Con::setIntVariable("Audio::numOpenHandles", mNumOpenHandles);
+ Con::setIntVariable("Audio::numOpenLoopingHandles", mNumOpenLoopingHandles);
+ Con::setIntVariable("Audio::numOpenStreamingHandles", mNumOpenStreamingHandles);
+
+ Con::setIntVariable("Audio::numActiveStreams", mNumActiveStreams);
+ Con::setIntVariable("Audio::numNullActiveStreams", mNumNullActiveStreams);
+ Con::setIntVariable("Audio::numActiveLoopingStreams", mNumActiveLoopingStreams);
+ Con::setIntVariable("Audio::numActiveStreamingStreams", mNumActiveStreamingStreams);
+
+ Con::setIntVariable("Audio::numLoopingStreams", mNumLoopingStreams);
+ Con::setIntVariable("Audio::numInactiveLoopingStreams", mNumInactiveLoopingStreams);
+ Con::setIntVariable("Audio::numCulledLoopingStreams", mNumCulledLoopingStreams);
+
+ Con::setIntVariable("Audio::numStreamingStreams", mNumStreamingStreams);
+ Con::setIntVariable("Audio::numInactiveStreamingStreams", mNumInactiveStreamingStreams);
+ Con::setIntVariable("Audio::numCulledStreamingStreams", mNumCulledStreamingStreams);
+}
+#endif
+
+//--------------------------------------------------------------------------
+// Audio Update...
+//--------------------------------------------------------------------------
+void alxLoopingUpdate()
+{
+ static LoopingList culledList;
+
+ U32 updateTime = Platform::getRealMilliseconds();
+
+ // check if can wakeup the inactive loopers
+ if(mLoopingCulledList.size())
+ {
+ Point3F listener;
+ alxGetListenerPoint3F(AL_POSITION, &listener);
+
+ // get the 'sort' value for this sound (could be based on time played...),
+ // and add to the culled list
+ LoopingList::iterator itr;
+ culledList.clear();
+
+ for(itr = mLoopingCulledList.begin(); itr != mLoopingCulledList.end(); itr++)
+ {
+ if((*itr)->mScore <= MIN_UNCULL_GAIN)
+ continue;
+
+ if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
+ continue;
+
+ culledList.push_back(*itr);
+ }
+
+ if(!culledList.size())
+ return;
+
+ U32 index = MAX_AUDIOSOURCES;
+
+ if(culledList.size() > 1)
+ culledList.sort();
+
+ for(itr = culledList.begin(); itr != culledList.end(); itr++)
+ {
+ if(!findFreeSource(&index))
+ {
+ // score does not include master volume
+ if(!cullSource(&index, (*itr)->mScore))
+ break;
+
+ // check buffer
+ if(!bool((*itr)->mBuffer))
+ {
+ // remove from culled list
+ LoopingList::iterator tmp;
+ tmp = mLoopingCulledList.findImage((*itr)->mHandle);
+ AssertFatal(tmp, "alxLoopingUpdate: failed to find culled source");
+ mLoopingCulledList.erase_fast(tmp);
+
+ // remove from looping list (and free)
+ tmp = mLoopingList.findImage((*itr)->mHandle);
+ if(tmp)
+ {
+ (*tmp)->clear();
+ mLoopingFreeList.push_back(*tmp);
+ mLoopingList.erase_fast(tmp);
+ }
+
+ continue;
+ }
+ }
+
+ // remove from culled list
+ LoopingList::iterator tmp = mLoopingCulledList.findImage((*itr)->mHandle);
+ AssertFatal(tmp, "alxLoopingUpdate: failed to find culled source");
+ mLoopingCulledList.erase_fast(tmp);
+
+ // restore all state data
+ mHandle[index] = (*itr)->mHandle;
+ mBuffer[index] = (*itr)->mBuffer;
+ mScore[index] = (*itr)->mScore;
+ mSourceVolume[index] = (*itr)->mDescription.mVolume;
+ mType[index] = (*itr)->mDescription.mType;
+ mSampleEnvironment[index] = (*itr)->mEnvironment;
+
+ ALuint source = mSource[index];
+
+ // setup play info
+ alGetError();
+
+ alxSourcePlay(source, *itr);
+ if(mEnvironmentEnabled)
+ alxSourceEnvironment(source, *itr);
+
+ alxPlay(mHandle[index]);
+ }
+ }
+}
+
+void alxStreamingUpdate()
+{
+ // update buffer queues on active streamers
+ // update the loopers
+ for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
+ {
+ if((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT)
+ continue;
+
+ (*itr)->updateBuffers();
+ }
+
+ static StreamingList culledList;
+
+ U32 updateTime = Platform::getRealMilliseconds();
+
+ // check if can wakeup the inactive loopers
+ if(mStreamingCulledList.size())
+ {
+ Point3F listener;
+ alxGetListenerPoint3F(AL_POSITION, &listener);
+
+ // get the 'sort' value for this sound (could be based on time played...),
+ // and add to the culled list
+ StreamingList::iterator itr;
+ culledList.clear();
+
+ for(itr = mStreamingCulledList.begin(); itr != mStreamingCulledList.end(); itr++)
+ {
+ if((*itr)->mScore <= MIN_UNCULL_GAIN)
+ continue;
+
+ if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
+ continue;
+ culledList.push_back(*itr);
+ }
+
+ if(!culledList.size())
+ return;
+
+ U32 index = MAX_AUDIOSOURCES;
+
+ if(culledList.size() > 1)
+ culledList.sort();
+
+ for(itr = culledList.begin(); itr != culledList.end(); itr++)
+ {
+ if(!findFreeSource(&index))
+ {
+ // score does not include master volume
+ if(!cullSource(&index, (*itr)->mScore))
+ break;
+
+ // check buffer
+ //if(!bool((*itr)->mBuffer))
+ //{
+ // remove from culled list
+ StreamingList::iterator tmp;
+ tmp = mStreamingCulledList.findImage((*itr)->mHandle);
+ AssertFatal(tmp, "alxStreamingUpdate: failed to find culled source");
+ mStreamingCulledList.erase_fast(tmp);
+
+ // remove from streaming list (and free)
+ tmp = mStreamingList.findImage((*itr)->mHandle);
+ if(tmp)
+ {
+ delete(*tmp);
+ mStreamingList.erase_fast(tmp);
+ }
+
+ continue;
+ //}
+ }
+
+ // remove from culled list
+ StreamingList::iterator tmp = mStreamingCulledList.findImage((*itr)->mHandle);
+ AssertFatal(tmp, "alxStreamingUpdate: failed to find culled source");
+ mStreamingCulledList.erase_fast(tmp);
+ alxSourcePlay(*itr);
+ // restore all state data
+ mHandle[index] = (*itr)->mHandle;
+ mScore[index] = (*itr)->mScore;
+ mSourceVolume[index] = (*itr)->mDescription.mVolume;
+ mType[index] = (*itr)->mDescription.mType;
+ mSampleEnvironment[index] = (*itr)->mEnvironment;
+
+ ALuint source = mSource[index];
+ (*itr)->mSource = mSource[index];
+
+ // setup play info
+ alGetError();
+
+ if(mEnvironmentEnabled)
+ alxSourceEnvironment(source, *itr);
+
+ alxPlay(mHandle[index]);
+ }
+ }
+}
+
+//--------------------------------------------------------------------------
+void alxCloseHandles()
+{
+ for(U32 i = 0; i < mNumSources; i++)
+ {
+ if(mHandle[i] & AUDIOHANDLE_LOADING_BIT)
+ continue;
+
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ continue;
+
+ ALint state = 0;
+ alGetSourcei(mSource[i], AL_SOURCE_STATE, &state);
+ if(state == AL_PLAYING)
+ continue;
+
+ if(!(mHandle[i] & AUDIOHANDLE_INACTIVE_BIT))
+ {
+ // should be playing? must have encounted an error.. remove
+ LoopingList::iterator itr = mLoopingList.findImage(mHandle[i]);
+ if(itr && !((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
+ {
+ AssertFatal(!mLoopingInactiveList.findImage((*itr)->mHandle), "alxCloseHandles: image incorrectly in inactive list");
+ AssertFatal(!mLoopingCulledList.findImage((*itr)->mHandle), "alxCloseHandles: image already in culled list");
+ mLoopingCulledList.push_back(*itr);
+ (*itr)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
+
+ mHandle[i] = NULL_AUDIOHANDLE;
+ mBuffer[i] = 0;
+ }
+
+ // should be playing? must have encounted an error.. remove
+// StreamingList::iterator itr2 = mStreamingList.findImage(mHandle[i]);
+// if(itr2 && !((*itr2)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
+// {
+// AssertFatal(!mStreamingInactiveList.findImage((*itr2)->mHandle), "alxCloseHandles: image incorrectly in inactive list");
+// AssertFatal(!mStreamingCulledList.findImage((*itr2)->mHandle), "alxCloseHandles: image already in culled list");
+// (*itr2)->freeStream();
+//
+// mStreamingCulledList.push_back(*itr2);
+// (*itr2)->mHandle |= AUDIOHANDLE_INACTIVE_BIT;
+//
+// mHandle[i] = NULL_AUDIOHANDLE;
+// mBuffer[i] = 0;
+// }
+ }
+
+ mHandle[i] = NULL_AUDIOHANDLE;
+ mBuffer[i] = 0;
+ }
+}
+
+//----------------------------------------------------------------------------------
+// - update the score for each audio source. this is used for culing sources.
+// normal ranges are between 0.f->1.f, voice/loading/music streams are scored
+// outside this range so that they will not be culled
+// - does not scale by attenuated volumes
+void alxUpdateScores(bool sourcesOnly)
+{
+ Point3F listener;
+ alGetListener3f(AL_POSITION, &listener.x, &listener.y, &listener.z);
+
+ // do the base sources
+ for(U32 i = 0; i < mNumSources; i++)
+ {
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ {
+ mScore[i] = 0.f;
+ continue;
+ }
+
+ // grab the volume.. (not attenuated by master for score)
+ F32 volume = mSourceVolume[i] * mAudioTypeVolume[mType[i]];
+
+ // 3d?
+ mScore[i] = volume;
+#ifdef REL_WORKAROUND
+ ALint val = AL_FALSE;
+ alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
+ if(val == AL_TRUE)
+#else
+ ALint val = AL_FALSE;
+ alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
+ if(val == AL_FALSE)
+#endif
+ {
+ // approximate 3d volume
+ Point3F pos;
+ alGetSourcefv(mSource[i], AL_POSITION, (ALfloat*)((F32*)pos) );
+
+ ALfloat min=0, max=1;
+ alGetSourcef(mSource[i], AL_REFERENCE_DISTANCE, &min);
+ alGetSourcef(mSource[i], AL_MAX_DISTANCE, &max);
+
+ pos -= listener;
+ F32 dist = pos.magnitudeSafe();
+
+ if(dist >= max)
+ mScore[i] = 0.f;
+ else if(dist > min)
+ mScore[i] *= (max-dist) / (max-min);
+ }
+ }
+
+ if(sourcesOnly)
+ return;
+
+ U32 updateTime = Platform::getRealMilliseconds();
+
+ // update the loopers
+ for(LoopingList::iterator itr = mLoopingList.begin(); itr != mLoopingList.end(); itr++)
+ {
+ if(!((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
+ continue;
+
+ if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
+ continue;
+
+ (*itr)->mScore = (*itr)->mDescription.mVolume;
+ if((*itr)->mDescription.mIs3D)
+ {
+ Point3F pos = (*itr)->mPosition - listener;
+ F32 dist = pos.magnitudeSafe();
+
+ F32 min = (*itr)->mDescription.mReferenceDistance;
+ F32 max = (*itr)->mDescription.mMaxDistance;
+
+ if(dist >= max)
+ (*itr)->mScore = 0.f;
+ else if(dist > min)
+ (*itr)->mScore *= (max-dist) / (max-min);
+ }
+
+ // attenuate by the channel gain
+ (*itr)->mScore *= mAudioTypeVolume[(*itr)->mDescription.mType];
+ }
+
+ // update the streamers
+ for(StreamingList::iterator itr = mStreamingList.begin(); itr != mStreamingList.end(); itr++)
+ {
+ if(!((*itr)->mHandle & AUDIOHANDLE_INACTIVE_BIT))
+ continue;
+
+ if((updateTime - (*itr)->mCullTime) < MIN_UNCULL_PERIOD)
+ continue;
+
+ (*itr)->mScore = (*itr)->mDescription.mVolume;
+ if((*itr)->mDescription.mIs3D)
+ {
+ Point3F pos = (*itr)->mPosition - listener;
+ F32 dist = pos.magnitudeSafe();
+
+ F32 min = (*itr)->mDescription.mReferenceDistance;
+ F32 max = (*itr)->mDescription.mMaxDistance;
+
+ if(dist >= max)
+ (*itr)->mScore = 0.f;
+ else if(dist > min)
+ (*itr)->mScore *= (max-dist) / (max-min);
+ }
+
+ // attenuate by the channel gain
+ (*itr)->mScore *= mAudioTypeVolume[(*itr)->mDescription.mType];
+ }
+}
+
+// the directx buffers are set to mute at max distance, but many of the providers seem to
+// ignore this flag... that is why this is here
+void alxUpdateMaxDistance()
+{
+ Point3F listener;
+ alGetListener3f(AL_POSITION, &listener.x, &listener.y, &listener.z);
+
+ for(U32 i = 0; i < mNumSources; i++)
+ {
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ continue;
+
+#ifdef REL_WORKAROUND
+ ALint val = AL_FALSE;
+ alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
+ if(val == AL_FALSE)
+#else
+ ALint val = AL_FALSE;
+ alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
+ if(val == AL_TRUE)
+#endif
+ continue;
+
+ Point3F pos;
+ alGetSourcefv(mSource[i], AL_POSITION, (F32*)pos);
+
+ F32 dist = 0.f;
+ alGetSourcef(mSource[i], AL_MAX_DISTANCE, &dist);
+
+ pos -= listener;
+ dist -= pos.len();
+
+ F32 gain = (dist < 0.f) ? 0.f : mSourceVolume[i] * mAudioTypeVolume[mType[i]] * mMasterVolume;
+ alSourcef(mSource[i], AL_GAIN, Audio::linearToDB(gain));
+
+ }
+}
+
+//--------------------------------------------------------------------------
+// Called to update alx system
+//--------------------------------------------------------------------------
+void alxUpdate()
+{
+ //if(mForceMaxDistanceUpdate)
+ alxUpdateMaxDistance();
+
+ alxCloseHandles();
+ alxUpdateScores(false);
+ alxLoopingUpdate();
+ alxStreamingUpdate();
+
+#ifdef TORQUE_GATHER_METRICS
+ alxGatherMetrics();
+#endif
+}
+
+//--------------------------------------------------------------------------
+// Misc
+//--------------------------------------------------------------------------
+// client-side function only
+ALuint alxGetWaveLen(ALuint buffer)
+{
+ if(buffer == AL_INVALID)
+ return(0);
+
+ ALint frequency = 0;
+ ALint bits = 0;
+ ALint channels = 0;
+ ALint size;
+
+ alGetBufferi(buffer, AL_FREQUENCY, &frequency);
+ alGetBufferi(buffer, AL_BITS, &bits);
+ alGetBufferi(buffer, AL_CHANNELS, &channels);
+ alGetBufferi(buffer, AL_SIZE, &size);
+
+ if(!frequency || !bits || !channels)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alxGetWaveLen: invalid buffer");
+ return(0);
+ }
+
+ F64 len = (F64(size) * 8000.f) / F64(frequency * bits * channels);
+ return(len);
+}
+
+
+//--------------------------------------------------------------------------
+// Environment:
+//--------------------------------------------------------------------------
+void alxEnvironmenti(ALenum pname, ALint value)
+{
+/* todo
+ alEnvironmentiIASIG(mEnvironment, pname, value);
+*/
+}
+
+void alxEnvironmentf(ALenum pname, ALfloat value)
+{
+/* todo
+ alEnvironmentfIASIG(mEnvironment, pname, value);
+*/
+}
+
+void alxGetEnvironmenti(ALenum pname, ALint * value)
+{
+/* todo
+ alGetEnvironmentiIASIG_EXT(mEnvironment, pname, value);
+*/
+}
+
+void alxGetEnvironmentf(ALenum pname, ALfloat * value)
+{
+/* todo
+ alGetEnvironmentfIASIG_EXT(mEnvironment, pname, value);
+*/
+}
+
+void alxEnableEnvironmental(bool enable)
+{
+ if(mEnvironmentEnabled == enable)
+ return;
+
+ // go through the playing sources and update their reverb mix
+ // - only 3d samples get environmental fx
+ // - only loopers can reenable fx
+ for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
+ {
+ if(mHandle[i] == NULL_AUDIOHANDLE)
+ continue;
+
+ ALint val = AL_FALSE;
+
+ // 3d?
+#ifdef REL_WORKAROUND
+ alGetSourcei(mSource[i], AL_SOURCE_ABSOLUTE, &val);
+ if(val == AL_FALSE)
+#else
+ alGetSourcei(mSource[i], AL_SOURCE_RELATIVE, &val);
+ if(val == AL_TRUE)
+#endif
+ continue;
+
+ // stopped?
+ val = AL_STOPPED;
+ alGetSourcei(mSource[i], AL_SOURCE_STATE, &val);
+
+ // only looping sources can reenable environmental effects (no description around
+ // for the non-loopers)
+ if(enable)
+ {
+ LoopingList::iterator itr = mLoopingList.findImage(mHandle[i]);
+ if(!itr)
+ continue;
+
+/* todo
+ alSourcef(mSource[i], AL_ENV_SAMPLE_REVERB_MIX_EXT, (*itr)->mDescription.mEnvironmentLevel);
+*/
+ }
+/* todo
+ else
+ alSourcef(mSource[i], AL_ENV_SAMPLE_REVERB_MIX_EXT, 0.f);
+*/
+ }
+
+ mEnvironmentEnabled = enable;
+}
+
+void alxSetEnvironment(const AudioEnvironment * env)
+{
+/* todo
+ mCurrentEnvironment = const_cast(env);
+
+ // reset environmental audio?
+ if(!env)
+ {
+ alxEnvironmenti(AL_ENV_ROOM_IASIG, AL_ENVIRONMENT_GENERIC);
+ return;
+ }
+
+ // room trashes all the values
+ if(env->mUseRoom)
+ {
+ alxEnvironmenti(AL_ENV_ROOM_IASIG, env->mRoom);
+ return;
+ }
+
+ // set all the params
+ alxEnvironmenti(AL_ENV_ROOM_HIGH_FREQUENCY_IASIG, env->mRoomHF);
+ alxEnvironmenti(AL_ENV_REFLECTIONS_IASIG, env->mReflections);
+ alxEnvironmenti(AL_ENV_REVERB_IASIG, env->mReverb);
+
+ alxEnvironmentf(AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG, env->mRoomRolloffFactor);
+ alxEnvironmentf(AL_ENV_DECAY_TIME_IASIG, env->mDecayTime);
+ alxEnvironmentf(AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG, env->mDecayTime);
+ alxEnvironmentf(AL_ENV_REFLECTIONS_DELAY_IASIG, env->mReflectionsDelay);
+ alxEnvironmentf(AL_ENV_REVERB_DELAY_IASIG, env->mReverbDelay);
+ alxEnvironmentf(AL_ENV_DENSITY_IASIG, env->mAirAbsorption);
+ alxEnvironmentf(AL_ENV_DIFFUSION_IASIG, env->mEnvironmentDiffusion);
+
+ // ext:
+ alxEnvironmenti(AL_ENV_ROOM_VOLUME_EXT, env->mRoomVolume);
+ alxEnvironmenti(AL_ENV_FLAGS_EXT, env->mFlags);
+
+ alxEnvironmentf(AL_ENV_EFFECT_VOLUME_EXT, env->mEffectVolume);
+ alxEnvironmentf(AL_ENV_DAMPING_EXT, env->mDamping);
+ alxEnvironmentf(AL_ENV_ENVIRONMENT_SIZE_EXT, env->mEnvironmentSize);
+*/
+}
+
+const AudioEnvironment * alxGetEnvironment()
+{
+ return(mCurrentEnvironment);
+}
+
+F32 alxGetStreamPosition( AUDIOHANDLE handle )
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if( !itr )
+ return -1.f;
+
+ return (*itr)->getElapsedTime();
+}
+
+F32 alxGetStreamDuration( AUDIOHANDLE handle )
+{
+ StreamingList::iterator itr = mStreamingList.findImage(handle);
+ if( !itr )
+ return -1.f;
+
+ return (*itr)->getTotalTime();
+}
+
+// Namespace: Audio ---------------------------------------------------------
+namespace Audio
+{
+
+//---------------------------------------------------------------------------
+// the following db<->linear conversion functions come from Loki openAL linux driver
+// code, here more for completeness than anything else (all current audio code
+// uses AL_GAIN_LINEAR)... in Audio:: so that looping updates and audio channel updates
+// can convert gain types and to give the miles driver access
+static const F32 logtab[] = {
+ 0.00, 0.001, 0.002, 0.003, 0.004,
+ 0.005, 0.01, 0.011, 0.012, 0.013,
+ 0.014, 0.015, 0.016, 0.02, 0.021,
+ 0.022, 0.023, 0.024, 0.025, 0.03,
+ 0.031, 0.032, 0.033, 0.034, 0.04,
+ 0.041, 0.042, 0.043, 0.044, 0.05,
+ 0.051, 0.052, 0.053, 0.054, 0.06,
+ 0.061, 0.062, 0.063, 0.064, 0.07,
+ 0.071, 0.072, 0.073, 0.08, 0.081,
+ 0.082, 0.083, 0.084, 0.09, 0.091,
+ 0.092, 0.093, 0.094, 0.10, 0.101,
+ 0.102, 0.103, 0.11, 0.111, 0.112,
+ 0.113, 0.12, 0.121, 0.122, 0.123,
+ 0.124, 0.13, 0.131, 0.132, 0.14,
+ 0.141, 0.142, 0.143, 0.15, 0.151,
+ 0.152, 0.16, 0.161, 0.162, 0.17,
+ 0.171, 0.172, 0.18, 0.181, 0.19,
+ 0.191, 0.192, 0.20, 0.201, 0.21,
+ 0.211, 0.22, 0.221, 0.23, 0.231,
+ 0.24, 0.25, 0.251, 0.26, 0.27,
+ 0.271, 0.28, 0.29, 0.30, 0.301,
+ 0.31, 0.32, 0.33, 0.34, 0.35,
+ 0.36, 0.37, 0.38, 0.39, 0.40,
+ 0.41, 0.43, 0.50, 0.60, 0.65,
+ 0.70, 0.75, 0.80, 0.85, 0.90,
+ 0.95, 0.97, 0.99 };
+const int logmax = sizeof logtab / sizeof *logtab;
+
+F32 DBToLinear(F32 value)
+{
+ if(value <= 0.f)
+ return(0.f);
+ if(value >= 1.f)
+ return(1.f);
+
+ S32 max = logmax;
+ S32 min = 0;
+ S32 mid;
+ S32 last = -1;
+
+ mid = (max - min) / 2;
+ do {
+ last = mid;
+
+ if(logtab[mid] == value)
+ break;
+
+ if(logtab[mid] < value)
+ min = mid;
+ else
+ max = mid;
+
+ mid = min + ((max - min) / 2);
+ } while(last != mid);
+
+ return((F32)mid / logmax);
+}
+
+F32 linearToDB(F32 value)
+{
+ if(value <= 0.f)
+ return(0.f);
+ if(value >= 1.f)
+ return(1.f);
+ return(logtab[(U32)(logmax * value)]);
+}
+//---------------------------------------------------------------------------
+
+static ALvoid errorCallback(ALbyte *msg)
+{
+ // used to allow our OpenAL implementation to display info on the console
+ Con::errorf(ConsoleLogEntry::General, (const char *)msg);
+}
+
+//--------------------------------------------------------------------------
+bool prepareContext()
+{
+// mForceMaxDistanceUpdate = Con::getBoolVariable("$pref::audio::forceMaxDistanceUpdate", false);
+ mForceMaxDistanceUpdate = false;
+
+ // allocate source channels: can only get 16 sources on NT, so check the max before
+ // jff: todo, allow for more than 16 channels on non-NT boxes
+ mNumSources = mRequestSources;
+ alGenSources(mRequestSources, mSource);
+
+ // invalidate all existing handles
+ dMemset(mHandle, NULL_AUDIOHANDLE, sizeof(mHandle));
+
+ // pre-load profile data
+ SimGroup* grp = Sim::getDataBlockGroup();
+ if (grp != NULL)
+ {
+ SimObjectList::iterator itr = grp->begin();
+ for (; itr != grp->end(); itr++)
+ {
+ AudioProfile *profile = dynamic_cast(*itr);
+ if(profile != NULL && profile->isPreload())
+ {
+ Resource buffer = AudioBuffer::find(profile->mFilename);
+ if((bool)buffer)
+ ALuint bufferId = buffer->getALBuffer();
+ }
+ }
+ }
+ return(true);
+}
+
+void shutdownContext()
+{
+ // invalidate active handles
+ dMemset(mSource, 0, sizeof(mSource));
+}
+
+
+//--------------------------------------------------------------------------
+bool OpenALInit()
+{
+ OpenALShutdown();
+
+ if(!OpenALDLLInit())
+ return false;
+
+ // Open a device
+#ifdef TORQUE_OS_LINUX
+ const char* deviceSpecifier =
+ Con::getVariable("Pref::Unix::OpenALSpecifier");
+ if (dStrlen(deviceSpecifier) == 0)
+ // use SDL for audio output by default
+ deviceSpecifier = "'((devices '(sdl)))";
+ mDevice = (ALCdevice *)alcOpenDevice((ALubyte*)deviceSpecifier);
+#else
+ mDevice = (ALCdevice *)alcOpenDevice((ALubyte*)NULL);
+#endif
+ if (mDevice == (ALCdevice *)NULL)
+ return false;
+
+ // Create an openAL context
+#ifdef TORQUE_OS_LINUX
+ int freq = Con::getIntVariable("Pref::Unix::OpenALFrequency");
+ if (freq == 0)
+ freq = 22050;
+
+ Con::printf(" Setting OpenAL output frequency to %d", freq);
+ // some versions of openal have bugs converting between 22050 and 44100
+ // samples when the lib is in 44100 mode.
+ int attrlist[] = {
+ // this 0x100 is "ALC_FREQUENCY" in the linux openal implementation.
+ // it doesn't match the value of the creative headers, so we can't use
+ // that define. seems like linux torque really shouldn't be using the
+ // creative headers.
+ 0x100, freq,
+ 0
+ };
+ mContext = alcCreateContext(mDevice,attrlist);
+#else
+ mContext = alcCreateContext(mDevice,NULL);
+#endif
+ if (mContext == NULL)
+ return false;
+
+ // Make this context the active context
+ alcMakeContextCurrent(mContext);
+
+ mNumSources = mRequestSources;
+ alGenSources(mRequestSources, mSource);
+
+ // invalidate all existing handles
+ dMemset(mHandle, NULL_AUDIOHANDLE, sizeof(mHandle));
+
+ // default all channels to full gain
+ for(U32 i = 0; i < Audio::NumAudioTypes; i++)
+ mAudioTypeVolume[i] = 1.f;
+
+ // Clear Error Code
+ alGetError();
+
+ // Similiar to DSound Model w/o min distance clamping
+ alEnable(AL_DISTANCE_MODEL);
+ alDistanceModel(AL_INVERSE_DISTANCE);
+ alListenerf(AL_GAIN_LINEAR, 1.f);
+
+ return true;
+}
+
+//--------------------------------------------------------------------------
+void OpenALShutdown()
+{
+ alxStopAll();
+
+ //if(mInitialized)
+ {
+ alxEnvironmentDestroy();
+ }
+
+ while(mLoopingList.size())
+ {
+ mLoopingList.last()->mBuffer.purge();
+ delete mLoopingList.last();
+ mLoopingList.pop_back();
+ }
+
+ while(mLoopingFreeList.size())
+ {
+ mLoopingFreeList.last()->mBuffer.purge();
+ delete mLoopingFreeList.last();
+ mLoopingFreeList.pop_back();
+ }
+
+ for(U32 i = 0; i < MAX_AUDIOSOURCES; i++)
+ mBuffer[i] = 0;
+
+ alDeleteSources(mNumSources, mSource);
+
+ if (mContext)
+ {
+ alcDestroyContext(mContext);
+ mContext = NULL;
+ }
+ if (mDevice)
+ {
+ alcCloseDevice(mDevice);
+ mDevice = NULL;
+ }
+
+ OpenALDLLShutdown();
+}
+
+
+} // end OpenAL namespace
+
+AudioStreamSource* alxFindAudioStreamSource(AUDIOHANDLE handle)
+{
+ StreamingList::iterator itr2 = mStreamingList.findImage(handle);
+ if(itr2)
+ return *itr2;
+ return NULL;
+}
diff --git a/engine/audio/audio.h b/engine/audio/audio.h
new file mode 100755
index 0000000..a9b2031
--- /dev/null
+++ b/engine/audio/audio.h
@@ -0,0 +1,16 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AUDIO_H_
+#define _AUDIO_H_
+
+#ifndef _PLATFORMAUDIO_H_
+#include "platform/platformAudio.h"
+#endif
+#ifndef _AUDIODATABLOCK_H_
+#include "audio/audioDataBlock.h"
+#endif
+
+#endif // _H_AUDIO_
diff --git a/engine/audio/audioBuffer.cc b/engine/audio/audioBuffer.cc
new file mode 100755
index 0000000..07e8f8f
--- /dev/null
+++ b/engine/audio/audioBuffer.cc
@@ -0,0 +1,428 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platformAL.h"
+#include "audio/audioBuffer.h"
+#include "core/stream.h"
+#include "console/console.h"
+#include "core/frameAllocator.h"
+
+#ifndef TORQUE_NO_OGGVORBIS
+#include "vorbis/codec.h"
+#endif
+
+#if defined(TORQUE_BIG_ENDIAN)
+ #define ENDIAN 1
+#else
+ #define ENDIAN 0
+#endif
+
+//#define LOG_SOUND_LOADS
+
+/// WAV File-header
+struct WAVFileHdr
+{
+ ALubyte id[4];
+ ALsizei size;
+ ALubyte type[4];
+};
+
+//// WAV Fmt-header
+struct WAVFmtHdr
+{
+ ALushort format;
+ ALushort channels;
+ ALuint samplesPerSec;
+ ALuint bytesPerSec;
+ ALushort blockAlign;
+ ALushort bitsPerSample;
+};
+
+/// WAV FmtEx-header
+struct WAVFmtExHdr
+{
+ ALushort size;
+ ALushort samplesPerBlock;
+};
+
+/// WAV Smpl-header
+struct WAVSmplHdr
+{
+ ALuint manufacturer;
+ ALuint product;
+ ALuint samplePeriod;
+ ALuint note;
+ ALuint fineTune;
+ ALuint SMPTEFormat;
+ ALuint SMPTEOffest;
+ ALuint loops;
+ ALuint samplerData;
+ struct
+ {
+ ALuint identifier;
+ ALuint type;
+ ALuint start;
+ ALuint end;
+ ALuint fraction;
+ ALuint count;
+ } loop[1];
+};
+
+/// WAV Chunk-header
+struct WAVChunkHdr
+{
+ ALubyte id[4];
+ ALuint size;
+};
+
+#define CHUNKSIZE 4096
+
+
+
+//--------------------------------------
+AudioBuffer::AudioBuffer(StringTableEntry filename)
+{
+ AssertFatal(StringTable->lookup(filename), "AudioBuffer:: filename is not a string table entry");
+
+ mFilename = filename;
+ mLoading = false;
+ malBuffer = 0;
+}
+
+AudioBuffer::~AudioBuffer()
+{
+ if( malBuffer != 0 ) {
+ alDeleteBuffers( 1, &malBuffer );
+ }
+}
+
+//--------------------------------------
+Resource AudioBuffer::find(const char *filename)
+{
+ U32 mark = FrameAllocator::getWaterMark();
+ char * f2 = NULL;
+
+ Resource buffer = ResourceManager->load(filename);
+ if (bool(buffer) == false)
+ {
+ // wav file doesn't exist, try ogg file instead
+ S32 len = dStrlen(filename);
+ if (len>3 && !dStricmp(filename+len-4,".wav"))
+ {
+ f2 = (char*)FrameAllocator::alloc(len+1);
+ dStrcpy(f2,filename);
+ f2[len-3] = 'o';
+ f2[len-2] = 'g';
+ f2[len-1] = 'g';
+ buffer = ResourceManager->load(filename);
+ }
+ }
+
+ // if resource still not there, try to create it if file exists
+ if (bool(buffer) == false)
+ {
+ // see if the file exists -- first try default, then try ogg
+ if (ResourceManager->getPathOf(filename))
+ {
+ AudioBuffer *temp = new AudioBuffer(StringTable->insert(filename));
+ ResourceManager->add(filename, temp);
+ buffer = ResourceManager->load(filename);
+ }
+ else if (f2 && ResourceManager->getPathOf(f2))
+ {
+ AudioBuffer *temp = new AudioBuffer(StringTable->insert(f2));
+ ResourceManager->add(f2, temp);
+ buffer = ResourceManager->load(f2);
+ }
+ }
+
+ FrameAllocator::setWaterMark(mark);
+ return buffer;
+}
+
+ResourceInstance* AudioBuffer::construct(Stream &)
+{
+ return NULL;
+}
+
+//-----------------------------------------------------------------
+ALuint AudioBuffer::getALBuffer()
+{
+ if (!alcGetCurrentContext())
+ return 0;
+
+ // clear the error state
+ alGetError();
+
+ // Intangir> fix for newest openAL from creative (it returns true, yea right 0 is not a valid buffer)
+ // it MIGHT not work at all for all i know.
+ if (malBuffer && alIsBuffer(malBuffer))
+ return malBuffer;
+
+ alGenBuffers(1, &malBuffer);
+ if(alGetError() != AL_NO_ERROR)
+ return 0;
+
+ ResourceObject * obj = ResourceManager->find(mFilename);
+ if(obj)
+ {
+ bool readSuccess = false;
+ S32 len = dStrlen(mFilename);
+
+ if(len > 3 && !dStricmp(mFilename + len - 4, ".wav"))
+ {
+#ifdef LOG_SOUND_LOADS
+ Con::printf("Reading WAV: %s\n", mFilename);
+#endif
+ readSuccess = readWAV(obj);
+ }
+#ifndef TORQUE_NO_OGGVORBIS
+ else if(len > 3 && !dStricmp(mFilename + len - 4, ".ogg"))
+ {
+# ifdef LOG_SOUND_LOADS
+ Con::printf("Reading Ogg: %s\n", mFilename);
+# endif
+ readSuccess = readOgg(obj);
+ }
+#endif
+ if(readSuccess)
+ return(malBuffer);
+ }
+
+ alDeleteBuffers(1, &malBuffer);
+ return 0;
+}
+
+/*! The Read a WAV file from the given ResourceObject and initialize
+ an alBuffer with it.
+*/
+bool AudioBuffer::readWAV(ResourceObject *obj)
+{
+ WAVChunkHdr chunkHdr;
+ WAVFmtExHdr fmtExHdr;
+ WAVFileHdr fileHdr;
+ WAVSmplHdr smplHdr;
+ WAVFmtHdr fmtHdr;
+
+ ALenum format = AL_FORMAT_MONO16;
+ char *data = NULL;
+ ALsizei size = 0;
+ ALsizei freq = 22050;
+ ALboolean loop = AL_FALSE;
+
+ Stream *stream = ResourceManager->openStream(obj);
+ if (!stream)
+ return false;
+
+ stream->read(4, &fileHdr.id[0]);
+ stream->read(&fileHdr.size);
+ stream->read(4, &fileHdr.type[0]);
+
+ fileHdr.size=((fileHdr.size+1)&~1)-4;
+
+ stream->read(4, &chunkHdr.id[0]);
+ stream->read(&chunkHdr.size);
+ // unread chunk data rounded up to nearest WORD
+ S32 chunkRemaining = chunkHdr.size + (chunkHdr.size&1);
+
+ while ((fileHdr.size!=0) && (stream->getStatus() != Stream::EOS))
+ {
+ // WAV Format header
+ if (!dStrncmp((const char*)chunkHdr.id,"fmt ",4))
+ {
+ stream->read(&fmtHdr.format);
+ stream->read(&fmtHdr.channels);
+ stream->read(&fmtHdr.samplesPerSec);
+ stream->read(&fmtHdr.bytesPerSec);
+ stream->read(&fmtHdr.blockAlign);
+ stream->read(&fmtHdr.bitsPerSample);
+
+ if (fmtHdr.format==0x0001)
+ {
+ format=(fmtHdr.channels==1?
+ (fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
+ (fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
+ freq=fmtHdr.samplesPerSec;
+ chunkRemaining -= sizeof(WAVFmtHdr);
+ }
+ else
+ {
+ stream->read(sizeof(WAVFmtExHdr), &fmtExHdr);
+ chunkRemaining -= sizeof(WAVFmtExHdr);
+ }
+ }
+ // WAV Format header
+ else if (!dStrncmp((const char*)chunkHdr.id,"data",4))
+ {
+ if (fmtHdr.format==0x0001)
+ {
+ size=chunkHdr.size;
+ data=new char[chunkHdr.size];
+ if (data)
+ {
+ stream->read(chunkHdr.size, data);
+#if defined(TORQUE_BIG_ENDIAN)
+ // need to endian-flip the 16-bit data.
+ if (fmtHdr.bitsPerSample==16) // !!!TBD we don't handle stereo, so may be RL flipped.
+ {
+ U16 *ds = (U16*)data;
+ U16 *de = (U16*)(data+size);
+ while (dsread(sizeof(WAVSmplHdr), &smplHdr);
+ loop = (smplHdr.loops ? AL_TRUE : AL_FALSE);
+ chunkRemaining -= sizeof(WAVSmplHdr);
+ }
+
+ // either we have unread chunk data or we found an unknown chunk type
+ // loop and read up to 1K bytes at a time until we have
+ // read to the end of this chunk
+ char buffer[1024];
+ AssertFatal(chunkRemaining >= 0, "AudioBuffer::readWAV: remaining chunk data should never be less than zero.");
+ while (chunkRemaining > 0)
+ {
+ S32 readSize = getMin(1024, chunkRemaining);
+ stream->read(readSize, buffer);
+ chunkRemaining -= readSize;
+ }
+
+ fileHdr.size-=(((chunkHdr.size+1)&~1)+8);
+
+ // read next chunk header...
+ stream->read(4, &chunkHdr.id[0]);
+ stream->read(&chunkHdr.size);
+ // unread chunk data rounded up to nearest WORD
+ chunkRemaining = chunkHdr.size + (chunkHdr.size&1);
+ }
+
+ ResourceManager->closeStream(stream);
+ if (data)
+ {
+ alBufferData(malBuffer, format, data, size, freq);
+ delete [] data;
+ return (alGetError() == AL_NO_ERROR);
+ }
+
+ return false;
+}
+
+#ifndef TORQUE_NO_OGGVORBIS
+/*! The Read an Ogg Vorbis file from the given ResourceObject and initialize
+ an alBuffer with it.
+*/
+bool AudioBuffer::readOgg(ResourceObject *obj)
+{
+ OggVorbisFile vf;
+ vorbis_info *vi;
+
+ ALenum format = AL_FORMAT_MONO16;
+ char *data = NULL;
+ ALsizei size = 0;
+ ALsizei freq = 22050;
+ ALboolean loop = AL_FALSE;
+ int current_section = 0;
+
+#if defined(TORQUE_BIG_ENDIAN)
+ int endian = 1;
+#else
+ int endian = 0;
+#endif
+
+ int eof = 0;
+
+ Stream *stream = ResourceManager->openStream(obj);
+ if (!stream)
+ return false;
+
+ if(vf.ov_open(stream, NULL, 0) < 0) {
+ return false;
+ }
+
+ //Read Vorbis File Info
+ vi = vf.ov_info(-1);
+ freq = vi->rate;
+
+ long samples = (long)vf.ov_pcm_total(-1);
+
+ if(vi->channels == 1) {
+ format = AL_FORMAT_MONO16;
+ size = 2 * samples;
+ } else {
+ format = AL_FORMAT_STEREO16;
+ size = 4 * samples;
+ }
+
+
+ data=new char[size];
+
+ if (data)
+ {
+ long ret = oggRead(&vf, data, size, endian, ¤t_section);
+ }
+
+ /* cleanup */
+ vf.ov_clear();
+
+ ResourceManager->closeStream(stream);
+ if (data)
+ {
+ alBufferData(malBuffer, format, data, size, freq);
+ delete [] data;
+ return (alGetError() == AL_NO_ERROR);
+ }
+
+ return false;
+}
+
+// ov_read() only returns a maximum of one page worth of data
+// this helper function will repeat the read until buffer is full
+long AudioBuffer::oggRead(OggVorbisFile* vf, char *buffer,int length,
+ int bigendianp,int *bitstream)
+{
+ long bytesRead = 0;
+ long totalBytes = 0;
+ long offset = 0;
+ long bytesToRead = 0;
+ //while((offset + CHUNKSIZE) < length) {
+ while((offset) < length)
+ {
+ if((length - offset) < CHUNKSIZE)
+ bytesToRead = length - offset;
+ else
+ bytesToRead = CHUNKSIZE;
+
+ bytesRead = vf->ov_read(buffer, bytesToRead, bigendianp, bitstream);
+ if(bytesRead <= 0)
+ break;
+ offset += bytesRead;
+ buffer += bytesRead;
+ }
+ return offset;
+}
+#endif
diff --git a/engine/audio/audioBuffer.h b/engine/audio/audioBuffer.h
new file mode 100755
index 0000000..33dadad
--- /dev/null
+++ b/engine/audio/audioBuffer.h
@@ -0,0 +1,56 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AUDIOBUFFER_H_
+#define _AUDIOBUFFER_H_
+
+#ifndef _PLATFORM_H_
+#include "platform/platform.h"
+#endif
+#ifndef _PLATFORMAL_H_
+#include "platform/platformAL.h"
+#endif
+#ifndef _RESMANAGER_H_
+#include "core/resManager.h"
+#endif
+
+// MLH - don't need oggbvorbis in tools
+#ifndef TORQUE_NO_OGGVORBIS
+#include "audio/vorbisStream.h"
+#endif
+
+//--------------------------------------------------------------------------
+
+class AudioBuffer: public ResourceInstance
+{
+ friend class AudioThread;
+
+private:
+ StringTableEntry mFilename;
+ bool mLoading;
+ ALuint malBuffer;
+
+ bool readRIFFchunk(Stream &s, const char *seekLabel, U32 *size);
+ bool readWAV(ResourceObject *obj);
+
+#ifndef TORQUE_NO_OGGVORBIS
+ bool readOgg(ResourceObject *obj);
+ long oggRead(OggVorbisFile* vf, char *buffer,int length,
+ int bigendianp,int *bitstream);
+#endif
+
+public:
+ AudioBuffer(StringTableEntry filename);
+ ~AudioBuffer();
+ ALuint getALBuffer();
+ bool isLoading() {return(mLoading);}
+
+ static Resource find(const char *filename);
+ static ResourceInstance* construct(Stream& stream);
+
+};
+
+
+#endif // _H_AUDIOBUFFER_
diff --git a/engine/audio/audioDataBlock.cc b/engine/audio/audioDataBlock.cc
new file mode 100755
index 0000000..1e3a330
--- /dev/null
+++ b/engine/audio/audioDataBlock.cc
@@ -0,0 +1,526 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "audio/audioDataBlock.h"
+#include "console/consoleTypes.h"
+#include "platform/platformAL.h"
+#include "sim/netConnection.h"
+
+//--------------------------------------------------------------------------
+namespace
+{
+ void writeRangedF32(BitStream * bstream, F32 val, F32 min, F32 max, U32 numBits)
+ {
+ val = (mClampF(val, min, max) - min) / (max - min);
+ bstream->writeInt(val * ((1 << numBits) - 1), numBits);
+ }
+
+ F32 readRangedF32(BitStream * bstream, F32 min, F32 max, U32 numBits)
+ {
+ return(min + (F32(bstream->readInt(numBits)) / F32((1 << numBits) - 1)) * (max - min));
+ }
+
+ void writeRangedS32(BitStream * bstream, S32 val, S32 min, S32 max)
+ {
+ bstream->writeRangedU32((val - min), 0, (max - min));
+ }
+
+ S32 readRangedS32(BitStream * bstream, S32 min, S32 max)
+ {
+ return(bstream->readRangedU32(0, (max - min)) + min);
+ }
+}
+
+//--------------------------------------------------------------------------
+// Class AudioEnvironment:
+//--------------------------------------------------------------------------
+IMPLEMENT_CO_DATABLOCK_V1(AudioEnvironment);
+
+AudioEnvironment::AudioEnvironment()
+{
+ mUseRoom = true;
+ mRoom = EAX_ENVIRONMENT_GENERIC;
+ mRoomHF = 0;
+ mReflections = 0;
+ mReverb = 0;
+ mRoomRolloffFactor = 0.1f;
+ mDecayTime = 0.1f;
+ mDecayHFRatio = 0.1f;
+ mReflectionsDelay = 0.f;
+ mReverbDelay = 0.f;
+ mRoomVolume = 0;
+ mEffectVolume = 0.f;
+ mDamping = 0.f;
+ mEnvironmentSize = 10.f;
+ mEnvironmentDiffusion = 1.f;
+ mAirAbsorption = 0.f;
+ mFlags = 0;
+}
+
+static EnumTable::Enums roomEnums[] =
+{
+ { EAX_ENVIRONMENT_GENERIC, "GENERIC" }, // 0
+ { EAX_ENVIRONMENT_PADDEDCELL, "PADDEDCELL" },
+ { EAX_ENVIRONMENT_ROOM, "ROOM" },
+ { EAX_ENVIRONMENT_BATHROOM, "BATHROOM" },
+ { EAX_ENVIRONMENT_LIVINGROOM, "LIVINGROOM" },
+ { EAX_ENVIRONMENT_STONEROOM, "STONEROOM" }, // 5
+ { EAX_ENVIRONMENT_AUDITORIUM, "AUDITORIUM" },
+ { EAX_ENVIRONMENT_CONCERTHALL, "CONCERTHALL" },
+ { EAX_ENVIRONMENT_CAVE, "CAVE" },
+ { EAX_ENVIRONMENT_ARENA, "ARENA" },
+ { EAX_ENVIRONMENT_HANGAR, "HANGAR" }, // 10
+ { EAX_ENVIRONMENT_CARPETEDHALLWAY, "CARPETEDHALLWAY" },
+ { EAX_ENVIRONMENT_HALLWAY, "HALLWAY" },
+ { EAX_ENVIRONMENT_STONECORRIDOR, "STONECORRIDOR" },
+ { EAX_ENVIRONMENT_ALLEY, "ALLEY" },
+ { EAX_ENVIRONMENT_FOREST, "FOREST" }, // 15
+ { EAX_ENVIRONMENT_CITY, "CITY" },
+ { EAX_ENVIRONMENT_MOUNTAINS, "MOUNTAINS" },
+ { EAX_ENVIRONMENT_QUARRY, "QUARRY" },
+ { EAX_ENVIRONMENT_PLAIN, "PLAIN" },
+ { EAX_ENVIRONMENT_PARKINGLOT, "PARKINGLOT" }, // 20
+ { EAX_ENVIRONMENT_SEWERPIPE, "SEWERPIPE" },
+ { EAX_ENVIRONMENT_UNDERWATER, "UNDERWATER" },
+ { EAX_ENVIRONMENT_DRUGGED, "DRUGGED" },
+ { EAX_ENVIRONMENT_DIZZY, "DIZZY" },
+ { EAX_ENVIRONMENT_PSYCHOTIC, "PSYCHOTIC" } // 25
+};
+static EnumTable gAudioEnvironmentRoomTypes(sizeof(roomEnums) / sizeof(roomEnums[0]), &roomEnums[0]);
+
+
+//--------------------------------------------------------------------------
+IMPLEMENT_CONSOLETYPE(AudioEnvironment)
+IMPLEMENT_GETDATATYPE(AudioEnvironment)
+IMPLEMENT_SETDATATYPE(AudioEnvironment)
+
+void AudioEnvironment::initPersistFields()
+{
+ Parent::initPersistFields();
+
+ addField("useRoom", TypeBool, Offset(mUseRoom, AudioEnvironment));
+ addField("room", TypeEnum, Offset(mRoom, AudioEnvironment), 1, &gAudioEnvironmentRoomTypes);
+ addField("roomHF", TypeS32, Offset(mRoomHF, AudioEnvironment));
+ addField("reflections", TypeS32, Offset(mReflections, AudioEnvironment));
+ addField("reverb", TypeS32, Offset(mReverb, AudioEnvironment));
+ addField("roomRolloffFactor", TypeF32, Offset(mRoomRolloffFactor, AudioEnvironment));
+ addField("decayTime", TypeF32, Offset(mDecayTime, AudioEnvironment));
+ addField("decayHFRatio", TypeF32, Offset(mDecayHFRatio, AudioEnvironment));
+ addField("reflectionsDelay", TypeF32, Offset(mReflectionsDelay, AudioEnvironment));
+ addField("reverbDelay", TypeF32, Offset(mReverbDelay, AudioEnvironment));
+ addField("roomVolume", TypeS32, Offset(mRoomVolume, AudioEnvironment));
+ addField("effectVolume", TypeF32, Offset(mEffectVolume, AudioEnvironment));
+ addField("damping", TypeF32, Offset(mDamping, AudioEnvironment));
+ addField("environmentSize", TypeF32, Offset(mEnvironmentSize, AudioEnvironment));
+ addField("environmentDiffusion", TypeF32, Offset(mEnvironmentDiffusion, AudioEnvironment));
+ addField("airAbsorption", TypeF32, Offset(mAirAbsorption, AudioEnvironment));
+ addField("flags", TypeS32, Offset(mFlags, AudioEnvironment));
+}
+
+
+void AudioEnvironment::packData(BitStream* stream)
+{
+ Parent::packData(stream);
+ if(stream->writeFlag(mUseRoom))
+ stream->writeRangedU32(mRoom, EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_COUNT);
+ else
+ {
+ writeRangedS32(stream, mRoomHF, -10000, 0);
+ writeRangedS32(stream, mReflections, -10000, 10000);
+ writeRangedS32(stream, mReverb, -10000, 2000);
+ writeRangedF32(stream, mRoomRolloffFactor, 0.1f, 10.f, 8);
+ writeRangedF32(stream, mDecayTime, 0.1f, 20.f, 8);
+ writeRangedF32(stream, mDecayHFRatio, 0.1f, 20.f, 8);
+ writeRangedF32(stream, mReflectionsDelay, 0.f, 0.3f, 9);
+ writeRangedF32(stream, mReverbDelay, 0.f, 0.1f, 7);
+ writeRangedS32(stream, mRoomVolume, -10000, 0);
+ writeRangedF32(stream, mEffectVolume, 0.f, 1.f, 8);
+ writeRangedF32(stream, mDamping, 0.f, 2.f, 9);
+ writeRangedF32(stream, mEnvironmentSize, 1.f, 100.f, 10);
+ writeRangedF32(stream, mEnvironmentDiffusion, 0.f, 1.f, 8);
+ writeRangedF32(stream, mAirAbsorption, -100.f, 0.f, 10);
+ stream->writeInt(mFlags, 6);
+ }
+}
+
+void AudioEnvironment::unpackData(BitStream* stream)
+{
+ Parent::unpackData(stream);
+ mUseRoom = stream->readFlag();
+ if(mUseRoom)
+ mRoom = stream->readRangedU32(EAX_ENVIRONMENT_GENERIC, EAX_ENVIRONMENT_COUNT);
+ else
+ {
+ mRoomHF = readRangedS32(stream, -10000, 0);
+ mReflections = readRangedS32(stream, -10000, 10000);
+ mReverb = readRangedS32(stream, -10000, 2000);
+ mRoomRolloffFactor = readRangedF32(stream, 0.1f, 10.f, 8);
+ mDecayTime = readRangedF32(stream, 0.1f, 20.f, 8);
+ mDecayHFRatio = readRangedF32(stream, 0.1f, 20.f, 8);
+ mReflectionsDelay = readRangedF32(stream, 0.f, 0.3f, 9);
+ mReverbDelay = readRangedF32(stream, 0.f, 0.1f, 7);
+ mRoomVolume = readRangedS32(stream, -10000, 0);
+ mEffectVolume = readRangedF32(stream, 0.f, 1.f, 8);
+ mDamping = readRangedF32(stream, 0.f, 2.f, 9);
+ mEnvironmentSize = readRangedF32(stream, 1.f, 100.f, 10);
+ mEnvironmentDiffusion = readRangedF32(stream, 0.f, 1.f, 8);
+ mAirAbsorption = readRangedF32(stream, -100.f, 0.f, 10);
+ mFlags = stream->readInt(6);
+ }
+}
+
+//--------------------------------------------------------------------------
+// Class AudioEnvironmentProfile:
+//--------------------------------------------------------------------------
+IMPLEMENT_CO_DATABLOCK_V1(AudioSampleEnvironment);
+
+AudioSampleEnvironment::AudioSampleEnvironment()
+{
+ mDirect = 0;
+ mDirectHF = 0;
+ mRoom = 0;
+ mRoomHF = 0;
+ mObstruction = 0.f;
+ mObstructionLFRatio = 0.f;
+ mOcclusion = 0.f;
+ mOcclusionLFRatio = 0.f;
+ mOcclusionRoomRatio = 0.f;
+ mRoomRolloff = 0.f;
+ mAirAbsorption = 0.f;
+ mOutsideVolumeHF = 0.f;
+ mFlags = 0;
+}
+
+//--------------------------------------------------------------------------
+IMPLEMENT_CONSOLETYPE(AudioSampleEnvironment)
+IMPLEMENT_GETDATATYPE(AudioSampleEnvironment)
+IMPLEMENT_SETDATATYPE(AudioSampleEnvironment)
+
+void AudioSampleEnvironment::initPersistFields()
+{
+ Parent::initPersistFields();
+
+ addField("direct", TypeS32, Offset(mDirect, AudioSampleEnvironment));
+ addField("directHF", TypeS32, Offset(mDirectHF, AudioSampleEnvironment));
+ addField("room", TypeS32, Offset(mRoom, AudioSampleEnvironment));
+ addField("obstruction", TypeF32, Offset(mObstruction, AudioSampleEnvironment));
+ addField("obstructionLFRatio", TypeF32, Offset(mObstructionLFRatio, AudioSampleEnvironment));
+ addField("occlusion", TypeF32, Offset(mOcclusion, AudioSampleEnvironment));
+ addField("occlusionLFRatio", TypeF32, Offset(mOcclusionLFRatio, AudioSampleEnvironment));
+ addField("occlusionRoomRatio", TypeF32, Offset(mOcclusionRoomRatio, AudioSampleEnvironment));
+ addField("roomRolloff", TypeF32, Offset(mRoomRolloff, AudioSampleEnvironment));
+ addField("airAbsorption", TypeF32, Offset(mAirAbsorption, AudioSampleEnvironment));
+ addField("outsideVolumeHF", TypeS32, Offset(mOutsideVolumeHF, AudioSampleEnvironment));
+ addField("flags", TypeS32, Offset(mFlags, AudioSampleEnvironment));
+}
+
+void AudioSampleEnvironment::packData(BitStream* stream)
+{
+ Parent::packData(stream);
+ writeRangedS32(stream, mDirect, -10000, 1000);
+ writeRangedS32(stream, mDirectHF, -10000, 0);
+ writeRangedS32(stream, mRoom, -10000, 1000);
+ writeRangedS32(stream, mRoomHF, -10000, 0);
+ writeRangedF32(stream, mObstruction, 0.f, 1.f, 9);
+ writeRangedF32(stream, mObstructionLFRatio, 0.f, 1.f, 8);
+ writeRangedF32(stream, mOcclusion, 0.f, 1.f, 9);
+ writeRangedF32(stream, mOcclusionLFRatio, 0.f, 1.f, 8);
+ writeRangedF32(stream, mOcclusionRoomRatio, 0.f, 10.f, 9);
+ writeRangedF32(stream, mRoomRolloff, 0.f, 10.f, 9);
+ writeRangedF32(stream, mAirAbsorption, 0.f, 10.f, 9);
+ writeRangedS32(stream, mOutsideVolumeHF, -10000, 0);
+ stream->writeInt(mFlags, 3);
+}
+
+void AudioSampleEnvironment::unpackData(BitStream* stream)
+{
+ Parent::unpackData(stream);
+ mDirect = readRangedS32(stream, -10000, 1000);
+ mDirectHF = readRangedS32(stream, -10000, 0);
+ mRoom = readRangedS32(stream, -10000, 1000);
+ mRoomHF = readRangedS32(stream, -10000, 0);
+ mObstruction = readRangedF32(stream, 0.f, 1.f, 9);
+ mObstructionLFRatio = readRangedF32(stream, 0.f, 1.f, 8);
+ mOcclusion = readRangedF32(stream, 0.f, 1.f, 9);
+ mOcclusionLFRatio = readRangedF32(stream, 0.f, 1.f, 8);
+ mOcclusionRoomRatio = readRangedF32(stream, 0.f, 10.f, 9);
+ mRoomRolloff = readRangedF32(stream, 0.f, 10.f, 9);
+ mAirAbsorption = readRangedF32(stream, 0.f, 10.f, 9);
+ mOutsideVolumeHF = readRangedS32(stream, -10000, 0);
+ mFlags = stream->readInt(3);
+}
+
+//--------------------------------------------------------------------------
+// Class AudioDescription:
+//--------------------------------------------------------------------------
+IMPLEMENT_CO_DATABLOCK_V1(AudioDescription);
+
+AudioDescription::AudioDescription()
+{
+ mDescription.mVolume = 1.0f;
+ mDescription.mIsLooping = false;
+ mDescription.mIsStreaming = false;
+ mDescription.mIs3D = false;
+ mDescription.mReferenceDistance = 1.0f;
+ mDescription.mMaxDistance = 100.0f;
+ mDescription.mConeInsideAngle = 360;
+ mDescription.mConeOutsideAngle = 360;
+ mDescription.mConeOutsideVolume = 1.0f;
+ mDescription.mConeVector.set(0, 0, 1);
+ mDescription.mEnvironmentLevel = 0.f;
+ mDescription.mLoopCount = -1;
+ mDescription.mMinLoopGap = 0;
+ mDescription.mMaxLoopGap = 0;
+ mDescription.mType = 0;
+}
+
+//--------------------------------------------------------------------------
+IMPLEMENT_CONSOLETYPE(AudioDescription)
+IMPLEMENT_GETDATATYPE(AudioDescription)
+IMPLEMENT_SETDATATYPE(AudioDescription)
+
+void AudioDescription::initPersistFields()
+{
+ Parent::initPersistFields();
+
+ addField("volume", TypeF32, Offset(mDescription.mVolume, AudioDescription));
+ addField("isLooping", TypeBool, Offset(mDescription.mIsLooping, AudioDescription));
+ addField("isStreaming", TypeBool, Offset(mDescription.mIsStreaming, AudioDescription));
+ addField("is3D", TypeBool, Offset(mDescription.mIs3D, AudioDescription));
+ addField("referenceDistance", TypeF32, Offset(mDescription.mReferenceDistance, AudioDescription));
+ addField("maxDistance", TypeF32, Offset(mDescription.mMaxDistance, AudioDescription));
+ addField("coneInsideAngle", TypeS32, Offset(mDescription.mConeInsideAngle, AudioDescription));
+ addField("coneOutsideAngle", TypeS32, Offset(mDescription.mConeOutsideAngle, AudioDescription));
+ addField("coneOutsideVolume", TypeF32, Offset(mDescription.mConeOutsideVolume, AudioDescription));
+ addField("coneVector", TypePoint3F, Offset(mDescription.mConeVector, AudioDescription));
+ addField("environmentLevel", TypeF32, Offset(mDescription.mEnvironmentLevel, AudioDescription));
+ addField("loopCount", TypeS32, Offset(mDescription.mLoopCount, AudioDescription));
+ addField("minLoopGap", TypeS32, Offset(mDescription.mMinLoopGap, AudioDescription));
+ addField("maxLoopGap", TypeS32, Offset(mDescription.mMaxLoopGap, AudioDescription));
+ addField("type", TypeS32, Offset(mDescription.mType, AudioDescription));
+
+}
+
+//--------------------------------------------------------------------------
+bool AudioDescription::onAdd()
+{
+ if (!Parent::onAdd())
+ return false;
+
+ // validate the data
+ mDescription.mVolume = mClampF(mDescription.mVolume, 0.0f, 1.0f);
+ mDescription.mLoopCount = mClamp(mDescription.mLoopCount, -1, mDescription.mLoopCount);
+ mDescription.mMaxLoopGap = mClamp(mDescription.mMaxLoopGap, mDescription.mMinLoopGap, mDescription.mMaxLoopGap);
+ mDescription.mMinLoopGap = mClamp(mDescription.mMinLoopGap, 0, mDescription.mMaxLoopGap);
+
+ if (mDescription.mIs3D)
+ {
+ // validate the data
+ mDescription.mReferenceDistance = mClampF(mDescription.mReferenceDistance, 0.f, mDescription.mReferenceDistance);
+ mDescription.mMaxDistance = (mDescription.mMaxDistance > mDescription.mReferenceDistance) ? mDescription.mMaxDistance : (mDescription.mReferenceDistance+0.01f);
+ mDescription.mConeInsideAngle = mClamp(mDescription.mConeInsideAngle, 0, 360);
+ mDescription.mConeOutsideAngle = mClamp(mDescription.mConeOutsideAngle, mDescription.mConeInsideAngle, 360);
+ mDescription.mConeOutsideVolume = mClampF(mDescription.mConeOutsideVolume, 0.0f, 1.0f);
+ mDescription.mConeVector.normalize();
+ mDescription.mEnvironmentLevel = mClampF(mDescription.mEnvironmentLevel, 0.f, 1.f);
+ }
+
+ if(mDescription.mType >= Audio::NumAudioTypes)
+ mDescription.mType = 0;
+
+ return true;
+}
+
+
+//--------------------------------------------------------------------------
+void AudioDescription::packData(BitStream* stream)
+{
+ Parent::packData(stream);
+ stream->writeFloat(mDescription.mVolume, 6);
+ if(stream->writeFlag(mDescription.mIsLooping))
+ {
+ stream->write(mDescription.mLoopCount);
+ stream->write(mDescription.mMinLoopGap);
+ stream->write(mDescription.mMaxLoopGap);
+ }
+
+ stream->writeFlag(mDescription.mIsStreaming);
+ stream->writeFlag(mDescription.mIs3D);
+ if (mDescription.mIs3D)
+ {
+ stream->write(mDescription.mReferenceDistance);
+ stream->write(mDescription.mMaxDistance);
+ stream->writeInt(mDescription.mConeInsideAngle, 9);
+ stream->writeInt(mDescription.mConeOutsideAngle, 9);
+ stream->writeInt(mDescription.mConeOutsideVolume, 6);
+ stream->writeNormalVector(mDescription.mConeVector, 8);
+ stream->write(mDescription.mEnvironmentLevel);
+ }
+ stream->writeInt(mDescription.mType, 3);
+}
+
+//--------------------------------------------------------------------------
+void AudioDescription::unpackData(BitStream* stream)
+{
+ Parent::unpackData(stream);
+ mDescription.mVolume = stream->readFloat(6);
+ mDescription.mIsLooping = stream->readFlag();
+ if(mDescription.mIsLooping)
+ {
+ stream->read(&mDescription.mLoopCount);
+ stream->read(&mDescription.mMinLoopGap);
+ stream->read(&mDescription.mMaxLoopGap);
+ }
+
+ mDescription.mIsStreaming = stream->readFlag();
+ mDescription.mIs3D = stream->readFlag();
+ if ( mDescription.mIs3D )
+ {
+ stream->read(&mDescription.mReferenceDistance);
+ stream->read(&mDescription.mMaxDistance);
+ mDescription.mConeInsideAngle = stream->readInt(9);
+ mDescription.mConeOutsideAngle = stream->readInt(9);
+ mDescription.mConeOutsideVolume = stream->readFloat(6);
+ stream->readNormalVector(&mDescription.mConeVector, 8);
+ stream->read(&mDescription.mEnvironmentLevel);
+ }
+ mDescription.mType = stream->readInt(3);
+}
+
+//--------------------------------------------------------------------------
+// Class AudioProfile:
+//--------------------------------------------------------------------------
+IMPLEMENT_CO_DATABLOCK_V1(AudioProfile);
+
+AudioProfile::AudioProfile()
+{
+ mFilename = NULL;
+ mDescriptionObject = NULL;
+ mSampleEnvironment = 0;
+ mPreload = false;
+}
+
+//--------------------------------------------------------------------------
+IMPLEMENT_CONSOLETYPE(AudioProfile)
+IMPLEMENT_GETDATATYPE(AudioProfile)
+IMPLEMENT_SETDATATYPE(AudioProfile)
+
+void AudioProfile::initPersistFields()
+{
+ Parent::initPersistFields();
+
+ addField("filename", TypeFilename, Offset(mFilename, AudioProfile));
+ addField("description", TypeAudioDescriptionPtr, Offset(mDescriptionObject, AudioProfile));
+ addField("environment", TypeAudioSampleEnvironmentPtr, Offset(mSampleEnvironment, AudioProfile));
+ addField("preload", TypeBool, Offset(mPreload, AudioProfile));
+}
+
+bool AudioProfile::preload(bool server, char errorBuffer[256])
+{
+ if(!Parent::preload(server, errorBuffer))
+ return false;
+
+ if(!server && NetConnection::filesWereDownloaded() && !bool(AudioBuffer::find(mFilename)))
+ return false;
+ return true;
+}
+
+//--------------------------------------------------------------------------
+bool AudioProfile::onAdd()
+{
+ if (!Parent::onAdd())
+ return false;
+
+ // if this is client side, make sure that description is as well
+ if(mDescriptionObject)
+ { // client side dataBlock id's are not in the dataBlock id range
+ if (getId() >= DataBlockObjectIdFirst && getId() <= DataBlockObjectIdLast)
+ {
+ SimObjectId pid = mDescriptionObject->getId();
+ if (pid < DataBlockObjectIdFirst || pid > DataBlockObjectIdLast)
+ {
+ Con::errorf(ConsoleLogEntry::General,"AudioProfile: data dataBlock not networkable (use datablock to create).");
+ return false;
+ }
+ }
+ }
+
+ if(mPreload && mFilename != NULL && alcGetCurrentContext())
+ {
+ mBuffer = AudioBuffer::find(mFilename);
+ if(bool(mBuffer))
+ {
+ ALuint bufferId = mBuffer->getALBuffer();
+ }
+ }
+
+ return(true);
+}
+
+//--------------------------------------------------------------------------
+void AudioProfile::packData(BitStream* stream)
+{
+ Parent::packData(stream);
+
+ // audio description:
+ if (stream->writeFlag(mDescriptionObject))
+ stream->writeRangedU32(mDescriptionObject->getId(), DataBlockObjectIdFirst,
+ DataBlockObjectIdLast);
+
+ // environmental info:
+ if (stream->writeFlag(mSampleEnvironment))
+ stream->writeRangedU32(mSampleEnvironment->getId(), DataBlockObjectIdFirst,
+ DataBlockObjectIdLast);
+
+ //
+ char buffer[256];
+ if(!mFilename)
+ buffer[0] = 0;
+ else
+ dStrcpy(buffer, mFilename);
+// S32 len = dStrlen(buffer);
+// if(len > 3 && !dStricmp(buffer + len - 4, ".wav"))
+// buffer[len-4] = 0;
+ stream->writeString(buffer);
+
+}
+
+//--------------------------------------------------------------------------
+void AudioProfile::unpackData(BitStream* stream)
+{
+ Parent::unpackData(stream);
+
+ // audio datablock:
+ if (stream->readFlag()) {
+ SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
+ DataBlockObjectIdLast);
+ Sim::findObject(id, mDescriptionObject);
+ }
+
+ // sample environment:
+ if (stream->readFlag()) {
+ SimObjectId id = stream->readRangedU32(DataBlockObjectIdFirst,
+ DataBlockObjectIdLast);
+ Sim::findObject(id, mSampleEnvironment);
+ }
+
+ char buffer[256];
+ stream->readString(buffer);
+// dStrcat(buffer, ".wav");
+
+ mFilename = StringTable->insert(buffer);
+
+ // Doh! Something missing from the unpackData...don't want to break
+ // network protocol, so set it to true always here. This is good for
+ // ThinkTanks only. In the future, simply send the preload bit.
+ mPreload = true;
+}
+
+
+
+
+
+
+
diff --git a/engine/audio/audioDataBlock.h b/engine/audio/audioDataBlock.h
new file mode 100755
index 0000000..d697382
--- /dev/null
+++ b/engine/audio/audioDataBlock.h
@@ -0,0 +1,139 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _AUDIODATABLOCK_H_
+#define _AUDIODATABLOCK_H_
+
+#ifndef _PLATFORMAUDIO_H_
+#include "platform/platformAudio.h"
+#endif
+#ifndef _AUDIOBUFFER_H_
+#include "audio/audioBuffer.h"
+#endif
+#ifndef _BITSTREAM_H_
+#include "core/bitStream.h"
+#endif
+#ifndef _SIMBASE_H_
+#include "console/simBase.h"
+#endif
+
+//--------------------------------------------------------------------------
+class AudioEnvironment : public SimDataBlock
+{
+ typedef SimDataBlock Parent;
+
+ public:
+
+ bool mUseRoom;
+ S32 mRoom;
+ S32 mRoomHF;
+ S32 mReflections;
+ S32 mReverb;
+ F32 mRoomRolloffFactor;
+ F32 mDecayTime;
+ F32 mDecayHFRatio;
+ F32 mReflectionsDelay;
+ F32 mReverbDelay;
+ S32 mRoomVolume;
+ F32 mEffectVolume;
+ F32 mDamping;
+ F32 mEnvironmentSize;
+ F32 mEnvironmentDiffusion;
+ F32 mAirAbsorption;
+ S32 mFlags;
+
+ AudioEnvironment();
+
+ static void initPersistFields();
+ void packData(BitStream* stream);
+ void unpackData(BitStream* stream);
+
+ DECLARE_CONOBJECT(AudioEnvironment);
+};
+DECLARE_CONSOLETYPE(AudioEnvironment)
+
+//--------------------------------------------------------------------------
+class AudioSampleEnvironment : public SimDataBlock
+{
+ typedef SimDataBlock Parent;
+
+ public:
+
+ S32 mDirect;
+ S32 mDirectHF;
+ S32 mRoom;
+ S32 mRoomHF;
+ F32 mObstruction;
+ F32 mObstructionLFRatio;
+ F32 mOcclusion;
+ F32 mOcclusionLFRatio;
+ F32 mOcclusionRoomRatio;
+ F32 mRoomRolloff;
+ F32 mAirAbsorption;
+ S32 mOutsideVolumeHF;
+ S32 mFlags;
+
+ AudioSampleEnvironment();
+ static void initPersistFields();
+
+ void packData(BitStream* stream);
+ void unpackData(BitStream* stream);
+
+ DECLARE_CONOBJECT(AudioSampleEnvironment);
+};
+DECLARE_CONSOLETYPE(AudioSampleEnvironment)
+
+//--------------------------------------------------------------------------
+class AudioDescription: public SimDataBlock
+{
+private:
+ typedef SimDataBlock Parent;
+
+public:
+ // field info
+ Audio::Description mDescription;
+
+ AudioDescription();
+ DECLARE_CONOBJECT(AudioDescription);
+ static void initPersistFields();
+ virtual bool onAdd();
+ virtual void packData(BitStream* stream);
+ virtual void unpackData(BitStream* stream);
+
+ const Audio::Description* getDescription() const { return &mDescription; }
+};
+DECLARE_CONSOLETYPE(AudioDescription)
+
+//----------------------------------------------------------------------------
+class AudioProfile: public SimDataBlock
+{
+private:
+ typedef SimDataBlock Parent;
+
+ Resource mBuffer;
+
+public:
+ // field info
+ AudioDescription *mDescriptionObject;
+ AudioSampleEnvironment *mSampleEnvironment;
+
+ StringTableEntry mFilename;
+ bool mPreload;
+
+ AudioProfile();
+ DECLARE_CONOBJECT(AudioProfile);
+ static void initPersistFields();
+
+ virtual bool onAdd();
+ virtual void packData(BitStream* stream);
+ virtual void unpackData(BitStream* stream);
+ virtual bool preload(bool server, char errorBuffer[256]);
+
+ const Audio::Description* getDescription() const { return mDescriptionObject ? mDescriptionObject->getDescription() : NULL; }
+ bool isPreload() { return mPreload; }
+};
+DECLARE_CONSOLETYPE(AudioProfile)
+
+#endif // _H_AUDIODATABLOCK_
diff --git a/engine/audio/audioFunctions.cc b/engine/audio/audioFunctions.cc
new file mode 100755
index 0000000..9ed46d6
--- /dev/null
+++ b/engine/audio/audioFunctions.cc
@@ -0,0 +1,554 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "platform/platformAudio.h"
+#include "console/simBase.h"
+#include "audio/audioDataBlock.h"
+
+
+extern ALuint alxGetWaveLen(ALuint buffer);
+
+extern F32 mAudioTypeVolume[Audio::NumAudioTypes];
+
+//--------------------------------------------------------------------------
+// Expose all al get/set methods...
+//--------------------------------------------------------------------------
+enum AL_GetSetBits{
+ Source = BIT(0),
+ Listener = BIT(1),
+ Context = BIT(2),
+ Environment = BIT(3),
+ Get = BIT(4),
+ Set = BIT(5),
+ Int = BIT(6),
+ Float = BIT(7),
+ Float3 = BIT(8),
+ Float6 = BIT(9)
+};
+
+static ALenum getEnum(const char * name, U32 flags)
+{
+ AssertFatal(name, "Audio getEnum: bad param");
+
+ static struct {
+ char * mName;
+ ALenum mAlenum;
+ U32 mFlags;
+ } table[] = {
+ //-----------------------------------------------------------------------------------------------------------------
+ // "name" ENUM Flags
+ //-----------------------------------------------------------------------------------------------------------------
+ { "AL_GAIN", AL_GAIN, (Source|Listener|Get|Set|Float) },
+ { "AL_GAIN_LINEAR", AL_GAIN_LINEAR, (Source|Listener|Get|Set|Float) },
+ { "AL_PITCH", AL_PITCH, (Source|Get|Set|Float) },
+ { "AL_REFERENCE_DISTANCE", AL_REFERENCE_DISTANCE, (Source|Get|Set|Float) },
+ { "AL_MAX_DISTANCE", AL_MAX_DISTANCE, (Source|Get|Set|Float) },
+ { "AL_CONE_OUTER_GAIN", AL_CONE_OUTER_GAIN, (Source|Get|Set|Float) },
+ { "AL_POSITION", AL_POSITION, (Source|Listener|Get|Set|Float3) },
+ { "AL_DIRECTION", AL_DIRECTION, (Source|Get|Set|Float3) },
+ { "AL_VELOCITY", AL_VELOCITY, (Source|Listener|Get|Set|Float3) },
+ { "AL_ORIENTATION", AL_ORIENTATION, (Listener|Set|Float6) },
+ { "AL_CONE_INNER_ANGLE", AL_CONE_INNER_ANGLE, (Source|Get|Set|Int) },
+ { "AL_CONE_OUTER_ANGLE", AL_CONE_OUTER_ANGLE, (Source|Get|Set|Int) },
+ { "AL_LOOPING", AL_LOOPING, (Source|Get|Set|Int) },
+ //{ "AL_STREAMING", AL_STREAMING, (Source|Get|Set|Int) },
+ //{ "AL_BUFFER", AL_BUFFER, (Source|Get|Set|Int) },
+
+ { "AL_VENDOR", AL_VENDOR, (Context|Get) },
+ { "AL_VERSION", AL_VERSION, (Context|Get) },
+ { "AL_RENDERER", AL_RENDERER, (Context|Get) },
+ { "AL_EXTENSIONS", AL_EXTENSIONS, (Context|Get) },
+
+ /*
+ // environment
+ { "AL_ENV_ROOM_IASIG", AL_ENV_ROOM_IASIG, (Environment|Get|Set|Int) },
+ { "AL_ENV_ROOM_HIGH_FREQUENCY_IASIG", AL_ENV_ROOM_HIGH_FREQUENCY_IASIG, (Environment|Get|Set|Int) },
+ { "AL_ENV_REFLECTIONS_IASIG", AL_ENV_REFLECTIONS_IASIG, (Environment|Get|Set|Int) },
+ { "AL_ENV_REVERB_IASIG", AL_ENV_REVERB_IASIG, (Environment|Get|Set|Int) },
+ { "AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG", AL_ENV_ROOM_ROLLOFF_FACTOR_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_DECAY_TIME_IASIG", AL_ENV_DECAY_TIME_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG", AL_ENV_DECAY_HIGH_FREQUENCY_RATIO_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_REFLECTIONS_DELAY_IASIG", AL_ENV_REFLECTIONS_DELAY_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_REVERB_DELAY_IASIG", AL_ENV_REVERB_DELAY_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_DIFFUSION_IASIG", AL_ENV_DIFFUSION_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_DENSITY_IASIG", AL_ENV_DENSITY_IASIG, (Environment|Get|Set|Float) },
+ { "AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG", AL_ENV_HIGH_FREQUENCY_REFERENCE_IASIG, (Environment|Get|Set|Float) },
+
+ { "AL_ENV_ROOM_VOLUME_EXT", AL_ENV_ROOM_VOLUME_EXT, (Environment|Get|Set|Int) },
+ { "AL_ENV_FLAGS_EXT", AL_ENV_FLAGS_EXT, (Environment|Get|Set|Int) },
+ { "AL_ENV_EFFECT_VOLUME_EXT", AL_ENV_EFFECT_VOLUME_EXT, (Environment|Get|Set|Float) },
+ { "AL_ENV_DAMPING_EXT", AL_ENV_DAMPING_EXT, (Environment|Get|Set|Float) },
+ { "AL_ENV_ENVIRONMENT_SIZE_EXT", AL_ENV_ENVIRONMENT_SIZE_EXT, (Environment|Get|Set|Float) },
+
+ // sample environment
+ { "AL_ENV_SAMPLE_DIRECT_EXT", AL_ENV_SAMPLE_DIRECT_EXT, (Source|Get|Set|Int) },
+ { "AL_ENV_SAMPLE_DIRECT_HF_EXT", AL_ENV_SAMPLE_DIRECT_HF_EXT, (Source|Get|Set|Int) },
+ { "AL_ENV_SAMPLE_ROOM_EXT", AL_ENV_SAMPLE_ROOM_EXT, (Source|Get|Set|Int) },
+ { "AL_ENV_SAMPLE_ROOM_HF_EXT", AL_ENV_SAMPLE_ROOM_HF_EXT, (Source|Get|Set|Int) },
+ { "AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT", AL_ENV_SAMPLE_OUTSIDE_VOLUME_HF_EXT, (Source|Get|Set|Int) },
+ { "AL_ENV_SAMPLE_FLAGS_EXT", AL_ENV_SAMPLE_FLAGS_EXT, (Source|Get|Set|Int) },
+
+ { "AL_ENV_SAMPLE_REVERB_MIX_EXT", AL_ENV_SAMPLE_REVERB_MIX_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_OBSTRUCTION_EXT", AL_ENV_SAMPLE_OBSTRUCTION_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT", AL_ENV_SAMPLE_OBSTRUCTION_LF_RATIO_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_OCCLUSION_EXT", AL_ENV_SAMPLE_OCCLUSION_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT", AL_ENV_SAMPLE_OCCLUSION_LF_RATIO_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT", AL_ENV_SAMPLE_OCCLUSION_ROOM_RATIO_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT", AL_ENV_SAMPLE_ROOM_ROLLOFF_EXT, (Source|Get|Set|Float) },
+ { "AL_ENV_SAMPLE_AIR_ABSORPTION_EXT", AL_ENV_SAMPLE_AIR_ABSORPTION_EXT, (Source|Get|Set|Float) },
+ */
+ };
+ for(U32 i = 0; i < (sizeof(table) / sizeof(table[0])); i++)
+ {
+ if((table[i].mFlags & flags) != flags)
+ continue;
+
+ if(dStricmp(table[i].mName, name) == 0)
+ return(table[i].mAlenum);
+ }
+
+ return(AL_INVALID);
+}
+
+
+//-----------------------------------------------
+ConsoleFunctionGroupBegin(Audio, "Functions dealing with the OpenAL audio layer.\n\n"
+ "@see www.OpenAL.org for what these functions do. Variances from posted"
+ " behaviour is described below.");
+
+ConsoleFunction(OpenALInitDriver, bool, 1, 1, "Initializes the OpenAL driver.\n\n"
+ "@note You must call this before any sounds will work!")
+{
+ if (Audio::OpenALInit())
+ {
+ static bool registered = false;
+ if (!registered) {
+ ResourceManager->registerExtension(".wav", AudioBuffer::construct);
+ ResourceManager->registerExtension(".ogg", AudioBuffer::construct);
+ }
+ registered = true;
+ return true;
+ }
+ return false;
+}
+
+//-----------------------------------------------
+ConsoleFunction(OpenALShutdownDriver, void, 1, 1, "OpenALShutdownDriver()")
+{
+ Audio::OpenALShutdown();
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(OpenALRegisterExtensions, void, 1, 1, "OpenALRegisterExtensions()")
+{
+}
+
+//-----------------------------------------------
+ConsoleFunction(alGetString, const char *, 2, 2, "(string item)\n\n"
+ "This wraps alGetString().")
+{
+ argc;
+ ALenum e = getEnum(argv[1], (Context|Get));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alGetString: invalid enum name '%s'", argv[1]);
+ return "";
+ }
+
+ return (const char*)alGetString(e);
+}
+
+
+//--------------------------------------------------------------------------
+// Soundfile
+//--------------------------------------------------------------------------
+ConsoleFunction(alxGetWaveLen, S32, 2, 2, "(string filename)\n\n"
+ "@param filename File to determine length of.\n"
+ "@returns Length in milliseconds.")
+{
+ Resource buffer = AudioBuffer::find(argv[1]);
+ if (bool(buffer)) {
+ ALuint alBuffer = buffer->getALBuffer();
+ return alxGetWaveLen(alBuffer);
+ }
+ else {
+ return 0;
+ }
+}
+
+//--------------------------------------------------------------------------
+// Source
+//--------------------------------------------------------------------------
+ConsoleFunction(alxCreateSource, S32, 2, 6,
+ "(profile) or "
+ "(profile, x,y,z) or "
+ "(description, filename) or "
+ "(description, filename, x,y,z)")
+{
+ AudioDescription *description = NULL;
+ AudioProfile *profile = dynamic_cast( Sim::findObject( argv[1] ) );
+ if (profile == NULL)
+ {
+ description = dynamic_cast( Sim::findObject( argv[1] ) );
+ if (description == NULL)
+ {
+ Con::printf("Unable to locate audio profile/description '%s'", argv[1]);
+ return NULL_AUDIOHANDLE;
+ }
+ }
+
+ if (profile)
+ {
+ if (argc == 2)
+ return alxCreateSource(profile);
+
+ MatrixF transform;
+ transform.set(EulerF(0,0,0), Point3F( dAtof(argv[2]),dAtof(argv[3]),dAtof(argv[4]) ));
+ return alxCreateSource(profile, &transform);
+ }
+
+ if (description)
+ {
+
+ if (argc == 3)
+ return alxCreateSource(description, argv[2]);
+
+ MatrixF transform;
+ transform.set(EulerF(0,0,0), Point3F( dAtof(argv[3]),dAtof(argv[4]),dAtof(argv[5]) ));
+ return alxCreateSource(description, argv[2], &transform);
+ }
+
+ return NULL_AUDIOHANDLE;
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxSourcef, void, 4, 4, "(handle, ALenum, value)")
+{
+ ALenum e = getEnum(argv[2], (Source|Set|Float));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxSourcef: invalid enum name '%s'", argv[2]);
+ return;
+ }
+
+ alxSourcef(dAtoi(argv[1]), e, dAtof(argv[3]));
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxSource3f, void, 3, 6, "(handle, ALenum, x, y, z)\n\n"
+ "@note You can replace the last three parameters with a string, "
+ "\"x y z\"")
+{
+ ALenum e = getEnum(argv[2], (Source|Set|Float3));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxSource3f: invalid enum name '%s'", argv[2]);
+ return;
+ }
+
+ if((argc != 4 && argc != 6))
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxSource3f: wrong number of args");
+ return;
+ }
+
+ Point3F pos;
+ if(argc == 4)
+ dSscanf(argv[3], "%g %g %g", &pos.x, &pos.y, &pos.z);
+ else
+ {
+ pos.x = dAtof(argv[3]);
+ pos.y = dAtof(argv[4]);
+ pos.z = dAtof(argv[5]);
+ }
+
+ alxSource3f(dAtoi(argv[1]), e, pos.x, pos.y, pos.z);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxSourcei, void, 4, 4, "(handle, ALenum, value)")
+{
+ ALenum e = getEnum(argv[2], (Source|Set|Int));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxSourcei: invalid enum name '%s'", argv[2]);
+ return;
+ }
+
+ alxSourcei(dAtoi(argv[1]), e, dAtoi(argv[3]));
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxGetSourcef, F32, 3, 3, "(handle, ALenum)")
+{
+ ALenum e = getEnum(argv[2], (Source|Get|Float));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSourcef: invalid enum name '%s'", argv[2]);
+ return(0.f);
+ }
+
+ F32 value;
+ alxGetSourcef(dAtoi(argv[1]), e, &value);
+ return(value);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxGetSource3f, const char *, 3, 3, "(handle, ALenum)" )
+{
+ ALenum e = getEnum(argv[2], (Source|Get|Float));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSource3f: invalid enum name '%s'", argv[2]);
+ return("0 0 0");
+ }
+
+ F32 value1, value2, value3;
+ alxGetSource3f(dAtoi(argv[1]), e, &value1, &value2, &value3);
+
+ char * ret = Con::getReturnBuffer(64);
+ dSprintf(ret, 64, "%7.3f %7.3 %7.3", value1, value2, value3);
+ return(ret);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxGetSourcei, S32, 3, 3, "(handle, ALenum)")
+{
+ ALenum e = getEnum(argv[2], (Source|Get|Int));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "cAudio_alxGetSourcei: invalid enum name '%s'", argv[2]);
+ return(0);
+ }
+
+ S32 value;
+ alxGetSourcei(dAtoi(argv[1]), e, &value);
+ return(value);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxPlay, S32, 2, 5, "alxPlay(handle) or "
+ "alxPlay(profile) or "
+ "alxPlay(profile, x,y,z)")
+{
+ if (argc == 2)
+ {
+ AUDIOHANDLE handle = dAtoi(argv[1]);
+ if (handle != 0)
+ return alxPlay(handle);
+ }
+
+ AudioProfile *profile = dynamic_cast( Sim::findObject( argv[1] ) );
+ if (profile == NULL)
+ {
+ Con::printf("Unable to locate audio profile '%s'", argv[1]);
+ return NULL_AUDIOHANDLE;
+ }
+
+ Point3F pos(0.f, 0.f, 0.f);
+ if(argc == 3)
+ dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
+ else if(argc == 5)
+ pos.set(dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]));
+
+ MatrixF transform;
+ transform.set(EulerF(0,0,0), pos);
+
+ return alxPlay(profile, &transform, NULL);
+}
+
+//-----------------------------------------------
+ConsoleFunction(alxStop, void, 2, 2, "(int handle)")
+{
+ AUDIOHANDLE handle = dAtoi(argv[1]);
+ if(handle == NULL_AUDIOHANDLE)
+ return;
+ alxStop(handle);
+}
+
+//-----------------------------------------------
+ConsoleFunction(alxStopAll, void, 1, 1, "()")
+{
+ alxStopAll();
+}
+
+//-----------------------------------------------
+ConsoleFunction(alxIsPlaying, bool, 2, 5, "alxIsPlaying(handle)")
+{
+ AUDIOHANDLE handle = dAtoi(argv[1]);
+ if(handle == NULL_AUDIOHANDLE)
+ return false;
+ return alxIsPlaying(handle);
+}
+
+
+//--------------------------------------------------------------------------
+// Listener
+//--------------------------------------------------------------------------
+ConsoleFunction(alxListenerf, void, 3, 3, "alxListener(ALenum, value)")
+{
+ ALenum e = getEnum(argv[1], (Listener|Set|Float));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alxListenerf: invalid enum name '%s'", argv[1]);
+ return;
+ }
+
+ alxListenerf(e, dAtof(argv[2]));
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alListener3f, void, 3, 5, "alListener3f(ALenum, \"x y z\") or "
+ "alListener3f(ALenum, x, y, z)")
+{
+ ALenum e = getEnum(argv[1], (Listener|Set|Float3));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alListener3f: invalid enum name '%s'", argv[1]);
+ return;
+ }
+
+ if(argc != 3 || argc != 5)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alListener3f: wrong number of arguments");
+ return;
+ }
+
+ Point3F pos;
+ if(argc == 3)
+ dSscanf(argv[2], "%g %g %g", &pos.x, &pos.y, &pos.z);
+ else
+ {
+ pos.x = dAtof(argv[2]);
+ pos.y = dAtof(argv[3]);
+ pos.z = dAtof(argv[4]);
+ }
+
+ alListener3f(e, pos.x, pos.y, pos.z);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alxGetListenerf, F32, 2, 2, "alxGetListenerf(Alenum)")
+{
+ ALenum e = getEnum(argv[1], (Source|Get|Float));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alxGetListenerf: invalid enum name '%s'", argv[1]);
+ return(0.f);
+ }
+
+ F32 value;
+ alxGetListenerf(e, &value);
+ return(value);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alGetListener3f, const char *, 2, 2, "alGetListener3f(Alenum)")
+{
+ ALenum e = getEnum(argv[2], (Source|Get|Float));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alGetListener3f: invalid enum name '%s'", argv[1]);
+ return("0 0 0");
+ }
+
+ F32 value1, value2, value3;
+ alGetListener3f(e, &value1, &value2, &value3);
+
+ char * ret = Con::getReturnBuffer(64);
+ dSprintf(ret, 64, "%7.3f %7.3 %7.3", value1, value2, value3);
+ return(ret);
+}
+
+
+//-----------------------------------------------
+ConsoleFunction(alGetListeneri, S32, 2, 2, "alGetListeneri(Alenum)")
+{
+ ALenum e = getEnum(argv[1], (Source|Get|Int));
+ if(e == AL_INVALID)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alGetListeneri: invalid enum name '%s'", argv[1]);
+ return(0);
+ }
+
+ S32 value;
+ alGetListeneri(e, &value);
+ return(value);
+}
+
+
+//--------------------------------------------------------------------------
+// Channel Volumes
+//--------------------------------------------------------------------------
+ConsoleFunction(alxGetChannelVolume, F32, 2, 2, "(int channel_id)\n\n"
+ "@param channel_id ID of channel to fetch volume from.\n"
+ "@return Volume of channel.")
+{
+ U32 type = dAtoi(argv[1]);
+ if(type >= Audio::NumAudioTypes)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alxGetChannelVolume: invalid channel '%d'", dAtoi(argv[1]));
+ return(0.f);
+ }
+
+ return(mAudioTypeVolume[type]);
+}
+
+//-----------------------------------------------
+ConsoleFunction(alxSetChannelVolume, bool, 3, 3, "(int channel_id, float volume)\n\n"
+ "@param channel_id ID of channel to set volume on.\n"
+ "@param volume New volume of channel, from 0.0f-1.0f"
+ )
+{
+ U32 type = dAtoi(argv[1]);
+ F32 volume = mClampF(dAtof(argv[2]), 0.f, 1.f);
+
+ if(type >= Audio::NumAudioTypes)
+ {
+ Con::errorf(ConsoleLogEntry::General, "alxSetChannelVolume: channel '%d' out of range [0, %d]", dAtoi(argv[1]), Audio::NumAudioTypes);
+ return false;
+ }
+
+ mAudioTypeVolume[type] = volume;
+ alxUpdateTypeGain(type);
+ return true;
+}
+
+//-----------------------------------------------
+ConsoleFunction(alxGetStreamPosition, F32, 2, 2, "alxGetStreamPosition(handle)" )
+{
+ AUDIOHANDLE handle = dAtoi(argv[1]);
+
+ if(handle == NULL_AUDIOHANDLE)
+ return -1;
+
+ return alxGetStreamPosition( handle );
+}
+
+//-----------------------------------------------
+ConsoleFunction(alxGetStreamDuration, F32, 2, 2, "alxGetStreamDuration(handle)" )
+{
+ AUDIOHANDLE handle = dAtoi(argv[1]);
+
+ if(handle == NULL_AUDIOHANDLE)
+ return -1;
+
+ return alxGetStreamDuration( handle );
+}
+
+
+ConsoleFunctionGroupEnd(Audio);
diff --git a/engine/audio/audioStreamSource.h b/engine/audio/audioStreamSource.h
new file mode 100755
index 0000000..e6b4c81
--- /dev/null
+++ b/engine/audio/audioStreamSource.h
@@ -0,0 +1,64 @@
+//--------------------------------------------
+// audioStreamSource.h
+// header for streaming audio source
+//
+// Kurtis Seebaldt
+//--------------------------------------------
+
+#ifndef _AUDIOSTREAMSOURCE_H_
+#define _AUDIOSTREAMSOURCE_H_
+
+#ifndef _PLATFORM_H_
+#include "platform/platform.h"
+#endif
+#ifndef _PLATFORMAUDIO_H_
+#include "platform/platformAudio.h"
+#endif
+#ifndef _PLATFORMAL_H_
+#include "platform/platformAL.h"
+#endif
+#ifndef _AUDIOBUFFER_H_
+#include "audio/audioBuffer.h"
+#endif
+#ifndef _RESMANAGER_H_
+#include "core/resManager.h"
+#endif
+
+#define NUMBUFFERS 16
+
+class AudioStreamSource
+{
+ public:
+ //need this because subclasses are deleted through base pointer
+ virtual ~AudioStreamSource() {}
+ virtual bool initStream() = 0;
+ virtual bool updateBuffers() = 0;
+ virtual void freeStream() = 0;
+ virtual F32 getElapsedTime() = 0;
+ virtual F32 getTotalTime() = 0;
+ //void clear();
+
+ AUDIOHANDLE mHandle;
+ ALuint mSource;
+
+ Audio::Description mDescription;
+ AudioSampleEnvironment *mEnvironment;
+
+ Point3F mPosition;
+ Point3F mDirection;
+ F32 mPitch;
+ F32 mScore;
+ U32 mCullTime;
+
+ bool bFinishedPlaying;
+ bool bIsValid;
+
+#ifdef TORQUE_OS_LINUX
+ void checkPosition();
+#endif
+
+ protected:
+ const char* mFilename;
+};
+
+#endif // _AUDIOSTREAMSOURCE_H_
diff --git a/engine/audio/audioStreamSourceFactory.cc b/engine/audio/audioStreamSourceFactory.cc
new file mode 100755
index 0000000..2477d57
--- /dev/null
+++ b/engine/audio/audioStreamSourceFactory.cc
@@ -0,0 +1,33 @@
+//--------------------------------------
+// audioStreamSource.cc
+// implementation of streaming audio source
+//
+// Kurtis Seebaldt
+//--------------------------------------
+
+#include "audio/audioStreamSourceFactory.h"
+
+#include "audio/wavStreamSource.h"
+#ifndef TORQUE_NO_OGGVORBIS
+#include "audio/vorbisStreamSource.h"
+#include "audio/oggMixedStreamSource.h"
+#endif
+
+AudioStreamSource* AudioStreamSourceFactory::getNewInstance(const char *filename)
+{
+#ifndef TORQUE_NO_OGGVORBIS
+
+ if(!dStricmp(filename, "oggMixedStream"))
+ return new OggMixedStreamSource(filename);
+
+ S32 len = dStrlen(filename);
+
+ // Check filename extension and guess filetype from that
+
+ if(len > 3 && !dStricmp(filename + len - 4, ".wav"))
+ return new WavStreamSource(filename);
+ if(len > 3 && !dStricmp(filename + len - 4, ".ogg"))
+ return new VorbisStreamSource(filename);
+#endif
+ return NULL;
+}
diff --git a/engine/audio/audioStreamSourceFactory.h b/engine/audio/audioStreamSourceFactory.h
new file mode 100755
index 0000000..1e2f123
--- /dev/null
+++ b/engine/audio/audioStreamSourceFactory.h
@@ -0,0 +1,30 @@
+//--------------------------------------------
+// audioStreamSource.h
+// header for streaming audio source
+//
+// Kurtis Seebaldt
+//--------------------------------------------
+
+#ifndef _AUDIOSTREAMSOURCEFACTORY_H_
+#define _AUDIOSTREAMSOURCEFACTORY_H_
+
+#ifndef _PLATFORM_H_
+#include "platform/platform.h"
+#endif
+#ifndef _PLATFORMAUDIO_H_
+#include "platform/platformAudio.h"
+#endif
+#ifndef _PLATFORMAL_H_
+#include "platform/platformAL.h"
+#endif
+#ifndef _AUDIOSTREAMSOURCE_H_
+#include "audio/audioStreamSource.h"
+#endif
+
+class AudioStreamSourceFactory
+{
+ public:
+ static AudioStreamSource* getNewInstance(const char* filename);
+};
+
+#endif // _AUDIOSTREAMSOURCEFACTORY_H_
diff --git a/engine/audio/oggMixedStreamSource.cc b/engine/audio/oggMixedStreamSource.cc
new file mode 100755
index 0000000..a06072e
--- /dev/null
+++ b/engine/audio/oggMixedStreamSource.cc
@@ -0,0 +1,133 @@
+//--------------------------------------
+//
+// This class is basically a buffer that is filled from
+// the ogg stream the theoraplayer has open
+//
+//--------------------------------------
+
+#include "audio/oggMixedStreamSource.h"
+
+OggMixedStreamSource::OggMixedStreamSource(const char *filename)
+{
+ bIsValid = false;
+ bBuffersAllocated = false;
+ for(int i = 0; i < BUFFERCNT; i++)
+ {
+ mBufferList[i] = 0;
+ m_fBufferInUse[i] = false;
+ }
+
+
+ mHandle = NULL_AUDIOHANDLE;
+ mSource = NULL;
+
+ mFilename = filename;
+ mPosition = Point3F(0.f,0.f,0.f);
+
+ dMemset(&mDescription, 0, sizeof(Audio::Description));
+ mEnvironment = 0;
+ mPosition.set(0.f,0.f,0.f);
+ mDirection.set(0.f,1.f,0.f);
+ mPitch = 1.f;
+ mScore = 0.f;
+ mCullTime = 0;
+
+ bFinishedPlaying = false;
+ bIsValid = false;
+ bBuffersAllocated = false;
+}
+
+OggMixedStreamSource::~OggMixedStreamSource()
+{
+ if(bIsValid)
+ freeStream();
+}
+
+bool OggMixedStreamSource::initStream()
+{
+ alSourceStop(mSource);
+ alSourcei(mSource, AL_BUFFER, 0);
+
+ // Clear Error Code
+ alGetError();
+
+ alGenBuffers(BUFFERCNT, mBufferList);
+ if (alGetError() != AL_NO_ERROR)
+ return false;
+
+ bBuffersAllocated = true;
+
+ alSourcei(mSource, AL_LOOPING, AL_FALSE);
+
+ bIsValid = true;
+
+ return true;
+}
+
+bool OggMixedStreamSource::updateBuffers()
+{
+ // buffers are updated from theora player
+ return true;
+}
+
+void OggMixedStreamSource::freeStream()
+{
+ // free the al buffers
+ if(bBuffersAllocated)
+ {
+ alSourceStop(mSource);
+ alSourcei(mSource, AL_BUFFER, 0);
+ alDeleteBuffers(BUFFERCNT, mBufferList);
+
+ alGetError();
+
+ for(int i = 0; i < BUFFERCNT; i++)
+ {
+ mBufferList[i] = 0;
+ m_fBufferInUse[i] = false;
+ }
+
+ bBuffersAllocated = false;
+ }
+}
+
+ALuint OggMixedStreamSource::GetAvailableBuffer()
+{
+ if(!bBuffersAllocated)
+ return 0;
+
+ // test for unused buffers
+ for(int i = 0; i < BUFFERCNT; i++)
+ {
+ if(!m_fBufferInUse[i])
+ {
+ m_fBufferInUse[i] = true;
+ return mBufferList[i];
+ }
+ }
+
+ alGetError();
+
+ // test for processed buffers
+ ALint processed;
+ alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
+
+ if(!processed)
+ return 0; // no available buffers
+
+ ALuint BufferID;
+ alSourceUnqueueBuffers(mSource, 1, &BufferID);
+
+ if (alGetError() != AL_NO_ERROR)
+ return 0; // something went wrong..
+
+ return BufferID;
+}
+
+bool OggMixedStreamSource::QueueBuffer(ALuint BufferID)
+{
+ alSourceQueueBuffers(mSource, 1, &BufferID);
+ if (alGetError() != AL_NO_ERROR)
+ return false;
+ return true;
+}
diff --git a/engine/audio/oggMixedStreamSource.h b/engine/audio/oggMixedStreamSource.h
new file mode 100755
index 0000000..f96bc9f
--- /dev/null
+++ b/engine/audio/oggMixedStreamSource.h
@@ -0,0 +1,50 @@
+//--------------------------------------------
+// oggMixedStreamSource.h
+// header for audio stream dummy class, basicly
+// an audio buffer filled remotely
+//
+//--------------------------------------------
+
+#ifndef _OGGMIXEDSTREAMSOURCE_H_
+#define _OGGMIXEDSTREAMSOURCE_H_
+
+#ifndef _AUDIOSTREAMSOURCE_H_
+#include "audio/audioStreamSource.h"
+#endif
+
+#define BUFFERCNT 128
+
+class OggMixedStreamSource: public AudioStreamSource
+{
+public:
+ OggMixedStreamSource(const char *filename);
+ virtual ~OggMixedStreamSource();
+
+ virtual bool initStream();
+ virtual bool updateBuffers();
+ virtual void freeStream();
+
+ ALuint GetAvailableBuffer();
+ bool QueueBuffer(ALuint BufferID);
+ void PlayStream();
+
+ virtual F32 getElapsedTime()
+ {
+ return 0.0;
+ }
+
+ virtual F32 getTotalTime()
+ {
+ return 1.0f;
+ }
+
+private:
+ ALuint mBufferList[BUFFERCNT];
+ bool m_fBufferInUse[BUFFERCNT];
+
+ bool bBuffersAllocated;
+
+ void clear();
+};
+
+#endif // _OGGMIXEDSTREAMSOURCE_H_
diff --git a/engine/audio/vorbisStream.cc b/engine/audio/vorbisStream.cc
new file mode 100755
index 0000000..a8baf64
--- /dev/null
+++ b/engine/audio/vorbisStream.cc
@@ -0,0 +1,1535 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: stdio-based convenience library for opening/seeking/decoding
+
+ ********************************************************************/
+
+#include
+#include
+#include
+#include
+
+#include "vorbis/codec.h"
+#include "vorbisStream.h"
+
+/* A 'chained bitstream' is a Vorbis bitstream that contains more than
+ one logical bitstream arranged end to end (the only form of Ogg
+ multiplexing allowed in a Vorbis bitstream; grouping [parallel
+ multiplexing] is not allowed in Vorbis) */
+
+/* A Vorbis file can be played beginning to end (streamed) without
+ worrying ahead of time about chaining (see decoder_example.c). If
+ we have the whole file, however, and want random access
+ (seeking/scrubbing) or desire to know the total length/time of a
+ file, we need to account for the possibility of chaining. */
+
+/* We can handle things a number of ways; we can determine the entire
+ bitstream structure right off the bat, or find pieces on demand.
+ This example determines and caches structure for the entire
+ bitstream, but builds a virtual decoder on the fly when moving
+ between links in the chain. */
+
+/* There are also different ways to implement seeking. Enough
+ information exists in an Ogg bitstream to seek to
+ sample-granularity positions in the output. Or, one can seek by
+ picking some portion of the stream roughly in the desired area if
+ we only want coarse navigation through the stream. */
+
+/*************************************************************************
+ * Many, many internal helpers. The intention is not to be confusing;
+ * rampant duplication and monolithic function implementation would be
+ * harder to understand anyway. The high level functions are last. Begin
+ * grokking near the end of the file */
+
+/* read a little more data from the file/pipe into the ogg_sync framer
+*/
+#define CHUNKSIZE 8500 /* a shade over 8k; anyone using pages well
+ over 8k gets what they deserve */
+long OggVorbisFile::_get_data(){
+ errno = 0;
+ if(vf->datasource){
+ char *buffer=ogg_sync_buffer(&vf->oy,CHUNKSIZE);
+ U32 result = vf->datasource->read(CHUNKSIZE, buffer);
+
+ if(result)ogg_sync_wrote(&vf->oy,CHUNKSIZE);
+ if(!result && errno)return(-1);
+ return(result);
+ }else
+ return(0);
+}
+
+/* save a tiny smidge of verbosity to make the code more readable */
+void OggVorbisFile::_seek_helper(long offset){
+ if(vf->datasource){
+ vf->datasource->setPosition(offset);
+ vf->offset=offset;
+ ogg_sync_reset(&vf->oy);
+ }else{
+ /* shouldn't happen unless someone writes a broken callback */
+ return;
+ }
+}
+
+/* The read/seek functions track absolute position within the stream */
+
+/* from the head of the stream, get the next page. boundary specifies
+ if the function is allowed to fetch more data from the stream (and
+ how much) or only use internally buffered data.
+
+ boundary: -1) unbounded search
+ 0) read no additional data; use cached only
+ n) search for a new page beginning for n bytes
+
+ return: <0) did not find a page (OV_FALSE, OV_EOF, OV_EREAD)
+ n) found a page at absolute offset n */
+
+long OggVorbisFile::_get_next_page(ogg_page *og,int boundary){
+ if(boundary>0)boundary+=vf->offset;
+ while(1){
+ long more;
+
+ if(boundary>0 && vf->offset>=boundary)return(OV_FALSE);
+ more=ogg_sync_pageseek(&vf->oy,og);
+
+ if(more<0){
+ /* skipped n bytes */
+ vf->offset-=more;
+ }else{
+ if(more==0){
+ /* send more paramedics */
+ if(!boundary)return(OV_FALSE);
+ {
+ long ret=_get_data();
+ if(ret==0)return(OV_EOF);
+ if(ret<0)return(OV_EREAD);
+ }
+ }else{
+ /* got a page. Return the offset at the page beginning,
+ advance the internal offset past the page end */
+ long ret=vf->offset;
+ vf->offset+=more;
+ return(ret);
+
+ }
+ }
+ }
+}
+
+/* find the latest page beginning before the current stream cursor
+ position. Much dirtier than the above as Ogg doesn't have any
+ backward search linkage. no 'readp' as it will certainly have to
+ read. */
+/* returns offset or OV_EREAD, OV_FAULT */
+long OggVorbisFile::_get_prev_page(ogg_page *og){
+ long begin=vf->offset;
+ long ret;
+ int offset=-1;
+
+ while(offset==-1){
+ begin-=CHUNKSIZE;
+ if(begin<0)
+ begin=0;
+ _seek_helper(begin);
+ while(vf->offsetoffset);
+ if(ret==OV_EREAD)return(OV_EREAD);
+ if(ret<0){
+ break;
+ }else{
+ offset=ret;
+ }
+ }
+ }
+
+ /* we have the offset. Actually snork and hold the page now */
+ _seek_helper(offset);
+ ret=_get_next_page(og,CHUNKSIZE);
+ if(ret<0)
+ /* this shouldn't be possible */
+ return(OV_EFAULT);
+
+ return(offset);
+}
+
+/* finds each bitstream link one at a time using a bisection search
+ (has to begin by knowing the offset of the lb's initial page).
+ Recurses for each link so it can alloc the link storage after
+ finding them all, then unroll and fill the cache at the same time */
+int OggVorbisFile::_bisect_forward_serialno(
+ long begin,
+ long searched,
+ long end,
+ long currentno,
+ long m){
+ long endsearched=end;
+ long next=end;
+ ogg_page og;
+ long ret;
+
+ /* the below guards against garbage seperating the last and
+ first pages of two links. */
+ while(searched=0)next=ret;
+ }else{
+ searched=ret+og.header_len+og.body_len;
+ }
+ }
+
+ _seek_helper(next);
+ ret=_get_next_page(&og,-1);
+ if(ret==OV_EREAD)return(OV_EREAD);
+
+ if(searched>=end || ret<0){
+ vf->links=m+1;
+ vf->offsets=(ogg_int64_t*)_ogg_malloc((m+2)*sizeof(*vf->offsets));
+ vf->offsets[m+1]=searched;
+ }else{
+ ret=_bisect_forward_serialno(next,vf->offset,
+ end,ogg_page_serialno(&og),m+1);
+ if(ret==OV_EREAD)return(OV_EREAD);
+ }
+
+ vf->offsets[m]=begin;
+ return(0);
+}
+
+/* uses the local ogg_stream storage in vf; this is important for
+ non-streaming input sources */
+int OggVorbisFile::_fetch_headers(vorbis_info *vi,vorbis_comment *vc,
+ long *serialno,ogg_page *og_ptr){
+ ogg_page og;
+ ogg_packet op;
+ int i,ret=0;
+
+ if(!og_ptr){
+ ret=_get_next_page(&og,CHUNKSIZE);
+ if(ret==OV_EREAD)return(OV_EREAD);
+ if(ret<0)return OV_ENOTVORBIS;
+ og_ptr=&og;
+ }
+
+ if(serialno)*serialno=ogg_page_serialno(og_ptr);
+ ogg_stream_init(&vf->os,ogg_page_serialno(og_ptr));
+ vf->ready_state=STREAMSET;
+
+ /* extract the initial header from the first page and verify that the
+ Ogg bitstream is in fact Vorbis data */
+
+ vorbis_info_init(vi);
+ vorbis_comment_init(vc);
+
+ i=0;
+ while(i<3)
+ {
+ ogg_stream_pagein(&vf->os,og_ptr);
+ while(i<3)
+ {
+ int result=ogg_stream_packetout(&vf->os,&op);
+
+ if(result==0)
+ break;
+ if(result==-1)
+ {
+ ret=OV_EBADHEADER;
+ goto bail_header;
+ }
+
+ if((ret=vorbis_synthesis_headerin(vi,vc,&op)))
+ {
+ goto bail_header;
+ }
+
+ i++;
+ }
+ if(i<3)
+ if(_get_next_page(og_ptr,CHUNKSIZE)<0)
+ {
+ ret=OV_EBADHEADER;
+ goto bail_header;
+ }
+ }
+ return 0;
+
+ bail_header:
+ vorbis_info_clear(vi);
+ vorbis_comment_clear(vc);
+ ogg_stream_clear(&vf->os);
+ vf->ready_state=OPENED;
+
+ return ret;
+}
+
+/* last step of the OggVorbis_File initialization; get all the
+ vorbis_info structs and PCM positions. Only called by the seekable
+ initialization (local stream storage is hacked slightly; pay
+ attention to how that's done) */
+
+/* this is void and does not propogate errors up because we want to be
+ able to open and use damaged bitstreams as well as we can. Just
+ watch out for missing information for links in the OggVorbis_File
+ struct */
+void OggVorbisFile::_prefetch_all_headers(long dataoffset){
+ ogg_page og;
+ int i,ret;
+
+ vf->vi=(vorbis_info*)_ogg_realloc(vf->vi,vf->links*sizeof(*vf->vi));
+ vf->vc=(vorbis_comment*)_ogg_realloc(vf->vc,vf->links*sizeof(*vf->vc));
+ vf->dataoffsets=(ogg_int64_t*)_ogg_malloc(vf->links*sizeof(*vf->dataoffsets));
+ vf->pcmlengths=(ogg_int64_t*)_ogg_malloc(vf->links*sizeof(*vf->pcmlengths));
+ vf->serialnos=(long*)_ogg_malloc(vf->links*sizeof(*vf->serialnos));
+
+ for(i=0;ilinks;i++){
+ if(i==0){
+ /* we already grabbed the initial header earlier. Just set the offset */
+ vf->dataoffsets[i]=dataoffset;
+ }else{
+
+ /* seek to the location of the initial header */
+
+ _seek_helper(vf->offsets[i]);
+ if(_fetch_headers(vf->vi+i,vf->vc+i,NULL,NULL)<0){
+ vf->dataoffsets[i]=-1;
+ }else{
+ vf->dataoffsets[i]=vf->offset;
+ ogg_stream_clear(&vf->os);
+ }
+ }
+
+ /* get the serial number and PCM length of this link. To do this,
+ get the last page of the stream */
+ {
+ long end=vf->offsets[i+1];
+ _seek_helper(end);
+
+ while(1){
+ ret=_get_prev_page(&og);
+ if(ret<0){
+ /* this should not be possible */
+ vorbis_info_clear(vf->vi+i);
+ vorbis_comment_clear(vf->vc+i);
+ break;
+ }
+ if(ogg_page_granulepos(&og)!=-1){
+ vf->serialnos[i]=ogg_page_serialno(&og);
+ vf->pcmlengths[i]=ogg_page_granulepos(&og);
+ break;
+ }
+ vf->offset=ret;
+ }
+ }
+ }
+}
+
+void OggVorbisFile::_make_decode_ready(){
+ if(vf->ready_state!=STREAMSET)return;
+ if(vf->seekable){
+ vorbis_synthesis_init(&vf->vd,vf->vi+vf->current_link);
+ }else{
+ vorbis_synthesis_init(&vf->vd,vf->vi);
+ }
+ vorbis_block_init(&vf->vd,&vf->vb);
+ vf->ready_state=INITSET;
+ return;
+}
+
+int OggVorbisFile::_open_seekable2(){
+ long serialno=vf->current_serialno,end;
+ long dataoffset=vf->offset;
+ ogg_page og;
+
+ /* we're partially open and have a first link header state in
+ storage in vf */
+ /* we can seek, so set out learning all about this file */
+ vf->datasource->setPosition(vf->datasource->getStreamSize());
+ vf->offset=vf->end=vf->datasource->getPosition();
+
+ /* We get the offset for the last page of the physical bitstream.
+ Most OggVorbis files will contain a single logical bitstream */
+ end=_get_prev_page(&og);
+ if(end<0){
+ ov_clear();
+ return(end);
+ }
+
+ /* more than one logical bitstream? */
+ if(ogg_page_serialno(&og)!=serialno){
+
+ /* Chained bitstream. Bisect-search each logical bitstream
+ section. Do so based on serial number only */
+ if(_bisect_forward_serialno(0,0,end+1,serialno,0)<0){
+ ov_clear();
+ return(OV_EREAD);
+ }
+
+ }else{
+
+ /* Only one logical bitstream */
+ if(_bisect_forward_serialno(0,end,end+1,serialno,0)){
+ ov_clear();
+ return(OV_EREAD);
+ }
+
+ }
+
+ /* the initial header memory is referenced by vf after; don't free it */
+ _prefetch_all_headers(dataoffset);
+ return(ov_raw_seek(0));
+}
+
+/* clear out the current logical bitstream decoder */
+void OggVorbisFile::_decode_clear(){
+ ogg_stream_clear(&vf->os);
+ vorbis_dsp_clear(&vf->vd);
+ vorbis_block_clear(&vf->vb);
+ vf->ready_state=OPENED;
+
+ vf->bittrack=0.f;
+ vf->samptrack=0.f;
+}
+
+/* fetch and process a packet. Handles the case where we're at a
+ bitstream boundary and dumps the decoding machine. If the decoding
+ machine is unloaded, it loads it. It also keeps pcm_offset up to
+ date (seek and read both use this. seek uses a special hack with
+ readp).
+
+ return: <0) error, OV_HOLE (lost packet) or OV_EOF
+ 0) need more data (only if readp==0)
+ 1) got a packet
+*/
+
+int OggVorbisFile::_process_packet(int readp){
+ ogg_page og;
+
+ /* handle one packet. Try to fetch it from current stream state */
+ /* extract packets from page */
+ while(1){
+
+ /* process a packet if we can. If the machine isn't loaded,
+ neither is a page */
+ if(vf->ready_state==INITSET){
+ while(1) {
+ ogg_packet op;
+ int result=ogg_stream_packetout(&vf->os,&op);
+ ogg_int64_t granulepos;
+
+ if(result==-1)return(OV_HOLE); /* hole in the data. */
+ if(result>0){
+ /* got a packet. process it */
+ granulepos=op.granulepos;
+ if(!vorbis_synthesis(&vf->vb,&op)){ /* lazy check for lazy
+ header handling. The
+ header packets aren't
+ audio, so if/when we
+ submit them,
+ vorbis_synthesis will
+ reject them */
+
+ /* suck in the synthesis data and track bitrate */
+ {
+ int oldsamples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+ vorbis_synthesis_blockin(&vf->vd,&vf->vb);
+ vf->samptrack+=vorbis_synthesis_pcmout(&vf->vd,NULL)-oldsamples;
+ vf->bittrack+=op.bytes*8;
+ }
+
+ /* update the pcm offset. */
+ if(granulepos!=-1 && !op.e_o_s){
+ int link=(vf->seekable?vf->current_link:0);
+ int i,samples;
+
+ /* this packet has a pcm_offset on it (the last packet
+ completed on a page carries the offset) After processing
+ (above), we know the pcm position of the *last* sample
+ ready to be returned. Find the offset of the *first*
+
+ As an aside, this trick is inaccurate if we begin
+ reading anew right at the last page; the end-of-stream
+ granulepos declares the last frame in the stream, and the
+ last packet of the last page may be a partial frame.
+ So, we need a previous granulepos from an in-sequence page
+ to have a reference point. Thus the !op.e_o_s clause
+ above */
+
+ samples=vorbis_synthesis_pcmout(&vf->vd,NULL);
+
+ granulepos-=samples;
+ for(i=0;ipcmlengths[i];
+ vf->pcm_offset=granulepos;
+ }
+ return(1);
+ }
+ }
+ else
+ break;
+ }
+ }
+
+ if(vf->ready_state>=OPENED){
+ if(!readp)return(0);
+ if(_get_next_page(&og,-1)<0)return(OV_EOF); /* eof.
+ leave unitialized */
+ /* bitrate tracking; add the header's bytes here, the body bytes
+ are done by packet above */
+ vf->bittrack+=og.header_len*8;
+
+ /* has our decoding just traversed a bitstream boundary? */
+ if(vf->ready_state==INITSET){
+ if(vf->current_serialno!=ogg_page_serialno(&og)){
+ _decode_clear();
+
+ if(!vf->seekable){
+ vorbis_info_clear(vf->vi);
+ vorbis_comment_clear(vf->vc);
+ }
+ }
+ }
+ }
+
+ /* Do we need to load a new machine before submitting the page? */
+ /* This is different in the seekable and non-seekable cases.
+
+ In the seekable case, we already have all the header
+ information loaded and cached; we just initialize the machine
+ with it and continue on our merry way.
+
+ In the non-seekable (streaming) case, we'll only be at a
+ boundary if we just left the previous logical bitstream and
+ we're now nominally at the header of the next bitstream
+ */
+
+ if(vf->ready_state!=INITSET){
+ int link;
+
+ if(vf->ready_stateseekable){
+ vf->current_serialno=ogg_page_serialno(&og);
+
+ /* match the serialno to bitstream section. We use this rather than
+ offset positions to avoid problems near logical bitstream
+ boundaries */
+ for(link=0;linklinks;link++)
+ if(vf->serialnos[link]==vf->current_serialno)break;
+ if(link==vf->links)return(OV_EBADLINK); /* sign of a bogus
+ stream. error out,
+ leave machine
+ uninitialized */
+
+ vf->current_link=link;
+
+ ogg_stream_init(&vf->os,vf->current_serialno);
+ ogg_stream_reset(&vf->os);
+ vf->ready_state=STREAMSET;
+
+ }else{
+ /* we're streaming */
+ /* fetch the three header packets, build the info struct */
+
+ int ret=_fetch_headers(vf->vi,vf->vc,&vf->current_serialno,&og);
+ if(ret)return(ret);
+ vf->current_link++;
+ link=0;
+ }
+ }
+
+ _make_decode_ready();
+ }
+ ogg_stream_pagein(&vf->os,&og);
+ }
+}
+
+static int _fseek64_wrap(Stream *stream,ogg_int64_t off,int whence){
+ if(stream==NULL)return(-1);
+
+ switch(whence) {
+ case 0:
+ stream->setPosition(off);
+ return stream->getPosition();
+ break;
+ case 1:
+ case 2:
+ ogg_int64_t pos = stream->getPosition();
+ stream->setPosition(pos + off);
+ return stream->getPosition();
+ break;
+ }
+ return 0;
+}
+
+int OggVorbisFile::_ov_open1(Stream *stream,char *initial,
+ long ibytes){
+ long offset=(stream?stream->getPosition():-1);
+ int ret;
+
+ memset(vf,0,sizeof(*vf));
+ vf->datasource = stream;
+
+ /* init the framing state */
+ ogg_sync_init(&vf->oy);
+
+ /* perhaps some data was previously read into a buffer for testing
+ against other stream types. Allow initialization from this
+ previously read data (as we may be reading from a non-seekable
+ stream) */
+ if(initial){
+ char *buffer=ogg_sync_buffer(&vf->oy,ibytes);
+ memcpy(buffer,initial,ibytes);
+ ogg_sync_wrote(&vf->oy,ibytes);
+ }
+
+ /* can we seek? Stevens suggests the seek test was portable */
+ if(offset!=-1)vf->seekable=1;
+
+ /* No seeking yet; Set up a 'single' (current) logical bitstream
+ entry for partial open */
+ vf->links=1;
+ vf->vi=(vorbis_info*)_ogg_calloc(vf->links,sizeof(*vf->vi));
+ vf->vc=(vorbis_comment*)_ogg_calloc(vf->links,sizeof(*vf->vc));
+
+ /* Try to fetch the headers, maintaining all the storage */
+ if((ret=_fetch_headers(vf->vi,vf->vc,&vf->current_serialno,NULL))<0){
+ vf->datasource=NULL;
+ ov_clear();
+ }else if(vf->ready_state < PARTOPEN)
+ vf->ready_state=PARTOPEN;
+ return(ret);
+}
+
+int OggVorbisFile::_ov_open2(){
+ if(vf->ready_state < OPENED)
+ vf->ready_state=OPENED;
+ if(vf->seekable){
+ int ret=_open_seekable2();
+ if(ret){
+ vf->datasource=NULL;
+ ov_clear();
+ }
+ return(ret);
+ }
+ return 0;
+}
+
+OggVorbisFile::OggVorbisFile() {
+ vf = NULL;
+ vf = new OggVorbis_File();
+}
+
+OggVorbisFile::~OggVorbisFile() {
+ if(vf != NULL)
+ delete(vf);
+}
+
+/* clear out the OggVorbis_File struct */
+int OggVorbisFile::ov_clear(){
+ if(vf){
+ vorbis_block_clear(&vf->vb);
+ vorbis_dsp_clear(&vf->vd);
+ ogg_stream_clear(&vf->os);
+
+ if(vf->vi && vf->links){
+ int i;
+ for(i=0;ilinks;i++){
+ vorbis_info_clear(vf->vi+i);
+ vorbis_comment_clear(vf->vc+i);
+ }
+ _ogg_free(vf->vi);
+ _ogg_free(vf->vc);
+ }
+ if(vf->dataoffsets)_ogg_free(vf->dataoffsets);
+ if(vf->pcmlengths)_ogg_free(vf->pcmlengths);
+ if(vf->serialnos)_ogg_free(vf->serialnos);
+ if(vf->offsets)_ogg_free(vf->offsets);
+ ogg_sync_clear(&vf->oy);
+// if(vf->datasource)vf->datasource->close();
+ memset(vf,0,sizeof(*vf));
+ }
+#ifdef DEBUG_LEAKS
+ _VDBG_dump();
+#endif
+ return(0);
+}
+
+/* inspects the OggVorbis file and finds/documents all the logical
+ bitstreams contained in it. Tries to be tolerant of logical
+ bitstream sections that are truncated/woogie.
+
+ return: -1) error
+ 0) OK
+*/
+
+int OggVorbisFile::ov_open_callbacks(Stream *f,char *initial,long ibytes){
+ int ret=_ov_open1(f,initial,ibytes);
+ if(ret)return ret;
+ return _ov_open2();
+}
+
+int OggVorbisFile::ov_open(Stream *stream,char *initial,long ibytes){
+ return ov_open_callbacks(stream, initial, ibytes);
+}
+
+/* Only partially open the vorbis file; test for Vorbisness, and load
+ the headers for the first chain. Do not seek (although test for
+ seekability). Use ov_test_open to finish opening the file, else
+ ov_clear to close/free it. Same return codes as open. */
+
+int OggVorbisFile::ov_test_callbacks(Stream *f,char *initial,long ibytes)
+{
+ return _ov_open1(f,initial,ibytes);
+}
+
+int OggVorbisFile::ov_test(Stream *stream,char *initial,long ibytes){
+ return ov_test_callbacks(stream, initial, ibytes);
+}
+
+int OggVorbisFile::ov_test_open(){
+ if(vf->ready_state!=PARTOPEN)return(OV_EINVAL);
+ return _ov_open2();
+}
+
+/* How many logical bitstreams in this physical bitstream? */
+long OggVorbisFile::ov_streams(){
+ return vf->links;
+}
+
+/* Is the FILE * associated with vf seekable? */
+long OggVorbisFile::ov_seekable(){
+ return vf->seekable;
+}
+
+/* returns the bitrate for a given logical bitstream or the entire
+ physical bitstream. If the file is open for random access, it will
+ find the *actual* average bitrate. If the file is streaming, it
+ returns the nominal bitrate (if set) else the average of the
+ upper/lower bounds (if set) else -1 (unset).
+
+ If you want the actual bitrate field settings, get them from the
+ vorbis_info structs */
+
+long OggVorbisFile::ov_bitrate(int i){
+ if(vf->ready_state=vf->links)return(OV_EINVAL);
+ if(!vf->seekable && i!=0)return(ov_bitrate(0));
+ if(i<0){
+ ogg_int64_t bits=0;
+ int i;
+ for(i=0;ilinks;i++)
+ bits+=(vf->offsets[i+1]-vf->dataoffsets[i])*8;
+ return(floor( (bits/ov_time_total(-1)) + 0.5f ));
+ }else{
+ if(vf->seekable){
+ /* return the actual bitrate */
+ return(floor( ((vf->offsets[i+1]-vf->dataoffsets[i])*8/ov_time_total(i)) + 0.5f ));
+ }else{
+ /* return nominal if set */
+ if(vf->vi[i].bitrate_nominal>0){
+ return vf->vi[i].bitrate_nominal;
+ }else{
+ if(vf->vi[i].bitrate_upper>0){
+ if(vf->vi[i].bitrate_lower>0){
+ return (vf->vi[i].bitrate_upper+vf->vi[i].bitrate_lower)/2;
+ }else{
+ return vf->vi[i].bitrate_upper;
+ }
+ }
+ return(OV_FALSE);
+ }
+ }
+ }
+}
+
+/* returns the actual bitrate since last call. returns -1 if no
+ additional data to offer since last call (or at beginning of stream),
+ EINVAL if stream is only partially open
+*/
+long OggVorbisFile::ov_bitrate_instant(){
+ int link=(vf->seekable?vf->current_link:0);
+ long ret;
+ if(vf->ready_statesamptrack==0)return(OV_FALSE);
+ ret=vf->bittrack/vf->samptrack*vf->vi[link].rate+.5;
+ vf->bittrack=0.f;
+ vf->samptrack=0.f;
+ return(ret);
+}
+
+/* Guess */
+long OggVorbisFile::ov_serialnumber(int i){
+ if(i>=vf->links)return(ov_serialnumber(vf->links-1));
+ if(!vf->seekable && i>=0)return(ov_serialnumber(-1));
+ if(i<0){
+ return(vf->current_serialno);
+ }else{
+ return(vf->serialnos[i]);
+ }
+}
+
+/* returns: total raw (compressed) length of content if i==-1
+ raw (compressed) length of that logical bitstream for i==0 to n
+ OV_EINVAL if the stream is not seekable (we can't know the length)
+ or if stream is only partially open
+*/
+ogg_int64_t OggVorbisFile::ov_raw_total(int i){
+ if(vf->ready_stateseekable || i>=vf->links)return(OV_EINVAL);
+ if(i<0){
+ long acc=0;
+ int i;
+ for(i=0;ilinks;i++)
+ acc+=ov_raw_total(i);
+ return(acc);
+ }else{
+ return(vf->offsets[i+1]-vf->offsets[i]);
+ }
+}
+
+/* returns: total PCM length (samples) of content if i==-1 PCM length
+ (samples) of that logical bitstream for i==0 to n
+ OV_EINVAL if the stream is not seekable (we can't know the
+ length) or only partially open
+*/
+ogg_int64_t OggVorbisFile::ov_pcm_total(int i)
+{
+ if(vf->ready_state < OPENED)
+ return(OV_EINVAL);
+ if(!vf->seekable || i>=vf->links)
+ return(OV_EINVAL);
+
+ if(i<0)
+ {
+ ogg_int64_t acc=0;
+ int i;
+ for(i=0;ilinks;i++)
+ acc+=ov_pcm_total(i);
+ return (acc);
+ }
+ else
+ {
+ return (vf->pcmlengths[i]);
+ }
+}
+
+/* returns: total seconds of content if i==-1
+ seconds in that logical bitstream for i==0 to n
+ OV_EINVAL if the stream is not seekable (we can't know the
+ length) or only partially open
+*/
+double OggVorbisFile::ov_time_total(int i)
+{
+ if(vf->ready_state < OPENED)
+ return(OV_EINVAL);
+
+ if(!vf->seekable || i>=vf->links)
+ return(OV_EINVAL);
+ if(i<0)
+ {
+ F64 acc=0;
+ S32 i;
+ for(i=0;ilinks;i++)
+ acc += ov_time_total(i);
+ return(acc);
+ }
+ else
+ {
+ return((float)(vf->pcmlengths[i])/vf->vi[i].rate);
+ }
+}
+
+/* seek to an offset relative to the *compressed* data. This also
+ scans packets to update the PCM cursor. It will cross a logical
+ bitstream boundary, but only if it can't get any packets out of the
+ tail of the bitstream we seek to (so no surprises).
+
+ returns zero on success, nonzero on failure */
+
+int OggVorbisFile::ov_raw_seek(long pos)
+{
+ ogg_stream_state work_os;
+
+ if(vf->ready_stateseekable)
+ return(OV_ENOSEEK); /* don't dump machine if we can't seek */
+
+ if(pos<0 || pos>vf->offsets[vf->links])
+ return(OV_EINVAL);
+
+ /* clear out decoding machine state */
+ vf->pcm_offset=-1;
+ _decode_clear();
+
+ _seek_helper(pos);
+
+ /* we need to make sure the pcm_offset is set, but we don't want to
+ advance the raw cursor past good packets just to get to the first
+ with a granulepos. That's not equivalent behavior to beginning
+ decoding as immediately after the seek position as possible.
+
+ So, a hack. We use two stream states; a local scratch state and
+ a the shared vf->os stream state. We use the local state to
+ scan, and the shared state as a buffer for later decode.
+
+ Unfortunately, on the last page we still advance to last packet
+ because the granulepos on the last page is not necessarily on a
+ packet boundary, and we need to make sure the granpos is
+ correct.
+ */
+
+ {
+ ogg_page og;
+ ogg_packet op;
+ int lastblock=0;
+ int accblock=0;
+ int thisblock;
+ int eosflag;
+
+ memset(&work_os,0,sizeof(work_os));/* so that it's safe to clear
+ it later even if we don't
+ init it */
+
+ while(1){
+ if(vf->ready_state==STREAMSET){
+ /* snarf/scan a packet if we can */
+ int result=ogg_stream_packetout(&work_os,&op);
+
+ if(result>0){
+
+ if(vf->vi[vf->current_link].codec_setup)
+ thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
+ if(eosflag)
+ ogg_stream_packetout(&vf->os,NULL);
+ else
+ if(lastblock)accblock+=(lastblock+thisblock)>>2;
+
+ if(op.granulepos!=-1){
+ int i,link=vf->current_link;
+ ogg_int64_t granulepos=op.granulepos;
+
+ for(i=0;ipcmlengths[i];
+ vf->pcm_offset=granulepos-accblock;
+ break;
+ }
+ lastblock=thisblock;
+ continue;
+ }
+ }
+
+ if(!lastblock){
+ if(_get_next_page(&og,-1)<0){
+ vf->pcm_offset=ov_pcm_total(-1);
+ break;
+ }
+ }else{
+ /* huh? Bogus stream with packets but no granulepos */
+ vf->pcm_offset=-1;
+ break;
+ }
+
+ /* has our decoding just traversed a bitstream boundary? */
+ if(vf->ready_state==STREAMSET)
+ if(vf->current_serialno!=ogg_page_serialno(&og)){
+ _decode_clear(); /* clear out stream state */
+ ogg_stream_clear(&work_os);
+ }
+
+ if(vf->ready_statecurrent_serialno=ogg_page_serialno(&og);
+ for(link=0;linklinks;link++)
+ if(vf->serialnos[link]==vf->current_serialno)break;
+ if(link==vf->links)goto seek_error; /* sign of a bogus stream.
+ error out, leave
+ machine uninitialized */
+ vf->current_link=link;
+
+ ogg_stream_init(&vf->os,vf->current_serialno);
+ ogg_stream_reset(&vf->os);
+ ogg_stream_init(&work_os,vf->current_serialno);
+ ogg_stream_reset(&work_os);
+ vf->ready_state=STREAMSET;
+
+ }
+
+ ogg_stream_pagein(&vf->os,&og);
+ ogg_stream_pagein(&work_os,&og);
+ eosflag=ogg_page_eos(&og);
+ }
+ }
+
+ ogg_stream_clear(&work_os);
+ return(0);
+
+ seek_error:
+ /* dump the machine so we're in a known state */
+ vf->pcm_offset=-1;
+ ogg_stream_clear(&work_os);
+ _decode_clear();
+ return OV_EBADLINK;
+}
+
+/* Page granularity seek (faster than sample granularity because we
+ don't do the last bit of decode to find a specific sample).
+
+ Seek to the last [granule marked] page preceeding the specified pos
+ location, such that decoding past the returned point will quickly
+ arrive at the requested position. */
+int OggVorbisFile::ov_pcm_seek_page(ogg_int64_t pos){
+ int link=-1;
+ long ret;
+ ogg_int64_t total=ov_pcm_total(-1);
+
+ if(vf->ready_stateseekable)return(OV_ENOSEEK);
+ if(pos<0 || pos>total)return(OV_EINVAL);
+
+ /* which bitstream section does this pcm offset occur in? */
+ for(link=vf->links-1;link>=0;link--){
+ total-=vf->pcmlengths[link];
+ if(pos>=total)break;
+ }
+
+ /* search within the logical bitstream for the page with the highest
+ pcm_pos preceeding (or equal to) pos. There is a danger here;
+ missing pages or incorrect frame number information in the
+ bitstream could make our task impossible. Account for that (it
+ would be an error condition) */
+
+ /* new search algorithm by HB (Nicholas Vinen) */
+ {
+ ogg_int64_t target=pos-total;
+ long end=vf->offsets[link+1];
+ long begin=vf->offsets[link];
+ ogg_int64_t endtime = vf->pcmlengths[link];
+ ogg_int64_t begintime = 0;
+ long best=begin;
+
+ ogg_page og;
+ while(beginoffset; /* raw offset of next page */
+ begintime=granulepos;
+
+ if(target-begin>44100)break;
+ bisect=begin; /* *not* begin + 1 */
+ }else{
+ if(bisect<=begin+1)
+ end=begin; /* found it */
+ else{
+ if(end==vf->offset){ /* we're pretty close - we'd be stuck in */
+ end=ret;
+ bisect-=CHUNKSIZE; /* an endless loop otherwise. */
+ if(bisect<=begin)bisect=begin+1;
+ _seek_helper(bisect);
+ }else{
+ end=ret;
+ endtime=granulepos;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* found our page. seek to it, update pcm offset. Easier case than
+ raw_seek, don't keep packets preceeding granulepos. */
+ {
+ ogg_page og;
+ ogg_packet op;
+ /* clear out decoding machine state */
+ _decode_clear();
+ /* seek */
+ _seek_helper(best);
+
+ if(_get_next_page(&og,-1)<0)return(OV_EOF); /* shouldn't happen */
+ vf->current_serialno=ogg_page_serialno(&og);
+ vf->current_link=link;
+
+ ogg_stream_init(&vf->os,vf->current_serialno);
+ ogg_stream_reset(&vf->os);
+ vf->ready_state=STREAMSET;
+ ogg_stream_pagein(&vf->os,&og);
+
+ /* pull out all but last packet; the one with granulepos */
+ while(1){
+ ret=ogg_stream_packetpeek(&vf->os,&op);
+ if(ret==0){
+ /* !!! the packet finishing this page originated on a
+ preceeding page. Keep fetching previous pages until we
+ get one with a granulepos or without the 'continued' flag
+ set. Then just use raw_seek for simplicity. */
+ while(1){
+ ret=_get_prev_page(&og);
+ if(ret<0)goto seek_error;
+ if(ogg_page_granulepos(&og)>-1 ||
+ !ogg_page_continued(&og)){
+ return ov_raw_seek(ret);
+ }
+ vf->offset=ret;
+ }
+ }
+ if(ret<0)goto seek_error;
+ if(op.granulepos!=-1){
+ vf->pcm_offset=op.granulepos+total;
+ break;
+ }else
+ ret=ogg_stream_packetout(&vf->os,NULL);
+ }
+ }
+ }
+
+ /* verify result */
+ if(vf->pcm_offset>pos || pos>ov_pcm_total(-1)){
+ ret=OV_EFAULT;
+ goto seek_error;
+ }
+ return(0);
+
+ seek_error:
+ /* dump machine so we're in a known state */
+ vf->pcm_offset=-1;
+ _decode_clear();
+ return ret;
+}
+
+/* seek to a sample offset relative to the decompressed pcm stream
+ returns zero on success, nonzero on failure */
+
+int OggVorbisFile::ov_pcm_seek(ogg_int64_t pos){
+ int thisblock,lastblock=0;
+ int ret=ov_pcm_seek_page(pos);
+ if(ret<0)return(ret);
+
+ /* discard leading packets we don't need for the lapping of the
+ position we want; don't decode them */
+
+ while(1){
+ ogg_packet op;
+ ogg_page og;
+
+ int ret=ogg_stream_packetpeek(&vf->os,&op);
+ if(ret>0){
+ thisblock=vorbis_packet_blocksize(vf->vi+vf->current_link,&op);
+ if(lastblock)vf->pcm_offset+=(lastblock+thisblock)>>2;
+
+ if(vf->pcm_offset+((thisblock+
+ vorbis_info_blocksize(vf->vi,1))>>2)>=pos)break;
+
+ ogg_stream_packetout(&vf->os,NULL);
+
+
+ /* end of logical stream case is hard, especially with exact
+ length positioning. */
+
+ if(op.granulepos>-1){
+ int i;
+ /* always believe the stream markers */
+ vf->pcm_offset=op.granulepos;
+ for(i=0;icurrent_link;i++)
+ vf->pcm_offset+=vf->pcmlengths[i];
+ }
+
+ lastblock=thisblock;
+
+ }else{
+ if(ret<0 && ret!=OV_HOLE)break;
+
+ /* suck in a new page */
+ if(_get_next_page(&og,-1)<0)break;
+ if(vf->current_serialno!=ogg_page_serialno(&og))_decode_clear();
+
+ if(vf->ready_statecurrent_serialno=ogg_page_serialno(&og);
+ for(link=0;linklinks;link++)
+ if(vf->serialnos[link]==vf->current_serialno)break;
+ if(link==vf->links)return(OV_EBADLINK);
+ vf->current_link=link;
+
+ ogg_stream_init(&vf->os,vf->current_serialno);
+ ogg_stream_reset(&vf->os);
+ vf->ready_state=STREAMSET;
+ lastblock=0;
+ }
+ ogg_stream_pagein(&vf->os,&og);
+ }
+ }
+
+ /* discard samples until we reach the desired position. Crossing a
+ logical bitstream boundary with abandon is OK. */
+ _make_decode_ready();
+ while(vf->pcm_offsetpcm_offset;
+ long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+
+ if(samples>target)samples=target;
+ vorbis_synthesis_read(&vf->vd,samples);
+ vf->pcm_offset+=samples;
+
+ if(samplespcm_offset=ov_pcm_total(-1); /* eof */
+ }
+ return 0;
+}
+
+/* seek to a playback time relative to the decompressed pcm stream
+ returns zero on success, nonzero on failure */
+int OggVorbisFile::ov_time_seek(double seconds){
+ /* translate time to PCM position and call ov_pcm_seek */
+
+ int link=-1;
+ ogg_int64_t pcm_total=ov_pcm_total(-1);
+ double time_total=ov_time_total(-1);
+
+ if(vf->ready_stateseekable)return(OV_ENOSEEK);
+ if(seconds<0 || seconds>time_total)return(OV_EINVAL);
+
+ /* which bitstream section does this time offset occur in? */
+ for(link=vf->links-1;link>=0;link--){
+ pcm_total-=vf->pcmlengths[link];
+ time_total-=ov_time_total(link);
+ if(seconds>=time_total)break;
+ }
+
+ /* enough information to convert time offset to pcm offset */
+ {
+ ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
+ return(ov_pcm_seek(target));
+ }
+}
+
+/* page-granularity version of ov_time_seek
+ returns zero on success, nonzero on failure */
+int OggVorbisFile::ov_time_seek_page(double seconds){
+ /* translate time to PCM position and call ov_pcm_seek */
+
+ int link=-1;
+ ogg_int64_t pcm_total=ov_pcm_total(-1);
+ double time_total=ov_time_total(-1);
+
+ if(vf->ready_stateseekable)return(OV_ENOSEEK);
+ if(seconds<0 || seconds>time_total)return(OV_EINVAL);
+
+ /* which bitstream section does this time offset occur in? */
+ for(link=vf->links-1;link>=0;link--){
+ pcm_total-=vf->pcmlengths[link];
+ time_total-=ov_time_total(link);
+ if(seconds>=time_total)break;
+ }
+
+ /* enough information to convert time offset to pcm offset */
+ {
+ ogg_int64_t target=pcm_total+(seconds-time_total)*vf->vi[link].rate;
+ return(ov_pcm_seek_page(target));
+ }
+}
+
+/* tell the current stream offset cursor. Note that seek followed by
+ tell will likely not give the set offset due to caching */
+ogg_int64_t OggVorbisFile::ov_raw_tell(){
+ if(vf->ready_stateoffset);
+}
+
+/* return PCM offset (sample) of next PCM sample to be read */
+ogg_int64_t OggVorbisFile::ov_pcm_tell(){
+ if(vf->ready_statepcm_offset);
+}
+
+/* return time offset (seconds) of next PCM sample to be read */
+double OggVorbisFile::ov_time_tell()
+{
+ /* translate time to PCM position and call ov_pcm_seek */
+
+ int link=-1;
+ ogg_int64_t pcm_total=0;
+ double time_total=0.f;
+
+ if(vf->ready_stateseekable)
+ {
+ pcm_total=ov_pcm_total(-1);
+ time_total=ov_time_total(-1);
+
+ /* which bitstream section does this time offset occur in? */
+ for(link=vf->links-1;link>=0;link--)
+ {
+ pcm_total -= vf->pcmlengths[link];
+ time_total -= ov_time_total(link);
+ if(vf->pcm_offset >= pcm_total)
+ break;
+ }
+ }
+
+ return((double)time_total+(double)(vf->pcm_offset-pcm_total)/vf->vi[link].rate);
+}
+
+/* link: -1) return the vorbis_info struct for the bitstream section
+ currently being decoded
+ 0-n) to request information for a specific bitstream section
+
+ In the case of a non-seekable bitstream, any call returns the
+ current bitstream. NULL in the case that the machine is not
+ initialized */
+
+vorbis_info* OggVorbisFile::ov_info(int link){
+ if(vf->seekable){
+ if(link<0)
+ if(vf->ready_state>=STREAMSET)
+ return vf->vi+vf->current_link;
+ else
+ return vf->vi;
+ else
+ if(link>=vf->links)
+ return NULL;
+ else
+ return vf->vi+link;
+ }else{
+ return vf->vi;
+ }
+}
+
+/* grr, strong typing, grr, no templates/inheritence, grr */
+vorbis_comment* OggVorbisFile::ov_comment(int link){
+ if(vf->seekable){
+ if(link<0)
+ if(vf->ready_state>=STREAMSET)
+ return vf->vc+vf->current_link;
+ else
+ return vf->vc;
+ else
+ if(link>=vf->links)
+ return NULL;
+ else
+ return vf->vc+link;
+ }else{
+ return vf->vc;
+ }
+}
+
+static int host_is_big_endian() {
+ ogg_int32_t pattern = 0xfeedface; /* deadbeef */
+ unsigned char *bytewise = (unsigned char *)&pattern;
+ if (bytewise[0] == 0xfe) return 1;
+ return 0;
+}
+
+/* up to this point, everything could more or less hide the multiple
+ logical bitstream nature of chaining from the toplevel application
+ if the toplevel application didn't particularly care. However, at
+ the point that we actually read audio back, the multiple-section
+ nature must surface: Multiple bitstream sections do not necessarily
+ have to have the same number of channels or sampling rate.
+
+ ov_read returns the sequential logical bitstream number currently
+ being decoded along with the PCM data in order that the toplevel
+ application can take action on channel/sample rate changes. This
+ number will be incremented even for streamed (non-seekable) streams
+ (for seekable streams, it represents the actual logical bitstream
+ index within the physical bitstream. Note that the accessor
+ functions above are aware of this dichotomy).
+
+ input values: buffer) a buffer to hold packed PCM data for return
+ length) the byte length requested to be placed into buffer
+ bigendianp) should the data be packed LSB first (0) or
+ MSB first (1)
+ word) word size for output. currently 1 (byte) or
+ 2 (16 bit short)
+
+ return values: <0) error/hole in data (OV_HOLE), partial open (OV_EINVAL)
+ 0) EOF
+ n) number of bytes of PCM actually returned. The
+ below works on a packet-by-packet basis, so the
+ return length is not related to the 'length' passed
+ in, just guaranteed to fit.
+
+ *section) set to the logical bitstream number */
+
+long OggVorbisFile::ov_read_float(float ***pcm_channels,int *bitstream){
+
+ if(vf->ready_stateready_state>=STREAMSET){
+ float **pcm;
+ long samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+ if(samples){
+ if(pcm_channels)*pcm_channels=pcm;
+ vorbis_synthesis_read(&vf->vd,samples);
+ vf->pcm_offset+=samples;
+ if(bitstream)*bitstream=vf->current_link;
+ return samples;
+
+ }
+ }
+
+ /* suck in another packet */
+ {
+ int ret=_process_packet(1);
+ if(ret==OV_EOF)return(0);
+ if(ret<=0)return(ret);
+ }
+
+ }
+}
+
+long OggVorbisFile::ov_read(char *buffer,int length,
+ int bigendianp,int *bitstream) {
+ int i,j;
+ int host_endian = host_is_big_endian();
+
+// int sgned = 1;
+
+ float **pcm;
+ long samples;
+
+ if(vf->ready_stateready_state>=STREAMSET) {
+ samples=vorbis_synthesis_pcmout(&vf->vd,&pcm);
+ if(samples)
+ break;
+ }
+
+ /* suck in another packet */
+ {
+ int ret=_process_packet(1);
+ if(ret==OV_EOF)
+ return(0);
+ if(ret<=0)
+ return(ret);
+ }
+ }
+
+ if(samples>0) {
+
+ /* yay! proceed to pack data into the byte buffer */
+
+ long channels=ov_info(-1)->channels;
+ long bytespersample=2 * channels;
+ if(samples>length/bytespersample)
+ samples=length/bytespersample;
+
+ /* a tight loop to pack each size */
+ {
+ int val;
+
+ int off = 32768;
+
+ if(host_endian==bigendianp) {
+
+ for(i=0;i32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ *dest=val;
+ dest+=channels;
+ }
+ }
+ } else if(bigendianp) {
+ for(j=0;j32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ val+=off;
+ *buffer++=(val>>8);
+ *buffer++=(val&0xff);
+ }
+ }
+ } else {
+ int val;
+ for(j=0;j32767)
+ val=32767;
+ else if(val<-32768)
+ val=-32768;
+ val+=off;
+ *buffer++=(val&0xff);
+ *buffer++=(val>>8);
+ }
+ }
+ }
+ }
+
+ vorbis_synthesis_read(&vf->vd,samples);
+ vf->pcm_offset+=samples;
+ if(bitstream)
+ *bitstream=vf->current_link;
+ return(samples*bytespersample);
+ } else {
+ return(samples);
+ }
+}
diff --git a/engine/audio/vorbisStream.h b/engine/audio/vorbisStream.h
new file mode 100755
index 0000000..9ed2c48
--- /dev/null
+++ b/engine/audio/vorbisStream.h
@@ -0,0 +1,156 @@
+/********************************************************************
+ * *
+ * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE. *
+ * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS *
+ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
+ * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. *
+ * *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001 *
+ * by the XIPHOPHORUS Company http://www.xiph.org/ *
+ * *
+ ********************************************************************
+
+ function: stdio-based convenience library for opening/seeking/decoding
+
+ ********************************************************************/
+
+// modified vorbisfile to use Torque Streams
+// Kurtis Seebaldt
+
+
+#ifndef _OV_FILE_H_
+#define _OV_FILE_H_
+
+#include
+#include "vorbis/codec.h"
+
+#ifndef _PLATFORM_H_
+#include "platform/platform.h"
+#endif
+#ifndef _RESMANAGER_H_
+#include "core/resManager.h"
+#endif
+
+/* The function prototypes for the callbacks are basically the same as for
+ * the stdio functions fread, fseek, fclose, ftell.
+ * The one difference is that the FILE * arguments have been replaced with
+ * a void * - this is to be used as a pointer to whatever internal data these
+ * functions might need. In the stdio case, it's just a FILE * cast to a void *
+ *
+ * If you use other functions, check the docs for these functions and return
+ * the right values. For seek_func(), you *MUST* return -1 if the stream is
+ * unseekable
+ */
+//typedef struct {
+// size_t (*read_func) (Stream *ptr, size_t size, size_t nmemb, void *datasource);
+// int (*seek_func) (Stream *datasource, ogg_int64_t offset, int whence);
+// int (*close_func) (void *datasource);
+// long (*tell_func) (void *datasource);
+//} ov_callbacks;
+
+#define NOTOPEN 0
+#define PARTOPEN 1
+#define OPENED 2
+#define STREAMSET 3
+#define INITSET 4
+
+typedef struct OggVorbis_File {
+ Stream *datasource; /* Pointer to a FILE *, etc. */
+ int seekable;
+ ogg_int64_t offset;
+ ogg_int64_t end;
+ ogg_sync_state oy;
+
+ /* If the FILE handle isn't seekable (eg, a pipe), only the current
+ stream appears */
+ int links;
+ ogg_int64_t *offsets;
+ ogg_int64_t *dataoffsets;
+ long *serialnos;
+ ogg_int64_t *pcmlengths;
+ vorbis_info *vi;
+ vorbis_comment *vc;
+
+ /* Decoding working state local storage */
+ ogg_int64_t pcm_offset;
+ int ready_state;
+ long current_serialno;
+ int current_link;
+
+ double bittrack;
+ double samptrack;
+
+ ogg_stream_state os; /* take physical pages, weld into a logical
+ stream of packets */
+ vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
+ vorbis_block vb; /* local working space for packet->PCM decode */
+
+// ov_callbacks callbacks;
+
+} OggVorbis_File;
+
+
+class OggVorbisFile {
+
+public:
+ OggVorbisFile();
+ ~OggVorbisFile();
+ int ov_clear();
+ int ov_open(Stream *stream,char *initial,long ibytes);
+ int ov_open_callbacks(Stream *datasource,
+ char *initial, long ibytes);
+
+ int ov_test(Stream *stream,char *initial,long ibytes);
+ int ov_test_callbacks(Stream *datasource,
+ char *initial, long ibytes);
+ int ov_test_open();
+
+ long ov_bitrate(int i);
+ long ov_bitrate_instant();
+ long ov_streams();
+ long ov_seekable();
+ long ov_serialnumber(int i);
+
+ ogg_int64_t ov_raw_total(int i);
+ ogg_int64_t ov_pcm_total(int i);
+ double ov_time_total(int i);
+
+ int ov_raw_seek(long pos);
+ int ov_pcm_seek(ogg_int64_t pos);
+ int ov_pcm_seek_page(ogg_int64_t pos);
+ int ov_time_seek(double pos);
+ int ov_time_seek_page(double pos);
+
+ ogg_int64_t ov_raw_tell();
+ ogg_int64_t ov_pcm_tell();
+ double ov_time_tell();
+
+ vorbis_info *ov_info(int link);
+ vorbis_comment *ov_comment(int link);
+
+ long ov_read_float(float ***pcm_channels,
+ int *bitstream);
+ long ov_read(char *buffer,int length,
+ int bigendianp,int *bitstream);
+
+private:
+ OggVorbis_File *vf;
+
+ long _get_data();
+ void _seek_helper(long offset);
+ long _get_next_page(ogg_page *og,int boundary);
+ long _get_prev_page(ogg_page *og);
+ int _bisect_forward_serialno(long begin,long searched,long end,long currentno,long m);
+ int _fetch_headers(vorbis_info *vi,vorbis_comment *vc,long *serialno,ogg_page *og_ptr);
+ void _prefetch_all_headers(long dataoffset);
+ void _make_decode_ready();
+ int _open_seekable2();
+ void _decode_clear();
+ int _process_packet(int readp);
+ int _fseek64_wrap(Stream *stream,ogg_int64_t off,int whence);
+ int _ov_open1(Stream *stream,char *initial,long ibytes);
+ int _ov_open2();
+
+};
+
+#endif
diff --git a/engine/audio/vorbisStreamSource.cc b/engine/audio/vorbisStreamSource.cc
new file mode 100755
index 0000000..cd26c4d
--- /dev/null
+++ b/engine/audio/vorbisStreamSource.cc
@@ -0,0 +1,442 @@
+//--------------------------------------
+// vorbisStreamSource.cc
+// implementation of streaming audio source
+//
+// Kurtis Seebaldt
+//--------------------------------------
+
+#include "audio/vorbisStreamSource.h"
+#include "vorbis/codec.h"
+
+#define BUFFERSIZE 32768
+#define CHUNKSIZE 4096
+
+#if defined(TORQUE_BIG_ENDIAN)
+#define ENDIAN 1
+#else
+#define ENDIAN 0
+#endif
+
+extern const char * MusicPlayerStreamingHook(const AUDIOHANDLE & handle);
+
+VorbisStreamSource::VorbisStreamSource(const char *filename)
+{
+ stream = NULL;
+ bIsValid = false;
+ bBuffersAllocated = false;
+ bVorbisFileInitialized = false;
+ mBufferList[0] = 0;
+ clear();
+
+ mFilename = filename;
+ mPosition = Point3F(0.f,0.f,0.f);
+}
+
+VorbisStreamSource::~VorbisStreamSource()
+{
+ if(bReady && bIsValid)
+ freeStream();
+}
+
+void VorbisStreamSource::clear()
+{
+ if(stream)
+ freeStream();
+
+ mHandle = NULL_AUDIOHANDLE;
+ mSource = NULL;
+
+ if(mBufferList[0] != 0)
+ alDeleteBuffers(NUMBUFFERS, mBufferList);
+ for(int i = 0; i < NUMBUFFERS; i++)
+ mBufferList[i] = 0;
+
+ dMemset(&mDescription, 0, sizeof(Audio::Description));
+ mEnvironment = 0;
+ mPosition.set(0.f,0.f,0.f);
+ mDirection.set(0.f,1.f,0.f);
+ mPitch = 1.f;
+ mScore = 0.f;
+ mCullTime = 0;
+
+ bReady = false;
+ bFinishedPlaying = false;
+ bIsValid = false;
+ bBuffersAllocated = false;
+ bVorbisFileInitialized = false;
+}
+
+bool VorbisStreamSource::initStream()
+{
+ vorbis_info *vi;
+
+ ALint error;
+
+ bFinished = false;
+
+ // JMQ: changed buffer to static and doubled size. workaround for
+ // https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
+ static char data[BUFFERSIZE*2];
+
+ alSourceStop(mSource);
+ alSourcei(mSource, AL_BUFFER, 0);
+
+ stream = ResourceManager->openStream(mFilename);
+ if(stream != NULL)
+ {
+ if(vf.ov_open(stream, NULL, 0) < 0)
+ {
+ return false;
+ }
+
+ bVorbisFileInitialized = true;
+
+ //Read Vorbis File Info
+ vi = vf.ov_info(-1);
+ freq = vi->rate;
+
+ long samples = (long)vf.ov_pcm_total(-1);
+
+ if(vi->channels == 1)
+ {
+ format = AL_FORMAT_MONO16;
+ DataSize = 2 * samples;
+ }
+ else
+ {
+ format = AL_FORMAT_STEREO16;
+ DataSize = 4 * samples;
+ }
+ DataLeft = DataSize;
+
+ // Clear Error Code
+ alGetError();
+
+ alGenBuffers(NUMBUFFERS, mBufferList);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ bBuffersAllocated = true;
+
+ int numBuffers = 0;
+ for(int loop = 0; loop < NUMBUFFERS; loop++)
+ {
+ ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
+ if (DataToRead == DataLeft)
+ bFinished = AL_TRUE;
+
+ long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
+ if(ret <= 0)
+ {
+ bFinished = AL_TRUE;
+ break;
+ }
+
+ DataLeft -= ret;
+ alBufferData(mBufferList[loop], format, data, ret, freq);
+ ++numBuffers;
+
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+ if(bFinished)
+ break;
+ }
+
+ // Queue the buffers on the source
+ alSourceQueueBuffers(mSource, NUMBUFFERS, mBufferList);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ alSourcei(mSource, AL_LOOPING, AL_FALSE);
+ bReady = true;
+ }
+ else
+ {
+ return false;
+ }
+ bIsValid = true;
+
+ return true;
+}
+
+bool VorbisStreamSource::updateBuffers()
+{
+ ALint processed;
+ ALuint BufferID;
+ ALint error;
+ // JMQ: changed buffer to static and doubled size. workaround for
+ // https://206.163.64.242/mantis/view_bug_page.php?f_id=0000242
+ static char data[BUFFERSIZE*2];
+
+ // don't do anything if stream not loaded properly
+ if(!bIsValid)
+ return false;
+
+ if(bFinished && mDescription.mIsLooping)
+ resetStream();
+
+ // reset AL error code
+ alGetError();
+
+#if 1 //def TORQUE_OS_LINUX
+ // JMQ: this doesn't really help on linux. it may make things worse.
+ // if it doesn't help on mac/win either, could disable it.
+ ALint state;
+ alGetSourcei(mSource, AL_SOURCE_STATE, &state);
+ if (state == AL_STOPPED)
+ {
+ // um, yeah. you should be playing
+ // restart
+ alSourcePlay(mSource);
+ //#ifdef TORQUE_DEBUG
+ //Con::errorf(">><<>><< THE MUSIC STOPPED >><<>><<");
+ //#endif
+ return true;
+ }
+#endif
+
+#ifdef TORQUE_OS_LINUX
+ checkPosition();
+#endif
+
+ // Get status
+ alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
+
+ // If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
+ if (processed > 0)
+ {
+ while (processed)
+ {
+ alSourceUnqueueBuffers(mSource, 1, &BufferID);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ if (!bFinished)
+ {
+ ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
+
+ if (DataToRead == DataLeft)
+ bFinished = AL_TRUE;
+
+ long ret = oggRead(data, BUFFERSIZE, ENDIAN, ¤t_section);
+ if(ret > 0)
+ {
+ DataLeft -= ret;
+
+ alBufferData(BufferID, format, data, ret, freq);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ // Queue buffer
+ alSourceQueueBuffers(mSource, 1, &BufferID);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+ }
+
+ processed--;
+
+ if(bFinished && mDescription.mIsLooping)
+ {
+ resetStream();
+ }
+ }
+ else
+ {
+ buffersinqueue--;
+ processed--;
+
+ if (buffersinqueue == 0)
+ {
+ bFinishedPlaying = AL_TRUE;
+ return AL_FALSE;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void VorbisStreamSource::freeStream()
+{
+ bReady = false;
+
+ if(stream != NULL)
+ ResourceManager->closeStream(stream);
+
+ stream = NULL;
+
+ if(bBuffersAllocated)
+ {
+ if(mBufferList[0] != 0)
+ alDeleteBuffers(NUMBUFFERS, mBufferList);
+ for(int i = 0; i < NUMBUFFERS; i++)
+ mBufferList[i] = 0;
+
+ bBuffersAllocated = false;
+ }
+
+ if(bVorbisFileInitialized)
+ {
+ vf.ov_clear();
+ bVorbisFileInitialized = false;
+ }
+}
+
+void VorbisStreamSource::resetStream()
+{
+ // MusicPlayerStreamingHook allow you to create a handler
+ // where you can rotate through streaming files
+ // Comment in and provide hook if desired.
+ //
+ //const char * newFile = MusicPlayerStreamingHook(mHandle);
+ //if (newFile)
+ //{
+ // setNewFile(newFile);
+ // return;
+ //}
+ //else
+ {
+ vf.ov_pcm_seek(0);
+ DataLeft = DataSize;
+ bFinished = AL_FALSE;
+ }
+}
+
+void VorbisStreamSource::setNewFile(const char * file)
+{
+ //---------------------
+ // close down old file...
+ //---------------------
+
+ if(stream != NULL)
+ {
+ ResourceManager->closeStream(stream);
+ stream = NULL;
+ }
+
+ if(bVorbisFileInitialized)
+ {
+ vf.ov_clear();
+ bVorbisFileInitialized = false;
+ }
+
+ //---------------------
+ // open up new file...
+ //---------------------
+
+ mFilename = file;
+ stream = ResourceManager->openStream(mFilename);
+ if(stream != NULL)
+ {
+ if(vf.ov_open(stream, NULL, 0) < 0)
+ {
+ bFinished = AL_TRUE;
+ bVorbisFileInitialized = false;
+ bIsValid = false;
+ return;
+ }
+
+ //Read Vorbis File Info
+ vorbis_info * vi = vf.ov_info(-1);
+ freq = vi->rate;
+
+ long samples = (long)vf.ov_pcm_total(-1);
+
+ if(vi->channels == 1)
+ {
+ format = AL_FORMAT_MONO16;
+ DataSize = 2 * samples;
+ }
+ else
+ {
+ format = AL_FORMAT_STEREO16;
+ DataSize = 4 * samples;
+ }
+ DataLeft = DataSize;
+
+ // Clear Error Code
+ alGetError();
+
+ bFinished = AL_FALSE;
+ bVorbisFileInitialized = true;
+ bIsValid = true;
+ }
+}
+
+// ov_read() only returns a maximum of one page worth of data
+// this helper function will repeat the read until buffer is full
+long VorbisStreamSource::oggRead(char *buffer,int length,
+ int bigendianp,int *bitstream)
+{
+ long bytesRead = 0;
+ long totalBytes = 0;
+ long offset = 0;
+ long bytesToRead = 0;
+
+ while((offset) < length)
+ {
+ if((length - offset) < CHUNKSIZE)
+ bytesToRead = length - offset;
+ else
+ bytesToRead = CHUNKSIZE;
+
+ bytesRead = vf.ov_read(buffer, bytesToRead, bigendianp, bitstream);
+ //#ifdef TORQUE_OS_LINUX
+#if 1 // Might fix mac audio issue and possibly others...based on references, this looks like correct action
+ // linux ver will hang on exit after a stream loop if we don't
+ // do this
+ if (bytesRead == OV_HOLE)
+ // retry, see:
+ // http://www.xiph.org/archives/vorbis-dev/200102/0163.html
+ // http://www.mit.edu/afs/sipb/user/xiphmont/ogg-sandbox/vorbis/doc/vorbis-errors.txt
+ continue;
+#endif
+
+ if(bytesRead <= 0)
+ break;
+ offset += bytesRead;
+ buffer += bytesRead;
+ }
+ return offset;
+}
+
+#ifdef TORQUE_OS_LINUX
+
+// JMQ: OpenAL sometimes replaces the stream source's position with its own
+// nifty value, causing the music to pan around the listener. how nice.
+// this function checks to see if the current source position in openal
+// is near the initial position, and slams it to the correct value if it
+// is wrong.
+
+// This is a bad place to put this, but I don't feel like adding a new
+// .cc file. And since this is an incredibly lame hack to
+// workaround a stupid OpenAL bug, I see no point in overengineering it.
+
+void AudioStreamSource::checkPosition()
+{
+ ALfloat pos[3];
+ alGetSourcefv(mSource, AL_POSITION, pos);
+
+ // just compute the difference between the openal pos and the
+ // correct pos. it better be pretty friggin small.
+ Point3F openalPos(pos[0], pos[1], pos[2]);
+ F32 slopDist = 0.0001f;
+
+ F32 dist = mFabs((openalPos - mPosition).len());
+ if (dist > slopDist)
+ // slam!
+ alSource3f(mSource, AL_POSITION, mPosition.x, mPosition.y, mPosition.z);
+}
+
+#endif
+
+F32 VorbisStreamSource::getElapsedTime()
+{
+ return vf.ov_time_tell();
+}
+
+F32 VorbisStreamSource::getTotalTime()
+{
+ return vf.ov_time_total(-1);
+}
diff --git a/engine/audio/vorbisStreamSource.h b/engine/audio/vorbisStreamSource.h
new file mode 100755
index 0000000..5c07cca
--- /dev/null
+++ b/engine/audio/vorbisStreamSource.h
@@ -0,0 +1,58 @@
+//--------------------------------------------
+// vorbisStreamSource.h
+// header for streaming audio source for Ogg Vorbis
+//
+// Kurtis Seebaldt
+//--------------------------------------------
+
+#ifndef _VORBISSTREAMSOURCE_H_
+#define _VORBISSTREAMSOURCE_H_
+
+#ifndef _AUDIOSTREAMSOURCE_H_
+#include "audio/audioStreamSource.h"
+#endif
+
+#include "audio/vorbisStream.h"
+
+class VorbisStreamSource: public AudioStreamSource
+{
+ public:
+ VorbisStreamSource(const char *filename);
+ virtual ~VorbisStreamSource();
+
+ virtual bool initStream();
+ virtual bool updateBuffers();
+ virtual void freeStream();
+ virtual F32 getElapsedTime();
+ virtual F32 getTotalTime();
+
+ private:
+ ALuint mBufferList[NUMBUFFERS];
+ S32 mNumBuffers;
+ S32 mBufferSize;
+ Stream *stream;
+
+ bool bReady;
+ bool bFinished;
+
+ ALenum format;
+ ALsizei size;
+ ALsizei freq;
+
+ ALuint DataSize;
+ ALuint DataLeft;
+ ALuint buffersinqueue;
+
+ bool bBuffersAllocated;
+ bool bVorbisFileInitialized;
+
+ int current_section;
+ OggVorbisFile vf;
+
+ void clear();
+ long oggRead(char *buffer,int length, int bigendianp,int *bitstream);
+ void resetStream();
+ void setNewFile(const char * file);
+};
+
+#endif // _VORBISSTREAMSOURCE_H_
diff --git a/engine/audio/wavStreamSource.cc b/engine/audio/wavStreamSource.cc
new file mode 100755
index 0000000..314496f
--- /dev/null
+++ b/engine/audio/wavStreamSource.cc
@@ -0,0 +1,334 @@
+//--------------------------------------
+// audioStreamSource.cc
+// implementation of streaming audio source
+//
+// Kurtis Seebaldt
+//--------------------------------------
+
+#include "audio/wavStreamSource.h"
+
+#define BUFFERSIZE 32768
+
+typedef struct
+{
+ ALubyte riff[4]; // 'RIFF'
+ ALsizei riffSize;
+ ALubyte wave[4]; // 'WAVE'
+ ALubyte fmt[4]; // 'fmt '
+ ALuint fmtSize;
+ ALushort Format;
+ ALushort Channels;
+ ALuint SamplesPerSec;
+ ALuint BytesPerSec;
+ ALushort BlockAlign;
+ ALushort BitsPerSample;
+ ALubyte data[4]; // 'data'
+ ALuint dataSize;
+} WAVE_Struct;
+
+/// WAV File-header
+struct WAVFileHdr
+{
+ ALubyte id[4];
+ ALsizei size;
+ ALubyte type[4];
+};
+
+//// WAV Fmt-header
+struct WAVFmtHdr
+{
+ ALushort format;
+ ALushort channels;
+ ALuint samplesPerSec;
+ ALuint bytesPerSec;
+ ALushort blockAlign;
+ ALushort bitsPerSample;
+};
+
+/// WAV FmtEx-header
+struct WAVFmtExHdr
+{
+ ALushort size;
+ ALushort samplesPerBlock;
+};
+
+/// WAV Smpl-header
+struct WAVSmplHdr
+{
+ ALuint manufacturer;
+ ALuint product;
+ ALuint samplePeriod;
+ ALuint note;
+ ALuint fineTune;
+ ALuint SMPTEFormat;
+ ALuint SMPTEOffest;
+ ALuint loops;
+ ALuint samplerData;
+ struct
+ {
+ ALuint identifier;
+ ALuint type;
+ ALuint start;
+ ALuint end;
+ ALuint fraction;
+ ALuint count;
+ } loop[1];
+};
+
+/// WAV Chunk-header
+struct WAVChunkHdr
+{
+ ALubyte id[4];
+ ALuint size;
+};
+
+
+
+WavStreamSource::WavStreamSource(const char *filename) {
+ stream = NULL;
+ bIsValid = false;
+ bBuffersAllocated = false;
+ mBufferList[0] = 0;
+ clear();
+
+ mFilename = filename;
+ mPosition = Point3F(0.f,0.f,0.f);
+}
+
+WavStreamSource::~WavStreamSource() {
+ if(bReady && bIsValid)
+ freeStream();
+}
+
+void WavStreamSource::clear()
+{
+ if(stream)
+ freeStream();
+
+ mHandle = NULL_AUDIOHANDLE;
+ mSource = NULL;
+
+ if(mBufferList[0] != 0)
+ alDeleteBuffers(NUMBUFFERS, mBufferList);
+ for(int i = 0; i < NUMBUFFERS; i++)
+ mBufferList[i] = 0;
+
+ dMemset(&mDescription, 0, sizeof(Audio::Description));
+ mEnvironment = 0;
+ mPosition.set(0.f,0.f,0.f);
+ mDirection.set(0.f,1.f,0.f);
+ mPitch = 1.f;
+ mScore = 0.f;
+ mCullTime = 0;
+
+ bReady = false;
+ bFinishedPlaying = false;
+ bIsValid = false;
+ bBuffersAllocated = false;
+}
+
+bool WavStreamSource::initStream() {
+ WAVChunkHdr chunkHdr;
+ WAVFmtExHdr fmtExHdr;
+ WAVFileHdr fileHdr;
+ WAVSmplHdr smplHdr;
+ WAVFmtHdr fmtHdr;
+
+ WAVE_Struct wave;
+
+ ALint error;
+
+ bFinished = false;
+
+ char data[BUFFERSIZE];
+
+ alSourceStop(mSource);
+ alSourcei(mSource, AL_BUFFER, 0);
+
+ stream = ResourceManager->openStream(mFilename);
+ if(stream != NULL) {
+ stream->read(4, &fileHdr.id[0]);
+ stream->read(&fileHdr.size);
+ stream->read(4, &fileHdr.type[0]);
+
+ stream->read(4, &chunkHdr.id[0]);
+ stream->read(&chunkHdr.size);
+
+ // WAV Format header
+ stream->read(&fmtHdr.format);
+ stream->read(&fmtHdr.channels);
+ stream->read(&fmtHdr.samplesPerSec);
+ stream->read(&fmtHdr.bytesPerSec);
+ stream->read(&fmtHdr.blockAlign);
+ stream->read(&fmtHdr.bitsPerSample);
+
+ format=(fmtHdr.channels==1?
+ (fmtHdr.bitsPerSample==8?AL_FORMAT_MONO8:AL_FORMAT_MONO16):
+ (fmtHdr.bitsPerSample==8?AL_FORMAT_STEREO8:AL_FORMAT_STEREO16));
+ freq=fmtHdr.samplesPerSec;
+
+ stream->read(4, &chunkHdr.id[0]);
+ stream->read(&chunkHdr.size);
+
+ DataSize = chunkHdr.size;
+ DataLeft = DataSize;
+ dataStart = stream->getPosition();
+
+ // Clear Error Code
+ alGetError();
+
+ alGenBuffers(NUMBUFFERS, mBufferList);
+ if ((error = alGetError()) != AL_NO_ERROR) {
+ return false;
+ }
+
+ bBuffersAllocated = true;
+
+ int numBuffers = 0;
+ for(int loop = 0; loop < NUMBUFFERS; loop++)
+ {
+ ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
+
+ if (DataToRead == DataLeft)
+ bFinished = AL_TRUE;
+ stream->read(DataToRead, data);
+ DataLeft -= DataToRead;
+ alBufferData(mBufferList[loop], format, data, DataToRead, freq);
+ if ((error = alGetError()) != AL_NO_ERROR) {
+ return false;
+ }
+ ++numBuffers;
+ if(bFinished)
+ break;
+ }
+
+ // Queue the buffers on the source
+ alSourceQueueBuffers(mSource, numBuffers, mBufferList);
+ if ((error = alGetError()) != AL_NO_ERROR) {
+ return false;
+ }
+
+ buffersinqueue = numBuffers;
+ alSourcei(mSource, AL_LOOPING, AL_FALSE);
+ bReady = true;
+ }
+ else {
+ return false;
+ }
+
+ bIsValid = true;
+
+ return true;
+}
+
+bool WavStreamSource::updateBuffers() {
+
+ ALint processed;
+ ALuint BufferID;
+ ALint error;
+ char data[BUFFERSIZE];
+
+ // don't do anything if buffer isn't initialized
+ if(!bIsValid)
+ return false;
+
+ if(bFinished && mDescription.mIsLooping) {
+ resetStream();
+ }
+
+ // reset AL error code
+ alGetError();
+
+ // Get status
+ alGetSourcei(mSource, AL_BUFFERS_PROCESSED, &processed);
+
+ // If some buffers have been played, unqueue them and load new audio into them, then add them to the queue
+ if (processed > 0)
+ {
+
+ while (processed)
+ {
+ alSourceUnqueueBuffers(mSource, 1, &BufferID);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ if (!bFinished)
+ {
+ ALuint DataToRead = (DataLeft > BUFFERSIZE) ? BUFFERSIZE : DataLeft;
+
+ if (DataToRead == DataLeft) {
+ bFinished = AL_TRUE;
+ }
+
+ stream->read(DataToRead, data);
+ DataLeft -= DataToRead;
+
+ alBufferData(BufferID, format, data, DataToRead, freq);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ // Queue buffer
+ alSourceQueueBuffers(mSource, 1, &BufferID);
+ if ((error = alGetError()) != AL_NO_ERROR)
+ return false;
+
+ processed--;
+
+ if(bFinished && mDescription.mIsLooping) {
+ resetStream();
+ }
+ }
+ else
+ {
+ buffersinqueue--;
+ processed--;
+
+ if (buffersinqueue == 0)
+ {
+ bFinishedPlaying = AL_TRUE;
+ return AL_FALSE;
+ }
+ }
+ }
+ }
+
+ return AL_TRUE;
+}
+
+void WavStreamSource::freeStream() {
+ bReady = false;
+
+
+ if(stream != NULL)
+ ResourceManager->closeStream(stream);
+ stream = NULL;
+
+ if(bBuffersAllocated) {
+ if(mBufferList[0] != 0)
+ alDeleteBuffers(NUMBUFFERS, mBufferList);
+ for(int i = 0; i < NUMBUFFERS; i++)
+ mBufferList[i] = 0;
+
+ bBuffersAllocated = false;
+ }
+}
+
+void WavStreamSource::resetStream() {
+ stream->setPosition(dataStart);
+ DataLeft = DataSize;
+ bFinished = AL_FALSE;
+}
+
+#include "console/console.h"
+
+F32 WavStreamSource::getElapsedTime()
+{
+ Con::warnf( "GetElapsedTime not implemented in WaveStreams yet" );
+ return -1.f;
+}
+
+F32 WavStreamSource::getTotalTime()
+{
+ Con::warnf( "GetTotalTime not implemented in WaveStreams yet" );
+ return -1.f;
+}
diff --git a/engine/audio/wavStreamSource.h b/engine/audio/wavStreamSource.h
new file mode 100755
index 0000000..e275c02
--- /dev/null
+++ b/engine/audio/wavStreamSource.h
@@ -0,0 +1,49 @@
+//--------------------------------------------
+// wavStreamSource.h
+// header for streaming audio source for WAV
+//--------------------------------------------
+
+#ifndef _WAVSTREAMSOURCE_H_
+#define _WAVSTREAMSOURCE_H_
+
+#ifndef _AUDIOSTREAMSOURCE_H_
+#include "audio/audioStreamSource.h"
+#endif
+
+class WavStreamSource: public AudioStreamSource
+{
+ public:
+ WavStreamSource(const char *filename);
+ virtual ~WavStreamSource();
+
+ virtual bool initStream();
+ virtual bool updateBuffers();
+ virtual void freeStream();
+ virtual F32 getElapsedTime();
+ virtual F32 getTotalTime();
+
+ private:
+ ALuint mBufferList[NUMBUFFERS];
+ S32 mNumBuffers;
+ S32 mBufferSize;
+ Stream *stream;
+
+ bool bReady;
+ bool bFinished;
+
+ ALenum format;
+ ALsizei size;
+ ALsizei freq;
+
+ ALuint DataSize;
+ ALuint DataLeft;
+ ALuint dataStart;
+ ALuint buffersinqueue;
+
+ bool bBuffersAllocated;
+
+ void clear();
+ void resetStream();
+};
+
+#endif // _AUDIOSTREAMSOURCE_H_
diff --git a/engine/collision/abstractPolyList.cc b/engine/collision/abstractPolyList.cc
new file mode 100755
index 0000000..d8042b0
--- /dev/null
+++ b/engine/collision/abstractPolyList.cc
@@ -0,0 +1,97 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "collision/abstractPolyList.h"
+
+
+//----------------------------------------------------------------------------
+
+AbstractPolyList::~AbstractPolyList()
+{
+ mInterestNormalRegistered = false;
+}
+
+static U32 PolyFace[6][4] = {
+ { 3, 2, 1, 0 },
+ { 7, 4, 5, 6 },
+ { 0, 5, 4, 3 },
+ { 6, 5, 0, 1 },
+ { 7, 6, 1, 2 },
+ { 4, 7, 2, 3 },
+};
+
+void AbstractPolyList::addBox(const Box3F &box)
+{
+ Point3F pos = box.min;
+ F32 dx = box.max.x - box.min.x;
+ F32 dy = box.max.y - box.min.y;
+ F32 dz = box.max.z - box.min.z;
+
+ U32 base = addPoint(pos);
+ pos.y += dy; addPoint(pos);
+ pos.x += dx; addPoint(pos);
+ pos.y -= dy; addPoint(pos);
+ pos.z += dz; addPoint(pos);
+ pos.x -= dx; addPoint(pos);
+ pos.y += dy; addPoint(pos);
+ pos.x += dx; addPoint(pos);
+
+ for (S32 i = 0; i < 6; i++) {
+ begin(0,i);
+ S32 v1 = base + PolyFace[i][0];
+ S32 v2 = base + PolyFace[i][1];
+ S32 v3 = base + PolyFace[i][2];
+ S32 v4 = base + PolyFace[i][3];
+ vertex(v1);
+ vertex(v2);
+ vertex(v3);
+ vertex(v4);
+ plane(v1,v2,v3);
+ end();
+ }
+}
+
+bool AbstractPolyList::getMapping(MatrixF *, Box3F *)
+{
+ // return list transform and bounds in list space...optional
+ return false;
+}
+
+
+bool AbstractPolyList::isInterestedInPlane(const PlaneF& plane)
+{
+ if (mInterestNormalRegistered == false) {
+ return true;
+ }
+ else {
+ PlaneF xformed;
+ mPlaneTransformer.transform(plane, xformed);
+ if (mDot(xformed, mInterestNormal) >= 0.0f)
+ return false;
+ else
+ return true;
+ }
+}
+
+bool AbstractPolyList::isInterestedInPlane(const U32 index)
+{
+ if (mInterestNormalRegistered == false) {
+ return true;
+ }
+ else {
+ const PlaneF& rPlane = getIndexedPlane(index);
+ if (mDot(rPlane, mInterestNormal) >= 0.0f)
+ return false;
+ else
+ return true;
+ }
+}
+
+void AbstractPolyList::setInterestNormal(const Point3F& normal)
+{
+ mInterestNormalRegistered = true;
+ mInterestNormal = normal;
+}
+
diff --git a/engine/collision/abstractPolyList.h b/engine/collision/abstractPolyList.h
new file mode 100755
index 0000000..b0deb24
--- /dev/null
+++ b/engine/collision/abstractPolyList.h
@@ -0,0 +1,241 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _ABSTRACTPOLYLIST_H_
+#define _ABSTRACTPOLYLIST_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _MPLANETRANSFORMER_H_
+#include "math/mPlaneTransformer.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+
+class SceneObject;
+
+//----------------------------------------------------------------------------
+
+/// A polygon filtering interface.
+///
+/// The various AbstractPolyList subclasses are used in Torque as an interface to
+/// handle spatial queries. SceneObject::buildPolyList() takes an implementor of
+/// AbstractPolyList (such as ConcretePolyList, ClippedPolyList, etc.) and a
+/// bounding volume. The function runs geometry data from all the objects in the
+/// bounding volume through the passed PolyList.
+///
+/// This interface only provides a method to get data INTO your implementation. Different
+/// subclasses provide different interfaces to get data back out, depending on their
+/// specific quirks.
+///
+/// The physics engine now uses convex hulls for collision detection.
+///
+/// @see Convex
+class AbstractPolyList
+{
+protected:
+ // User set state
+ SceneObject* mCurrObject;
+
+ MatrixF mBaseMatrix; // Base transform
+ MatrixF mTransformMatrix; // Current object transform
+ MatrixF mMatrix; // Base * current transform
+ Point3F mScale;
+
+ PlaneTransformer mPlaneTransformer;
+
+ bool mInterestNormalRegistered;
+ Point3F mInterestNormal;
+
+public:
+ AbstractPolyList();
+ virtual ~AbstractPolyList();
+
+ /// @name Common Interface
+ /// @{
+ void setBaseTransform(const MatrixF& mat);
+
+ /// Sets the transform applying to the current stream of
+ /// vertices.
+ ///
+ /// @param mat Transformation of the object. (in)
+ /// @param scale Scaling of the object. (in)
+ void setTransform(const MatrixF* mat, const Point3F& scale);
+
+ /// Gets the transform applying to the current stream of
+ /// vertices.
+ ///
+ /// @param mat Transformation of the object. (out)
+ /// @param scale Scaling of the object. (out)
+ void getTransform(MatrixF* mat, Point3F * scale);
+
+ /// This is called by the object which is currently feeding us
+ /// vertices, to tell us who it is.
+ void setObject(SceneObject*);
+
+ /// Add a box via the query interface (below). This wraps some calls
+ /// to addPoint and addPlane.
+ void addBox(const Box3F &box);
+
+ void doConstruct();
+ /// @}
+
+ /// @name Query Interface
+ ///
+ /// It is through this interface that geometry data is fed to the
+ /// PolyList. The order of calls are:
+ /// - begin()
+ /// - One or more calls to vertex() and one call to plane(), in any order.
+ /// - end()
+ ///
+ /// @code
+ /// // Example code that adds data to a PolyList.
+ /// // See AbstractPolyList::addBox() for the function this was taken from.
+ ///
+ /// // First, we add points... (note that we use base to track the start of adding.)
+ /// U32 base = addPoint(pos);
+ /// pos.y += dy; addPoint(pos);
+ /// pos.x += dx; addPoint(pos);
+ /// pos.y -= dy; addPoint(pos);
+ /// pos.z += dz; addPoint(pos);
+ /// pos.x -= dx; addPoint(pos);
+ /// pos.y += dy; addPoint(pos);
+ /// pos.x += dx; addPoint(pos);
+ ///
+ /// // Now we add our surfaces. (there are six, as we are adding a cube here)
+ /// for (S32 i = 0; i < 6; i++) {
+ /// // Begin a surface
+ /// begin(0,i);
+ ///
+ /// // Calculate the vertex ids; we have a lookup table to tell us offset from base.
+ /// // In your own code, you might use a different method.
+ /// S32 v1 = base + PolyFace[i][0];
+ /// S32 v2 = base + PolyFace[i][1];
+ /// S32 v3 = base + PolyFace[i][2];
+ /// S32 v4 = base + PolyFace[i][3];
+ ///
+ /// // Reference the four vertices that make up this surface.
+ /// vertex(v1);
+ /// vertex(v2);
+ /// vertex(v3);
+ /// vertex(v4);
+ ///
+ /// // Indicate the plane of this surface.
+ /// plane(v1,v2,v3);
+ ///
+ /// // End the surface.
+ /// end();
+ /// }
+ /// @endcode
+ /// @{
+
+ /// Are we empty of data?
+ virtual bool isEmpty() const = 0;
+
+ /// Adds a point to the poly list, and returns
+ /// an ID number for that point.
+ virtual U32 addPoint(const Point3F& p) = 0;
+
+ /// Adds a plane to the poly list, and returns
+ /// an ID number for that point.
+ virtual U32 addPlane(const PlaneF& plane) = 0;
+
+ /// Start a surface.
+ ///
+ /// @param material A material ID for this surface.
+ /// @param surfaceKey A key value to associate with this surface.
+ virtual void begin(U32 material,U32 surfaceKey) = 0;
+
+ /// Indicate the plane of the surface.
+ virtual void plane(U32 v1,U32 v2,U32 v3) = 0;
+ /// Indicate the plane of the surface.
+ virtual void plane(const PlaneF& p) = 0;
+ /// Indicate the plane of the surface.
+ virtual void plane(const U32 index) = 0;
+
+ /// Reference a vertex which is in this surface.
+ virtual void vertex(U32 vi) = 0;
+
+ /// Mark the end of a surface.
+ virtual void end() = 0;
+
+ /// Return list transform and bounds in list space.
+ ///
+ /// @returns False if no data is available.
+ virtual bool getMapping(MatrixF *, Box3F *);
+ /// @}
+
+ /// @name Interest
+ ///
+ /// This is a mechanism to let you specify interest in a specific normal.
+ /// If you set a normal you're interested in, then any planes facing "away"
+ /// from that normal are culled from the results.
+ ///
+ /// This is handy if you're using this to do a physics check, as you're not
+ /// interested in polygons facing away from you (since you don't collide with
+ /// the backsides/insides of things).
+ /// @{
+
+ void setInterestNormal(const Point3F& normal);
+ void clearInterestNormal() { mInterestNormalRegistered = false; }
+
+
+ virtual bool isInterestedInPlane(const PlaneF& plane);
+ virtual bool isInterestedInPlane(const U32 index);
+
+ /// @}
+
+ protected:
+ /// A helper function to convert a plane index to a PlaneF structure.
+ virtual const PlaneF& getIndexedPlane(const U32 index) = 0;
+};
+
+inline AbstractPolyList::AbstractPolyList()
+{
+ doConstruct();
+}
+
+inline void AbstractPolyList::doConstruct()
+{
+ mCurrObject = NULL;
+ mBaseMatrix.identity();
+ mMatrix.identity();
+ mScale.set(1, 1, 1);
+
+ mPlaneTransformer.setIdentity();
+
+ mInterestNormalRegistered = false;
+}
+
+inline void AbstractPolyList::setBaseTransform(const MatrixF& mat)
+{
+ mBaseMatrix = mat;
+}
+
+inline void AbstractPolyList::setTransform(const MatrixF* mat, const Point3F& scale)
+{
+ mMatrix = mBaseMatrix;
+ mTransformMatrix = *mat;
+ mMatrix.mul(*mat);
+ mScale = scale;
+
+ mPlaneTransformer.set(mMatrix, mScale);
+}
+
+inline void AbstractPolyList::getTransform(MatrixF* mat, Point3F * scale)
+{
+ *mat = mTransformMatrix;
+ *scale = mScale;
+}
+
+inline void AbstractPolyList::setObject(SceneObject* obj)
+{
+ mCurrObject = obj;
+}
+
+
+#endif
diff --git a/engine/collision/boxConvex.cc b/engine/collision/boxConvex.cc
new file mode 100755
index 0000000..8e39eb6
--- /dev/null
+++ b/engine/collision/boxConvex.cc
@@ -0,0 +1,199 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "math/mMath.h"
+#include "game/gameBase.h"
+#include "collision/boxConvex.h"
+
+
+//----------------------------------------------------------------------------
+
+struct Corner {
+ S32 a,b,c;
+ S32 ab,ac,bc;
+} sCorner[] =
+{
+ { 1,2,4, 4,0,1 },
+ { 0,3,5, 4,0,3 },
+ { 0,3,6, 4,1,2 },
+ { 1,2,7, 4,3,2 },
+ { 0,5,6, 0,1,5 },
+ { 1,4,7, 0,3,5 },
+ { 2,4,7, 1,2,5 },
+ { 3,5,6, 3,2,5 },
+};
+
+struct Face {
+ S32 vertex[4];
+ S32 axis;
+ bool flip;
+} sFace[] =
+{
+ { 0,4,5,1, 1,true },
+ { 0,2,6,4, 0,true },
+ { 3,7,6,2, 1,false },
+ { 3,1,5,7, 0,false },
+ { 0,1,3,2, 2,true },
+ { 4,6,7,5, 2,false },
+};
+
+Point3F BoxConvex::support(const VectorF& v) const
+{
+ Point3F p = mCenter;
+ p.x += (v.x >= 0)? mSize.x: -mSize.x;
+ p.y += (v.y >= 0)? mSize.y: -mSize.y;
+ p.z += (v.z >= 0)? mSize.z: -mSize.z;
+ return p;
+}
+
+Point3F BoxConvex::getVertex(S32 v)
+{
+ Point3F p = mCenter;
+ p.x += (v & 1)? mSize.x: -mSize.x;
+ p.y += (v & 2)? mSize.y: -mSize.y;
+ p.z += (v & 4)? mSize.z: -mSize.z;
+ return p;
+}
+
+inline bool isOnPlane(Point3F p,PlaneF& plane)
+{
+ F32 dist = mDot(plane,p) + plane.d;
+ return dist < 0.1 && dist > -0.1;
+}
+
+void BoxConvex::getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf)
+{
+ cf->material = 0;
+ cf->object = mObject;
+
+ S32 v = 0;
+ v += (n.x >= 0)? 1: 0;
+ v += (n.y >= 0)? 2: 0;
+ v += (n.z >= 0)? 4: 0;
+
+ PlaneF plane;
+ plane.set(getVertex(v),n);
+
+ // Emit vertex and edge
+ S32 mask = 0;
+ Corner& corner = sCorner[v];
+ mask |= isOnPlane(getVertex(corner.a),plane)? 1: 0;
+ mask |= isOnPlane(getVertex(corner.b),plane)? 2: 0;
+ mask |= isOnPlane(getVertex(corner.c),plane)? 4: 0;
+
+ switch(mask) {
+ case 0: {
+ cf->mVertexList.increment();
+ mat.mulP(getVertex(v),&cf->mVertexList.last());
+ break;
+ }
+ case 1:
+ emitEdge(v,corner.a,mat,cf);
+ break;
+ case 2:
+ emitEdge(v,corner.b,mat,cf);
+ break;
+ case 4:
+ emitEdge(v,corner.c,mat,cf);
+ break;
+ case 1 | 2:
+ emitFace(corner.ab,mat,cf);
+ break;
+ case 2 | 4:
+ emitFace(corner.bc,mat,cf);
+ break;
+ case 1 | 4:
+ emitFace(corner.ac,mat,cf);
+ break;
+ }
+}
+
+void BoxConvex::getPolyList(AbstractPolyList* list)
+{
+ list->setTransform(&getTransform(), getScale());
+ list->setObject(getObject());
+
+ U32 base = list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, -mSize.z));
+ list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, -mSize.z));
+ list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, -mSize.z));
+ list->addPoint(mCenter + Point3F( mSize.x, mSize.y, -mSize.z));
+ list->addPoint(mCenter + Point3F(-mSize.x, -mSize.y, mSize.z));
+ list->addPoint(mCenter + Point3F( mSize.x, -mSize.y, mSize.z));
+ list->addPoint(mCenter + Point3F(-mSize.x, mSize.y, mSize.z));
+ list->addPoint(mCenter + Point3F( mSize.x, mSize.y, mSize.z));
+
+ for (U32 i = 0; i < 6; i++) {
+ list->begin(0, i);
+
+ list->vertex(base + sFace[i].vertex[0]);
+ list->vertex(base + sFace[i].vertex[1]);
+ list->vertex(base + sFace[i].vertex[2]);
+ list->vertex(base + sFace[i].vertex[3]);
+
+ list->plane(base + sFace[i].vertex[0],
+ base + sFace[i].vertex[1],
+ base + sFace[i].vertex[2]);
+ list->end();
+ }
+}
+
+
+void BoxConvex::emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf)
+{
+ S32 vc = cf->mVertexList.size();
+ cf->mVertexList.increment(2);
+ Point3F *vp = cf->mVertexList.begin();
+ mat.mulP(getVertex(v1),&vp[vc]);
+ mat.mulP(getVertex(v2),&vp[vc + 1]);
+
+ cf->mEdgeList.increment();
+ ConvexFeature::Edge& edge = cf->mEdgeList.last();
+ edge.vertex[0] = vc;
+ edge.vertex[1] = vc + 1;
+}
+
+void BoxConvex::emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf)
+{
+ Face& face = sFace[fi];
+
+ // Emit vertices
+ S32 vc = cf->mVertexList.size();
+ cf->mVertexList.increment(4);
+ Point3F *vp = cf->mVertexList.begin();
+ for (S32 v = 0; v < 4; v++)
+ mat.mulP(getVertex(face.vertex[v]),&vp[vc + v]);
+
+ // Emit edges
+ cf->mEdgeList.increment(4);
+ ConvexFeature::Edge* edge = cf->mEdgeList.end() - 4;
+ for (S32 e = 0; e < 4; e++) {
+ edge[e].vertex[0] = vc + e;
+ edge[e].vertex[1] = vc + ((e + 1) & 3);
+ }
+
+ // Emit 2 triangle faces
+ cf->mFaceList.increment(2);
+ ConvexFeature::Face* ef = cf->mFaceList.end() - 2;
+ mat.getColumn(face.axis,&ef->normal);
+ if (face.flip)
+ ef[0].normal.neg();
+ ef[1].normal = ef[0].normal;
+ ef[1].vertex[0] = ef[0].vertex[0] = vc;
+ ef[1].vertex[1] = ef[0].vertex[2] = vc + 2;
+ ef[0].vertex[1] = vc + 1;
+ ef[1].vertex[2] = vc + 3;
+}
+
+
+
+const MatrixF& OrthoBoxConvex::getTransform() const
+{
+ Point3F translation;
+ Parent::getTransform().getColumn(3, &translation);
+ mOrthoMatrixCache.setColumn(3, translation);
+ return mOrthoMatrixCache;
+}
+
diff --git a/engine/collision/boxConvex.h b/engine/collision/boxConvex.h
new file mode 100755
index 0000000..5e00aea
--- /dev/null
+++ b/engine/collision/boxConvex.h
@@ -0,0 +1,46 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _BOXCONVEX_H_
+#define _BOXCONVEX_H_
+
+#ifndef _CONVEX_H_
+#include "collision/convex.h"
+#endif
+
+
+//----------------------------------------------------------------------------
+
+class BoxConvex: public Convex
+{
+ Point3F getVertex(S32 v);
+ void emitEdge(S32 v1,S32 v2,const MatrixF& mat,ConvexFeature* cf);
+ void emitFace(S32 fi,const MatrixF& mat,ConvexFeature* cf);
+public:
+ //
+ Point3F mCenter;
+ VectorF mSize;
+
+ BoxConvex() { mType = BoxConvexType; }
+ void init(SceneObject* obj) { mObject = obj; }
+
+ Point3F support(const VectorF& v) const;
+ void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
+ void getPolyList(AbstractPolyList* list);
+};
+
+
+class OrthoBoxConvex: public BoxConvex
+{
+ typedef BoxConvex Parent;
+ mutable MatrixF mOrthoMatrixCache;
+
+ public:
+ OrthoBoxConvex() { mOrthoMatrixCache.identity(); }
+
+ virtual const MatrixF& getTransform() const;
+};
+
+#endif
diff --git a/engine/collision/clippedPolyList.cc b/engine/collision/clippedPolyList.cc
new file mode 100755
index 0000000..bd82649
--- /dev/null
+++ b/engine/collision/clippedPolyList.cc
@@ -0,0 +1,268 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "console/console.h"
+#include "collision/clippedPolyList.h"
+
+
+//----------------------------------------------------------------------------
+
+ClippedPolyList::ClippedPolyList()
+{
+ VECTOR_SET_ASSOCIATION(mPolyList);
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mIndexList);
+ VECTOR_SET_ASSOCIATION(mPolyPlaneList);
+ VECTOR_SET_ASSOCIATION(mPlaneList);
+
+ mNormal.set(0,0,0);
+ mIndexList.reserve(100);
+}
+
+ClippedPolyList::~ClippedPolyList()
+{
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::clear()
+{
+ // Only clears internal data
+ mPolyList.clear();
+ mVertexList.clear();
+ mIndexList.clear();
+ mPolyPlaneList.clear();
+}
+
+bool ClippedPolyList::isEmpty() const
+{
+ return mPolyList.size() == 0;
+}
+
+
+//----------------------------------------------------------------------------
+
+U32 ClippedPolyList::addPoint(const Point3F& p)
+{
+ mVertexList.increment();
+ Vertex& v = mVertexList.last();
+ v.point.x = p.x * mScale.x;
+ v.point.y = p.y * mScale.y;
+ v.point.z = p.z * mScale.z;
+ mMatrix.mulP(v.point);
+
+ // Build the plane mask
+ register U32 mask = 1;
+ register S32 count = mPlaneList.size();
+ register PlaneF * plane = mPlaneList.address();
+
+ v.mask = 0;
+ while(--count >= 0) {
+ if (plane++->distToPlane(v.point) > 0)
+ v.mask |= mask;
+ mask <<= 1;
+ }
+
+ return mVertexList.size() - 1;
+}
+
+
+U32 ClippedPolyList::addPlane(const PlaneF& plane)
+{
+ mPolyPlaneList.increment();
+ mPlaneTransformer.transform(plane, mPolyPlaneList.last());
+
+ return mPolyPlaneList.size() - 1;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::begin(U32 material,U32 surfaceKey)
+{
+ mPolyList.increment();
+ Poly& poly = mPolyList.last();
+ poly.object = mCurrObject;
+ poly.material = material;
+ poly.vertexStart = mIndexList.size();
+ poly.surfaceKey = surfaceKey;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::plane(U32 v1,U32 v2,U32 v3)
+{
+ mPolyList.last().plane.set(mVertexList[v1].point,
+ mVertexList[v2].point,mVertexList[v3].point);
+}
+
+void ClippedPolyList::plane(const PlaneF& p)
+{
+ mPlaneTransformer.transform(p, mPolyList.last().plane);
+}
+
+void ClippedPolyList::plane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ mPolyList.last().plane = mPolyPlaneList[index];
+}
+
+const PlaneF& ClippedPolyList::getIndexedPlane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ return mPolyPlaneList[index];
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::vertex(U32 vi)
+{
+ mIndexList.push_back(vi);
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::end()
+{
+ Poly& poly = mPolyList.last();
+
+ // Anything facing away from the mNormal is rejected
+ if (mDot(poly.plane,mNormal) > 0) {
+ mIndexList.setSize(poly.vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+
+ // Build initial inside/outside plane masks
+ U32 indexStart = poly.vertexStart;
+ U32 vertexCount = mIndexList.size() - indexStart;
+
+ U32 frontMask = 0,backMask = 0;
+ U32 i;
+ for (i = indexStart; i < mIndexList.size(); i++) {
+ U32 mask = mVertexList[mIndexList[i]].mask;
+ frontMask |= mask;
+ backMask |= ~mask;
+ }
+
+ // Trivial accept if all the vertices are on the backsides of
+ // all the planes.
+ if (!frontMask) {
+ poly.vertexCount = vertexCount;
+ return;
+ }
+
+ // Trivial reject if any plane not crossed has all it's points
+ // on the front.
+ U32 crossMask = frontMask & backMask;
+ if (~crossMask & frontMask) {
+ mIndexList.setSize(poly.vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+
+ // Need to do some clipping
+ for (U32 p = 0; p < mPlaneList.size(); p++) {
+ U32 pmask = 1 << p;
+ // Only test against this plane if we have something
+ // on both sides
+ if (crossMask & pmask) {
+ U32 indexEnd = mIndexList.size();
+ U32 i1 = indexEnd - 1;
+ U32 mask1 = mVertexList[mIndexList[i1]].mask;
+
+ for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
+ U32 mask2 = mVertexList[mIndexList[i2]].mask;
+ if ((mask1 ^ mask2) & pmask) {
+ //
+ mVertexList.increment();
+ VectorF& v1 = mVertexList[mIndexList[i1]].point;
+ VectorF& v2 = mVertexList[mIndexList[i2]].point;
+ VectorF vv = v2 - v1;
+ F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
+
+ mIndexList.push_back(mVertexList.size() - 1);
+ Vertex& iv = mVertexList.last();
+ iv.point.x = v1.x + vv.x * t;
+ iv.point.y = v1.y + vv.y * t;
+ iv.point.z = v1.z + vv.z * t;
+ iv.mask = 0;
+
+ // Test against the remaining planes
+ for (U32 i = p + 1; i < mPlaneList.size(); i++)
+ if (mPlaneList[i].distToPlane(iv.point) > 0) {
+ iv.mask = 1 << i;
+ break;
+ }
+ }
+ if (!(mask2 & pmask)) {
+ U32 index = mIndexList[i2];
+ mIndexList.push_back(index);
+ }
+ mask1 = mask2;
+ i1 = i2;
+ }
+
+ // Check for degenerate
+ indexStart = indexEnd;
+ if (mIndexList.size() - indexStart < 3) {
+ mIndexList.setSize(poly.vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+ }
+ }
+
+ // Emit what's left and compress the index list.
+ poly.vertexCount = mIndexList.size() - indexStart;
+ memcpy(&mIndexList[poly.vertexStart],
+ &mIndexList[indexStart],poly.vertexCount);
+ mIndexList.setSize(poly.vertexStart + poly.vertexCount);
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::memcpy(U32* dst, U32* src,U32 size)
+{
+ U32* end = src + size;
+ while (src != end)
+ *dst++ = *src++;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ClippedPolyList::render()
+{
+ glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glColor4f(1,0,0,0.25);
+ glEnable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+ Poly * p;
+ for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
+ glDrawElements(GL_POLYGON,p->vertexCount,
+ GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
+ }
+
+ glColor3f(0.6,0.6,0.6);
+ glDisable(GL_BLEND);
+ for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
+ glDrawElements(GL_LINE_LOOP,p->vertexCount,
+ GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
+ }
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
diff --git a/engine/collision/clippedPolyList.h b/engine/collision/clippedPolyList.h
new file mode 100755
index 0000000..46f44de
--- /dev/null
+++ b/engine/collision/clippedPolyList.h
@@ -0,0 +1,84 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _CLIPPEDPOLYLIST_H_
+#define _CLIPPEDPOLYLIST_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+#ifndef _ABSTRACTPOLYLIST_H_
+#include "collision/abstractPolyList.h"
+#endif
+
+
+//----------------------------------------------------------------------------
+/// Clipped PolyList
+///
+/// This takes the geometry passed to it and clips against the PlaneList set
+/// by the caller.
+//
+/// @see AbstractPolyList
+class ClippedPolyList: public AbstractPolyList
+{
+ void memcpy(U32* d, U32* s,U32 size);
+
+public:
+ struct Vertex {
+ Point3F point;
+ U32 mask;
+ };
+
+ struct Poly {
+ PlaneF plane;
+ SceneObject* object;
+ U32 material;
+ U32 vertexStart;
+ U32 vertexCount;
+ U32 surfaceKey;
+ };
+
+ typedef Vector PlaneList;
+ typedef Vector VertexList;
+ typedef Vector PolyList;
+ typedef Vector IndexList;
+
+ // Internal data
+ PolyList mPolyList;
+ VertexList mVertexList;
+ IndexList mIndexList;
+
+ PlaneList mPolyPlaneList;
+
+ // Data set by caller
+ PlaneList mPlaneList;
+ VectorF mNormal;
+
+ //
+ ClippedPolyList();
+ ~ClippedPolyList();
+ void clear();
+ void render();
+
+ // Virtual methods
+ bool isEmpty() const;
+ U32 addPoint(const Point3F& p);
+ U32 addPlane(const PlaneF& plane);
+ void begin(U32 material,U32 surfaceKey);
+ void plane(U32 v1,U32 v2,U32 v3);
+ void plane(const PlaneF& p);
+ void plane(const U32 index);
+ void vertex(U32 vi);
+ void end();
+
+ protected:
+ const PlaneF& getIndexedPlane(const U32 index);
+};
+
+
+#endif
diff --git a/engine/collision/collision.h b/engine/collision/collision.h
new file mode 100755
index 0000000..c0b0f13
--- /dev/null
+++ b/engine/collision/collision.h
@@ -0,0 +1,65 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _COLLISION_H_
+#define _COLLISION_H_
+
+#ifndef _DATACHUNKER_H_
+#include "core/dataChunker.h"
+#endif
+#ifndef _MPLANE_H_
+#include "math/mPlane.h"
+#endif
+
+class SceneObject;
+
+//----------------------------------------------------------------------------
+
+struct Collision
+{
+ SceneObject* object;
+ Point3F point;
+ VectorF normal;
+ U32 material;
+
+ // Face and Face dot are currently only set by the extrudedPolyList
+ // clipper. Values are otherwise undefined.
+ U32 face; // Which face was hit
+ F32 faceDot; // -Dot of face with poly normal
+ F32 distance;
+};
+
+struct CollisionList
+{
+ enum {
+ MaxCollisions = 64
+ };
+ int count;
+ Collision collision[MaxCollisions];
+ F32 t;
+ // MaxHeight is currently only set by the extrudedPolyList
+ // clipper. It represents the maximum vertex z value of
+ // the returned collision surfaces.
+ F32 maxHeight;
+};
+
+
+//----------------------------------------------------------------------------
+// BSP Collision tree
+// Solid nodes are represented by structures with NULL frontNode and
+// backNode pointers. The material field is only valid on a solid node.
+// There is no structure for empty nodes, frontNode or backNode
+// should be set to NULL to represent empty half-spaces.
+
+struct BSPNode
+{
+ U32 material;
+ PlaneF plane;
+ BSPNode *frontNode, *backNode;
+};
+
+typedef Chunker BSPTree;
+
+#endif
diff --git a/engine/collision/concretePolyList.cc b/engine/collision/concretePolyList.cc
new file mode 100755
index 0000000..1cf92fb
--- /dev/null
+++ b/engine/collision/concretePolyList.cc
@@ -0,0 +1,149 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "console/console.h"
+#include "collision/concretePolyList.h"
+
+
+//----------------------------------------------------------------------------
+
+ConcretePolyList::ConcretePolyList()
+{
+ VECTOR_SET_ASSOCIATION(mPolyList);
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mIndexList);
+ VECTOR_SET_ASSOCIATION(mPolyPlaneList);
+
+ mIndexList.reserve(100);
+}
+
+ConcretePolyList::~ConcretePolyList()
+{
+
+}
+
+
+//----------------------------------------------------------------------------
+void ConcretePolyList::clear()
+{
+ // Only clears internal data
+ mPolyList.clear();
+ mVertexList.clear();
+ mIndexList.clear();
+ mPolyPlaneList.clear();
+}
+
+//----------------------------------------------------------------------------
+
+U32 ConcretePolyList::addPoint(const Point3F& p)
+{
+ mVertexList.increment();
+ Point3F& v = mVertexList.last();
+ v.x = p.x * mScale.x;
+ v.y = p.y * mScale.y;
+ v.z = p.z * mScale.z;
+ mMatrix.mulP(v);
+
+ return mVertexList.size() - 1;
+}
+
+
+U32 ConcretePolyList::addPlane(const PlaneF& plane)
+{
+ mPolyPlaneList.increment();
+ mPlaneTransformer.transform(plane, mPolyPlaneList.last());
+
+ return mPolyPlaneList.size() - 1;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ConcretePolyList::begin(U32 material,U32 surfaceKey)
+{
+ mPolyList.increment();
+ Poly& poly = mPolyList.last();
+ poly.object = mCurrObject;
+ poly.material = material;
+ poly.vertexStart = mIndexList.size();
+ poly.surfaceKey = surfaceKey;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ConcretePolyList::plane(U32 v1,U32 v2,U32 v3)
+{
+ mPolyList.last().plane.set(mVertexList[v1],
+ mVertexList[v2],mVertexList[v3]);
+}
+
+void ConcretePolyList::plane(const PlaneF& p)
+{
+ mPlaneTransformer.transform(p, mPolyList.last().plane);
+}
+
+void ConcretePolyList::plane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ mPolyList.last().plane = mPolyPlaneList[index];
+}
+
+const PlaneF& ConcretePolyList::getIndexedPlane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ return mPolyPlaneList[index];
+}
+
+
+//----------------------------------------------------------------------------
+
+void ConcretePolyList::vertex(U32 vi)
+{
+ mIndexList.push_back(vi);
+}
+
+
+//----------------------------------------------------------------------------
+
+bool ConcretePolyList::isEmpty() const
+{
+ return false;
+}
+
+void ConcretePolyList::end()
+{
+ Poly& poly = mPolyList.last();
+ poly.vertexCount = mIndexList.size() - poly.vertexStart;
+}
+
+
+void ConcretePolyList::render()
+{
+ glVertexPointer(3,GL_FLOAT,sizeof(Point3F),mVertexList.address());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glColor4f(1,0,0,0.25);
+ glEnable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+ Poly * p;
+ for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
+ if(p->material != 0xFFFFFFFF)
+ glDrawElements(GL_POLYGON,p->vertexCount,
+ GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
+ }
+
+ glColor3f(0.6,0.6,0.6);
+ glDisable(GL_BLEND);
+ for (p = mPolyList.begin(); p < mPolyList.end(); p++) {
+ glDrawElements(GL_LINE_LOOP,p->vertexCount,
+ GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
+ }
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
diff --git a/engine/collision/concretePolyList.h b/engine/collision/concretePolyList.h
new file mode 100755
index 0000000..708ce1f
--- /dev/null
+++ b/engine/collision/concretePolyList.h
@@ -0,0 +1,66 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _CONCRETEPOLYLIST_H_
+#define _CONCRETEPOLYLIST_H_
+
+#ifndef _ABSTRACTPOLYLIST_H_
+#include "collision/abstractPolyList.h"
+#endif
+
+/// A concrete, renderable PolyList
+///
+/// This class is used to store geometry from a PolyList query.
+///
+/// It allows you to render this data, as well.
+///
+/// @see AbstractPolyList
+class ConcretePolyList : public AbstractPolyList
+{
+ public:
+
+ struct Poly {
+ PlaneF plane;
+ SceneObject* object;
+ U32 material;
+ U32 vertexStart;
+ U32 vertexCount;
+ U32 surfaceKey;
+ };
+
+ typedef Vector PlaneList;
+ typedef Vector VertexList;
+ typedef Vector PolyList;
+ typedef Vector IndexList;
+
+ PolyList mPolyList;
+ VertexList mVertexList;
+ IndexList mIndexList;
+
+ PlaneList mPolyPlaneList;
+
+ public:
+ ConcretePolyList();
+ ~ConcretePolyList();
+ void clear();
+
+ // Virtual methods
+ U32 addPoint(const Point3F& p);
+ U32 addPlane(const PlaneF& plane);
+ void begin(U32 material,U32 surfaceKey);
+ void plane(U32 v1,U32 v2,U32 v3);
+ void plane(const PlaneF& p);
+ void plane(const U32 index);
+ void vertex(U32 vi);
+ void end();
+
+ void render();
+ bool isEmpty() const;
+
+ protected:
+ const PlaneF& getIndexedPlane(const U32 index);
+};
+
+#endif // _H_EARLYOUTPOLYLIST_
diff --git a/engine/collision/convex.cc b/engine/collision/convex.cc
new file mode 100755
index 0000000..af73e11
--- /dev/null
+++ b/engine/collision/convex.cc
@@ -0,0 +1,641 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "dgl/dgl.h"
+#include "core/dataChunker.h"
+#include "collision/collision.h"
+#include "sceneGraph/sceneGraph.h"
+#include "sim/sceneObject.h"
+#include "terrain/terrData.h"
+#include "collision/convex.h"
+#include "collision/gjk.h"
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+static DataChunker sChunker;
+
+CollisionStateList CollisionStateList::sFreeList;
+CollisionWorkingList CollisionWorkingList::sFreeList;
+F32 sqrDistanceEdges(const Point3F& start0,
+ const Point3F& end0,
+ const Point3F& start1,
+ const Point3F& end1,
+ Point3F* is,
+ Point3F* it);
+
+
+//----------------------------------------------------------------------------
+// Collision State
+//----------------------------------------------------------------------------
+
+CollisionState::CollisionState()
+{
+ mLista = mListb = 0;
+}
+
+CollisionState::~CollisionState()
+{
+ if (mLista)
+ mLista->free();
+ if (mListb)
+ mListb->free();
+}
+
+void CollisionState::swap()
+{
+}
+
+void CollisionState::set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w)
+{
+}
+
+
+F32 CollisionState::distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
+ const MatrixF* w2a, const MatrixF* _w2b)
+{
+ return 0;
+}
+
+void CollisionState::render()
+{
+}
+
+
+
+//----------------------------------------------------------------------------
+// Feature Collision
+//----------------------------------------------------------------------------
+
+bool ConvexFeature::collide(ConvexFeature& cf,CollisionList* cList, F32 tol)
+{
+ // Our vertices vs. other faces
+ const Point3F* vert = mVertexList.begin();
+ const Point3F* vend = mVertexList.end();
+ while (vert != vend) {
+ cf.testVertex(*vert,cList,false, tol);
+ vert++;
+ }
+
+ // Other vertices vs. our faces
+ vert = cf.mVertexList.begin();
+ vend = cf.mVertexList.end();
+ while (vert != vend) {
+ U32 storeCount = cList->count;
+ testVertex(*vert,cList,true, tol);
+
+ // Fix up last reference. material and object are copied from this rather
+ // than the object we're colliding against.
+ if (storeCount != cList->count) {
+ cList->collision[cList->count - 1].material = cf.material;
+ cList->collision[cList->count - 1].object = cf.object;
+ }
+ vert++;
+ }
+
+ // Edge vs. Edge
+ const Edge* edge = mEdgeList.begin();
+ const Edge* eend = mEdgeList.end();
+ while (edge != eend) {
+ cf.testEdge(this,mVertexList[edge->vertex[0]],
+ mVertexList[edge->vertex[1]],cList, tol);
+ edge++;
+ }
+
+ return true;
+}
+
+inline bool isInside(const Point3F& p, const Point3F& a, const Point3F& b, const VectorF& n)
+{
+ VectorF v;
+ mCross(n,b - a,&v);
+ return mDot(v,p - a) < 0.0f;
+}
+
+void ConvexFeature::testVertex(const Point3F& v,CollisionList* cList,bool flip, F32 tol)
+{
+ // Test vertex against all faces
+ const Face* face = mFaceList.begin();
+ const Face* end = mFaceList.end();
+ for (; face != end; face++) {
+ if (cList->count >= CollisionList::MaxCollisions)
+ return;
+
+ const Point3F& p0 = mVertexList[face->vertex[0]];
+ const Point3F& p1 = mVertexList[face->vertex[1]];
+ const Point3F& p2 = mVertexList[face->vertex[2]];
+
+ // Point near the plane?
+ F32 distance = mDot(face->normal,v - p0);
+ if (distance > tol || distance < -tol)
+ continue;
+
+ // Make sure it's within the bounding edges
+ if (isInside(v,p0,p1,face->normal) && isInside(v,p1,p2,face->normal) &&
+ isInside(v,p2,p0,face->normal)) {
+
+ // Add collision to this face
+ Collision& info = cList->collision[cList->count++];
+ info.point = v;
+ info.normal = face->normal;
+ if (flip)
+ info.normal.neg();
+ info.material = material;
+ info.object = object;
+ info.distance = distance;
+ }
+ }
+}
+
+void ConvexFeature::testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol)
+{
+ F32 tolSquared = tol*tol;
+
+ // Test edges against edges
+ const Edge* edge = mEdgeList.begin();
+ const Edge* end = mEdgeList.end();
+ for (; edge != end; edge++) {
+ if (cList->count >= CollisionList::MaxCollisions)
+ return;
+
+ const Point3F& s2 = mVertexList[edge->vertex[0]];
+ const Point3F& e2 = mVertexList[edge->vertex[1]];
+
+ // Get the distance and closest points
+ Point3F i1,i2;
+ F32 distance = sqrDistanceEdges(s1, e1, s2, e2, &i1, &i2);
+ if (distance > tolSquared)
+ continue;
+ distance = mSqrt(distance);
+
+ // Need to figure out how to orient the collision normal.
+ // The current test involves checking to see whether the collision
+ // points are contained within the convex volumes, which is slow.
+ if (inVolume(i1) || cf->inVolume(i2))
+ distance = -distance;
+
+ // Contact normal
+ VectorF normal = i1 - i2;
+ normal *= 1 / distance;
+
+ // Return a collision
+ Collision& info = cList->collision[cList->count++];
+ info.point = i1;
+ info.normal = normal;
+ info.distance = distance;
+ info.material = material;
+ info.object = object;
+ }
+}
+
+bool ConvexFeature::inVolume(const Point3F& v)
+{
+ // Test the point to see if it's inside the volume
+ const Face* face = mFaceList.begin();
+ const Face* end = mFaceList.end();
+ for (; face != end; face++) {
+ const Point3F& p0 = mVertexList[face->vertex[0]];
+ if (mDot(face->normal,v - p0) > 0)
+ return false;
+ }
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+// Collision State management
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+
+CollisionStateList::CollisionStateList()
+{
+ mPrev = mNext = this;
+ mState = NULL;
+}
+
+void CollisionStateList::linkAfter(CollisionStateList* ptr)
+{
+ mPrev = ptr;
+ mNext = ptr->mNext;
+ ptr->mNext = this;
+ mNext->mPrev = this;
+}
+
+void CollisionStateList::unlink()
+{
+ mPrev->mNext = mNext;
+ mNext->mPrev = mPrev;
+ mPrev = mNext = this;
+}
+
+CollisionStateList* CollisionStateList::alloc()
+{
+ if (!sFreeList.isEmpty()) {
+ CollisionStateList* nxt = sFreeList.mNext;
+ nxt->unlink();
+ nxt->mState = NULL;
+ return nxt;
+ }
+ return constructInPlace((CollisionStateList*)sChunker.alloc(sizeof(CollisionStateList)));
+}
+
+void CollisionStateList::free()
+{
+ unlink();
+ linkAfter(&sFreeList);
+}
+
+
+//----------------------------------------------------------------------------
+
+CollisionWorkingList::CollisionWorkingList()
+{
+ wLink.mPrev = wLink.mNext = this;
+ rLink.mPrev = rLink.mNext = this;
+}
+
+void CollisionWorkingList::wLinkAfter(CollisionWorkingList* ptr)
+{
+ wLink.mPrev = ptr;
+ wLink.mNext = ptr->wLink.mNext;
+ ptr->wLink.mNext = this;
+ wLink.mNext->wLink.mPrev = this;
+}
+
+void CollisionWorkingList::rLinkAfter(CollisionWorkingList* ptr)
+{
+ rLink.mPrev = ptr;
+ rLink.mNext = ptr->rLink.mNext;
+ ptr->rLink.mNext = this;
+ rLink.mNext->rLink.mPrev = this;
+}
+
+void CollisionWorkingList::unlink()
+{
+ wLink.mPrev->wLink.mNext = wLink.mNext;
+ wLink.mNext->wLink.mPrev = wLink.mPrev;
+ wLink.mPrev = wLink.mNext = this;
+
+ rLink.mPrev->rLink.mNext = rLink.mNext;
+ rLink.mNext->rLink.mPrev = rLink.mPrev;
+ rLink.mPrev = rLink.mNext = this;
+}
+
+CollisionWorkingList* CollisionWorkingList::alloc()
+{
+ if (sFreeList.wLink.mNext != &sFreeList) {
+ CollisionWorkingList* nxt = sFreeList.wLink.mNext;
+ nxt->unlink();
+ return nxt;
+ }
+ return constructInPlace((CollisionWorkingList*)sChunker.alloc(sizeof(CollisionWorkingList)));
+}
+
+void CollisionWorkingList::free()
+{
+ unlink();
+ wLinkAfter(&sFreeList);
+}
+
+
+//----------------------------------------------------------------------------
+// Convex Base Class
+//----------------------------------------------------------------------------
+
+U32 Convex::sTag = (U32)-1;
+
+//----------------------------------------------------------------------------
+
+Convex::Convex()
+{
+ mNext = mPrev = this;
+ mTag = 0;
+}
+
+Convex::~Convex()
+{
+ // Unlink from Convex Database
+ unlink();
+
+ // Delete collision states
+ while (mList.mNext != &mList)
+ delete mList.mNext->mState;
+
+ // Free up working list
+ while (mWorking.wLink.mNext != &mWorking)
+ mWorking.wLink.mNext->free();
+
+ // Free up references
+ while (mReference.rLink.mNext != &mReference)
+ mReference.rLink.mNext->free();
+}
+
+
+//----------------------------------------------------------------------------
+
+void Convex::collectGarbage()
+{
+ // Delete unreferenced Convex Objects
+ for (Convex* itr = mNext; itr != this; itr = itr->mNext) {
+ if (itr->mReference.rLink.mNext == &itr->mReference) {
+ Convex* ptr = itr;
+ itr = itr->mPrev;
+ delete ptr;
+ }
+ }
+}
+
+void Convex::nukeList()
+{
+ // Delete all Convex Objects
+ for (Convex* itr = mNext; itr != this; itr = itr->mNext) {
+ Convex* ptr = itr;
+ itr = itr->mPrev;
+ delete ptr;
+ }
+}
+
+void Convex::registerObject(Convex *convex)
+{
+ convex->linkAfter(this);
+}
+
+
+//----------------------------------------------------------------------------
+
+void Convex::linkAfter(Convex* ptr)
+{
+ mPrev = ptr;
+ mNext = ptr->mNext;
+ ptr->mNext = this;
+ mNext->mPrev = this;
+}
+
+void Convex::unlink()
+{
+ mPrev->mNext = mNext;
+ mNext->mPrev = mPrev;
+ mPrev = mNext = this;
+}
+
+
+//----------------------------------------------------------------------------
+
+Point3F Convex::support(const VectorF&) const
+{
+ return Point3F(0,0,0);
+}
+
+void Convex::getFeatures(const MatrixF&,const VectorF&,ConvexFeature* f)
+{
+ f->object = NULL;
+}
+
+const MatrixF& Convex::getTransform() const
+{
+ return mObject->getTransform();
+}
+
+const Point3F& Convex::getScale() const
+{
+ return mObject->getScale();
+}
+
+Box3F Convex::getBoundingBox() const
+{
+ return mObject->getWorldBox();
+}
+
+Box3F Convex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const
+{
+ Box3F wBox = mObject->getObjBox();
+ wBox.min.convolve(scale);
+ wBox.max.convolve(scale);
+ mat.mul(wBox);
+ return wBox;
+}
+
+void Convex::render()
+{
+ for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext)
+ if (itr->mState->mLista == itr)
+ itr->mState->render();
+}
+
+
+//----------------------------------------------------------------------------
+
+void Convex::addToWorkingList(Convex* ptr)
+{
+ CollisionWorkingList* cl = CollisionWorkingList::alloc();
+ cl->wLinkAfter(&mWorking);
+ cl->rLinkAfter(&ptr->mReference);
+ cl->mConvex = ptr;
+};
+
+
+//----------------------------------------------------------------------------
+
+void Convex::updateWorkingList(const Box3F& box, const U32 colMask)
+{
+ sTag++;
+
+ // Clear objects off the working list that are no longer intersecting
+ for (CollisionWorkingList* itr = mWorking.wLink.mNext; itr != &mWorking; itr = itr->wLink.mNext) {
+ itr->mConvex->mTag = sTag;
+ if ((!box.isOverlapped(itr->mConvex->getBoundingBox())) || (!itr->mConvex->getObject()->isCollisionEnabled())) {
+ CollisionWorkingList* cl = itr;
+ itr = itr->wLink.mPrev;
+ cl->free();
+ }
+ }
+
+ // Special processing for the terrain and interiors...
+ AssertFatal(mObject->getContainer(), "Must be in a container!");
+
+ SimpleQueryList sql;
+ mObject->getContainer()->findObjects(box, colMask,SimpleQueryList::insertionCallback, &sql);
+ for (U32 i = 0; i < sql.mList.size(); i++)
+ sql.mList[i]->buildConvex(box, this);
+}
+
+// ---------------------------------------------------------------------------
+
+void Convex::updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement)
+{
+ Box3F box1 = getBoundingBox(mat, scale);
+ box1.min -= Point3F(1, 1, 1);
+ box1.max += Point3F(1, 1, 1);
+ if (displacement) {
+ Point3F oldMin = box1.min;
+ Point3F oldMax = box1.max;
+
+ box1.min.setMin(oldMin + *displacement);
+ box1.min.setMin(oldMax + *displacement);
+ box1.max.setMax(oldMin + *displacement);
+ box1.max.setMax(oldMax + *displacement);
+ }
+ sTag++;
+
+ // Destroy states which are no longer intersecting
+ for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
+ Convex* cv = (itr->mState->a == this)? itr->mState->b: itr->mState->a;
+ cv->mTag = sTag;
+ if (!box1.isOverlapped(cv->getBoundingBox())) {
+ CollisionState* cs = itr->mState;
+ itr = itr->mPrev;
+ delete cs;
+ }
+ }
+
+ // Add collision states for new overlapping objects
+ for (CollisionWorkingList* itr0 = mWorking.wLink.mNext; itr0 != &mWorking; itr0 = itr0->wLink.mNext) {
+ register Convex* cv = itr0->mConvex;
+ if (cv->mTag != sTag && box1.isOverlapped(cv->getBoundingBox())) {
+ CollisionState* state = new GjkCollisionState;
+ state->set(this,cv,mat,cv->getTransform());
+ state->mLista->linkAfter(&mList);
+ state->mListb->linkAfter(&cv->mList);
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+CollisionState* Convex::findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist)
+{
+ updateStateList(mat, scale);
+ F32 dist = +1E30;
+ CollisionState *st = 0;
+
+ // Prepare scaled version of transform
+ MatrixF axform = mat;
+ axform.scale(scale);
+ MatrixF axforminv(true);
+ MatrixF temp(mat);
+ axforminv.scale(Point3F(1.0f/scale.x,1.0f/scale.y,1.0f/scale.z));
+ temp.affineInverse();
+ axforminv.mul(temp);
+
+ for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
+ CollisionState* state = itr->mState;
+ if (state->mLista != itr)
+ state->swap();
+
+ // Prepare scaled version of transform
+ MatrixF bxform = state->b->getTransform();
+ temp = bxform;
+ Point3F bscale = state->b->getScale();
+ bxform.scale(bscale);
+ MatrixF bxforminv(true);
+ bxforminv.scale(Point3F(1.0f/bscale.x,1.0f/bscale.y,1.0f/bscale.z));
+ temp.affineInverse();
+ bxforminv.mul(temp);
+
+ //
+ F32 dd = state->distance(axform, bxform, dontCareDist, &axforminv, &bxforminv);
+ if (dd < dist) {
+ dist = dd;
+ st = state;
+ }
+ }
+ if (dist < dontCareDist)
+ return st;
+ else
+ return NULL;
+}
+
+
+//----------------------------------------------------------------------------
+
+bool Convex::getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol)
+{
+ for (CollisionStateList* itr = mList.mNext; itr != &mList; itr = itr->mNext) {
+ CollisionState* state = itr->mState;
+ if (state->mLista != itr)
+ state->swap();
+ if (state->dist <= tol) {
+ ConvexFeature fa,fb;
+ VectorF v;
+
+ // The idea is that we need to scale the matrix, so we need to
+ // make a copy of it, before we can pass it in to getFeatures.
+ // This is used to scale us for comparison against the other
+ // convex, which is correctly scaled.
+ MatrixF omat = mat;
+ omat.scale(scale);
+ MatrixF imat = omat;
+ imat.inverse();
+ imat.mulV(-state->v,&v);
+ getFeatures(omat,v,&fa);
+
+ imat = state->b->getTransform();
+ imat.scale(state->b->getScale());
+ MatrixF bxform = imat;
+ imat.inverse();
+ imat.mulV(state->v,&v);
+ state->b->getFeatures(bxform,v,&fb);
+
+ fa.collide(fb,cList,tol);
+ }
+ }
+ return (cList->count != 0);
+}
+
+void Convex::getPolyList(AbstractPolyList*)
+{
+
+}
+
+
+//-----------------------------------------------------------------------------
+// This function based on code orignally written for the book:
+// 3D Game Engine Design, by David H. Eberly
+//
+F32 sqrDistanceEdges(const Point3F& start0, const Point3F& end0,
+ const Point3F& start1, const Point3F& end1,
+ Point3F* is, Point3F* it)
+{
+ Point3F direction0 = end0 - start0;
+ F32 fA00 = direction0.lenSquared();
+
+ Point3F direction1 = end1 - start1;
+ F32 fA11 = direction1.lenSquared();
+ F32 fA01 = -mDot(direction0, direction1);
+
+ Point3F kDiff = start0 - start1;
+ F32 fC = kDiff.lenSquared();
+ F32 fB0 = mDot(kDiff, direction0);
+ F32 fDet = mAbs((S32)(fA00*fA11 - fA01*fA01));
+
+ // Since the endpoints are tested as vertices, we're not interested
+ // in parallel lines, and intersections that don't involve end-points.
+ if (fDet >= 0.00001) {
+
+ // Calculate time of intersection for each line
+ F32 fB1 = -mDot(kDiff, direction1);
+ F32 fS = fA01*fB1-fA11*fB0;
+ F32 fT = fA01*fB0-fA00*fB1;
+
+ // Only interested in collisions that don't involve the end points
+ if (fS >= 0.0 && fS <= fDet && fT >= 0.0 && fT <= fDet) {
+ F32 fInvDet = 1.0 / fDet;
+ fS *= fInvDet;
+ fT *= fInvDet;
+ F32 fSqrDist = (fS*(fA00*fS + fA01*fT + 2.0*fB0) +
+ fT*(fA01*fS + fA11*fT + 2.0*fB1) + fC);
+
+ // Intersection points.
+ *is = start0 + direction0 * fS;
+ *it = start1 + direction1 * fT;
+ return mFabs(fSqrDist);
+ }
+ }
+
+ // Return a large number in the cases where endpoints are involved.
+ return 1e10f;
+}
diff --git a/engine/collision/convex.h b/engine/collision/convex.h
new file mode 100755
index 0000000..0be5b59
--- /dev/null
+++ b/engine/collision/convex.h
@@ -0,0 +1,249 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _CONVEX_H_
+#define _CONVEX_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+
+struct Collision;
+struct CollisionList;
+struct CollisionStateList;
+class AbstractPolyList;
+class SceneObject;
+class Convex;
+
+
+//----------------------------------------------------------------------------
+
+class ConvexFeature
+{
+public:
+ struct Edge {
+ S32 vertex[2];
+ };
+ struct Face {
+ VectorF normal;
+ S32 vertex[3];
+ };
+
+ Vector mVertexList;
+ Vector mEdgeList;
+ Vector mFaceList;
+ S32 material;
+ SceneObject* object;
+
+ ConvexFeature() : mVertexList(64), mEdgeList(128), mFaceList(64) {
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mEdgeList);
+ VECTOR_SET_ASSOCIATION(mFaceList);
+ }
+
+ bool collide(ConvexFeature& cf,CollisionList* cList, F32 tol = 0.1);
+ void testVertex(const Point3F& v,CollisionList* cList,bool,F32 tol);
+ void testEdge(ConvexFeature* cf,const Point3F& s1, const Point3F& e1, CollisionList* cList, F32 tol);
+ bool inVolume(const Point3F& v);
+};
+
+
+//----------------------------------------------------------------------------
+
+enum ConvexType {
+ TSConvexType,
+ BoxConvexType,
+ TerrainConvexType,
+ InteriorConvexType,
+ ShapeBaseConvexType,
+ TSStaticConvexType,
+ InteriorMapConvexType
+};
+
+
+//----------------------------------------------------------------------------
+
+struct CollisionState
+{
+ CollisionStateList* mLista;
+ CollisionStateList* mListb;
+ Convex* a;
+ Convex* b;
+
+ F32 dist; // Current estimated distance
+ VectorF v; // Vector between closest points
+
+ //
+ CollisionState();
+ virtual ~CollisionState();
+ virtual void swap();
+ virtual void set(Convex* a,Convex* b,const MatrixF& a2w, const MatrixF& b2w);
+ virtual F32 distance(const MatrixF& a2w, const MatrixF& b2w, const F32 dontCareDist,
+ const MatrixF* w2a = NULL, const MatrixF* _w2b = NULL);
+ void render();
+};
+
+
+//----------------------------------------------------------------------------
+
+struct CollisionStateList
+{
+ static CollisionStateList sFreeList;
+ CollisionStateList* mNext;
+ CollisionStateList* mPrev;
+ CollisionState* mState;
+
+ CollisionStateList();
+
+ void linkAfter(CollisionStateList* next);
+ void unlink();
+ bool isEmpty() { return mNext == this; }
+
+ static CollisionStateList* alloc();
+ void free();
+};
+
+
+//----------------------------------------------------------------------------
+
+struct CollisionWorkingList
+{
+ static CollisionWorkingList sFreeList;
+ struct WLink {
+ CollisionWorkingList* mNext;
+ CollisionWorkingList* mPrev;
+ } wLink;
+ struct RLink {
+ CollisionWorkingList* mNext;
+ CollisionWorkingList* mPrev;
+ } rLink;
+ Convex* mConvex;
+
+ void wLinkAfter(CollisionWorkingList* next);
+ void rLinkAfter(CollisionWorkingList* next);
+ void unlink();
+ CollisionWorkingList();
+
+ static CollisionWorkingList* alloc();
+ void free();
+};
+
+
+//----------------------------------------------------------------------------
+
+class Convex {
+
+ /// @name Linked list managent
+ /// @{
+ Convex* mNext; ///< Next item in linked list of Convex's
+ Convex* mPrev; ///< Previous item in linked list of Convex's
+
+ /// Insert this Convex after the provided convex
+ /// @param next
+ void linkAfter(Convex* next);
+
+ /// Remove this Convex from the linked list
+ void unlink();
+ /// @}
+
+ U32 mTag;
+ static U32 sTag;
+
+protected:
+ CollisionStateList mList; ///< Objects we're testing against
+ CollisionWorkingList mWorking; ///< Objects within our bounds
+ CollisionWorkingList mReference; ///< Other convex testing against us
+ SceneObject* mObject; ///< Object this Convex is built around
+ ConvexType mType; ///< Type of Convex this is @see ConvexType
+
+ //
+public:
+
+ /// Constructor
+ Convex();
+
+ /// Destructor
+ virtual ~Convex();
+
+ /// Registers another Convex by linking it after this one
+ void registerObject(Convex *convex);
+
+ /// Runs through the linked list of Convex objects and removes the ones
+ /// with no references
+ void collectGarbage();
+
+ /// Deletes all convex objects in the list
+ void nukeList();
+
+ /// Returns the type of this Convex
+ ConvexType getType() { return mType; }
+
+ /// Returns the object this Convex is built from
+ SceneObject* getObject() { return mObject; }
+
+ /// Traverses mList and renders all collision states
+ void render();
+
+ /// Adds the provided Convex to the list of objects within the bounds of this Convex
+ /// @param ptr Convex to add to the working list of this object
+ void addToWorkingList(Convex* ptr);
+
+ /// Returns the list of objects currently inside the bounds of this Convex
+ CollisionWorkingList& getWorkingList() { return mWorking; }
+
+ /// Finds the closest
+ CollisionState* findClosestState(const MatrixF& mat, const Point3F& scale, const F32 dontCareDist = 1);
+
+ /// Returns the list of objects this object is testing against
+ CollisionStateList* getStateList() { return mList.mNext; }
+
+ /// Updates the CollisionStateList (mList) with new collision states and removing
+ /// ones no longer under consideration
+ /// @param mat Used as the matrix to create a bounding box for updating the list
+ /// @param scale Used to scale the bounding box
+ /// @param displacement Bounding box displacement (optional)
+ void updateStateList(const MatrixF& mat, const Point3F& scale, const Point3F* displacement = NULL);
+
+ /// Updates the working collision list of objects which are currently colliding with
+ /// (inside the bounds of) this Convex.
+ ///
+ /// @param box Used as the bounding box.
+ /// @param colMask Mask of objects to check against.
+ void updateWorkingList(const Box3F& box, const U32 colMask);
+
+ /// Returns the transform of the object this is built around
+ virtual const MatrixF& getTransform() const;
+
+ /// Returns the scale of the object this is built around
+ virtual const Point3F& getScale() const;
+
+ /// Returns the bounding box for the object this is built around in world space
+ virtual Box3F getBoundingBox() const;
+
+ /// Returns the object space bounding box for the object this is built around
+ /// transformed and scaled
+ /// @param mat Matrix to transform the object-space box by
+ /// @param scale Scaling factor to scale the bounding box by
+ virtual Box3F getBoundingBox(const MatrixF& mat, const Point3F& scale) const;
+
+ /// Returns the farthest point, along a vector, still bound by the convex
+ /// @param v Vector
+ virtual Point3F support(const VectorF& v) const;
+
+ ///
+ virtual void getFeatures(const MatrixF& mat,const VectorF& n, ConvexFeature* cf);
+
+ /// Builds a collision poly list out of this convex
+ /// @param list (Out) Poly list built
+ virtual void getPolyList(AbstractPolyList* list);
+
+ ///
+ bool getCollisionInfo(const MatrixF& mat, const Point3F& scale, CollisionList* cList,F32 tol);
+};
+
+#endif
diff --git a/engine/collision/convexBrush.cc b/engine/collision/convexBrush.cc
new file mode 100755
index 0000000..c1762b6
--- /dev/null
+++ b/engine/collision/convexBrush.cc
@@ -0,0 +1,1290 @@
+#include "collision/convexBrush.h"
+
+#include "dgl/dgl.h"
+#include "math/mRandom.h"
+#include "game/gameConnection.h"
+#include "math/mPlaneTransformer.h"
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+ConvexBrush::ConvexBrush()
+{
+ mBounds.min = Point3F(0, 0, 0);
+ mBounds.max = Point3F(0, 0, 0);
+
+ mBrushScale = 1.0f;
+ mType = 0;
+ mCentroid = Point3F(0.0f, 0.0f, 0.0f);
+ mStatus = Unprocessed;
+ mSelected = false;
+
+ // Set our transform to no translation or rotation
+ mTransform.identity();
+
+ mTransformScratch.identity();
+ mRotationScratch.identity();
+
+ mScale.set(1.0f, 1.0f, 1.0f);
+ mRotation.set(EulerF(0.0f, 0.0f, 0.0f));
+
+ // No id assigned yet
+ mID = -1;
+
+ mOwner = NULL;
+}
+
+ConvexBrush::~ConvexBrush()
+{
+}
+
+// Just adds a plane to the list and default values for texture info
+bool ConvexBrush::addPlane(PlaneF pln)
+{
+ mFaces.mPolyList.increment();
+ mFaces.mPolyList.last().material = 0;
+ mFaces.mPolyList.last().object = NULL;
+ mFaces.mPolyList.last().surfaceKey = 0;
+ mFaces.mPolyList.last().vertexCount = 0;
+ mFaces.mPolyList.last().vertexStart = 0;
+ mFaces.mPolyList.last().plane = mFaces.addPlane(pln);
+
+ // The texgens and the faces increase together
+ mTexInfos.increment();
+ mTexInfos.last().texGens[0] = PlaneF(1.0f, 0.0f, 0.0f, 1.0f);
+ mTexInfos.last().texGens[1] = PlaneF(0.0f, 0.0f, 1.0f, 1.0f);
+ mTexInfos.last().scale[0] = 1.0f;
+ mTexInfos.last().scale[1] = 1.0f;
+ mTexInfos.last().rot = 0.0f;
+ mTexInfos.last().texDiv[0] = 1.0f;
+ mTexInfos.last().texDiv[1] = 1.0f;
+ mTexInfos.last().texture = StringTable->insert("");
+
+ return true;
+}
+
+bool ConvexBrush::addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture)
+{
+ addPlane(pln);
+
+ mTexInfos.last().texGens[0] = texGen[0];
+ mTexInfos.last().texGens[1] = texGen[1];
+ mTexInfos.last().scale[0] = scale[0];
+ mTexInfos.last().scale[1] = scale[1];
+ mTexInfos.last().texture = StringTable->insert(texture);
+
+ return true;
+}
+
+bool ConvexBrush::addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture, U32 matIdx)
+{
+ addFace(pln, texGen, scale, texture);
+
+ mFaces.mPolyList.last().material = matIdx;
+
+ return true;
+}
+
+bool ConvexBrush::addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], U32 matIdx)
+{
+ return addFace(pln, texGen, scale, "", matIdx);;
+}
+
+// Call this after you have added the planes to generate the rest of the data
+bool ConvexBrush::processBrush()
+{
+ // It is possible that our status was changed externally by the parser
+ if (mStatus != Unprocessed)
+ return false;
+
+ // First check to see if we have enough planes
+ if (mFaces.mPlaneList.size() < 4)
+ {
+ mStatus = ShortPlanes;
+ char debug[512];
+ dSprintf(debug, 512, "This brush only has %d planes which is insufficent to create a convex volume. Possibly a badly formatted .map?", mFaces.mPlaneList.size());
+ mDebugInfo = StringTable->insert(debug);
+ return false;
+ }
+
+ // Generate the geometry
+ selfClip();
+
+ // Verify that all of the faces have at least 3 vertices
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ if (mFaces.mPolyList[i].vertexCount < 3)
+ {
+ mStatus = BadFaces;
+ mDebugInfo = StringTable->insert("There are bad faces on this brush. This is most likely caused by a flat or non-volumetric brush.");
+ return false;
+ }
+ }
+
+ // Generate proper windings for the faces
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ // Create a temporary index list
+ Vector indexes;
+ for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
+ indexes.push_back(mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j]);
+
+ // Create a buffer to return the winding to
+ Vector winding;
+
+ if (createWinding(indexes, mFaces.mPlaneList[mFaces.mPolyList[i].plane], winding))
+ {
+ // Copy the correctly order indices back
+ for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
+ mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j] = winding[j];
+ }
+ else
+ {
+ mStatus = BadWinding;
+ mDebugInfo = StringTable->insert("Unable to generate windings for some of the faces. This is often caused by having brushes that are too thin.");
+ return false;
+ }
+
+ indexes.clear();
+ winding.clear();
+ }
+
+ //validateWindings();
+
+ generateEdgelist();
+
+ if (!validateEdges())
+ return false;
+
+ calcBounds();
+
+ // If we got this far without any errors consider the brush to be good
+ if (mStatus == Unprocessed)
+ mStatus = Good;
+ else
+ return false;
+
+ return true;
+}
+
+void ConvexBrush::calcCentroid()
+{
+ // First find the centroid
+ for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
+ mCentroid += mFaces.mVertexList[i];
+
+ mCentroid /= mFaces.mVertexList.size();
+}
+
+void ConvexBrush::setupTransform()
+{
+ // First calculate the centroid
+ calcCentroid();
+
+ mTransform.setColumn(3, mCentroid);
+
+ mCentroid.set(0.0f, 0.0f, 0.0f);
+
+ // Now untransform the verts and planes
+ MatrixF invTrans = mTransform;
+ invTrans.inverse();
+
+ // Untransform our verts
+ for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
+ invTrans.mulP(mFaces.mVertexList[i]);
+
+ // Untransform our bounds
+ invTrans.mulP(mBounds.min);
+ invTrans.mulP(mBounds.max);
+
+ // Untransform our planes
+ PlaneTransformer trans;
+ trans.set(invTrans, Point3F(1.0f, 1.0f, 1.0f));
+
+ for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
+ {
+ PlaneF temp;
+ trans.transform(mFaces.mPlaneList[i], temp);
+ mFaces.mPlaneList[i] = temp;
+ }
+}
+
+bool ConvexBrush::validateEdges()
+{
+ // Check to make sure that ever edge has two faces
+ for (U32 i = 0; i < mFaces.mEdgeList.size(); i++)
+ {
+ OptimizedPolyList::Edge ed = mFaces.mEdgeList[i];
+ if (mFaces.mEdgeList[i].faces[1] == -1)
+ {
+ mStatus = Concave;
+ mDebugInfo = StringTable->insert("There are missing faces on this brush. This is caused by having concave edges in the brush.");
+ return false;
+ }
+ }
+
+ // Now test for concavity
+ for (U32 i = 0; i < mFaces.mEdgeList.size(); i++)
+ {
+ U32 face0 = mFaces.mEdgeList[i].faces[0];
+ U32 face1 = mFaces.mEdgeList[i].faces[1];
+
+ // Get the indices that make up the edge
+ U32 i0 = mFaces.mEdgeList[i].vertexes[0];
+ U32 i1 = mFaces.mEdgeList[i].vertexes[1];
+
+ // Now get a point from face0 that isn't on the edge
+ U32 nsx;
+ for (U32 j = 0; j < mFaces.mPolyList[face0].vertexCount; j++)
+ {
+ nsx = mFaces.mIndexList[mFaces.mPolyList[face0].vertexStart + j];
+
+ if (nsx != i0 && nsx != i1)
+ break;
+ }
+
+ // Get the vertices
+ Point3F ns = mFaces.mVertexList[nsx];
+
+ // Grab the plane of face1
+ PlaneF pln = mFaces.mPlaneList[mFaces.mPolyList[face1].plane];
+
+ // Test to see if the point is in front or behind
+ if (pln.whichSide(ns) == PlaneF::Back)
+ mFaces.mEdgeList[i].type = OptimizedPolyList::Convex;
+ else
+ {
+ mFaces.mEdgeList[i].type = OptimizedPolyList::Concave;
+ mStatus = Concave;
+ mDebugInfo = StringTable->insert("Found some concave edges on this brush.");
+ }
+ }
+
+ if (mStatus == Concave)
+ return false;
+ else
+ return true;
+}
+
+void ConvexBrush::validateWindings()
+{
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ if (mFaces.mPolyList[i].vertexCount < 3)
+ continue;
+
+ U32 i0 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart];
+ U32 i1 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + 1];
+ U32 i2 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + 2];
+
+ Point3F p0 = mFaces.mVertexList[i0];
+ Point3F p1 = mFaces.mVertexList[i1];
+ Point3F p2 = mFaces.mVertexList[i2];
+
+ PlaneF wnorm(p0, p1, p2);
+
+ if (wnorm.whichSide(mCentroid) == PlaneF::Front)
+ {
+ Con::errorf("Face %d's winding is inverted", i);
+
+ // Let's reverse the winding
+ Vector fixed;
+
+ for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
+ fixed.push_front(mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j]);
+
+ for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
+ mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j] = fixed[j];
+ }
+ }
+}
+
+void ConvexBrush::addIndex(Vector& indices, U32 index)
+{
+ for (U32 i = 0; i < indices.size(); i++)
+ {
+ if (indices[i] == index)
+ return;
+ }
+
+ indices.push_back(index);
+}
+
+// This clips the planes against each other
+bool ConvexBrush::selfClip()
+{
+ // Used to hold our indices temporarily
+ Vector* faceIndices = new Vector[mFaces.mPolyList.size()];
+
+ //Con::errorf("New brush");
+ //for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
+ // Con::printf("plane %d(%g, %g, %g, %g)", i, mFaces.mPlaneList[i].x, mFaces.mPlaneList[i].y, mFaces.mPlaneList[i].z, mFaces.mPlaneList[i].d);
+
+ // Clip them against each other to generate the vertexes
+ for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
+ {
+ for (U32 j = i+1; j < mFaces.mPlaneList.size(); j++)
+ {
+ for (U32 k = j+1; k < mFaces.mPlaneList.size(); k++)
+ {
+ Point3D interd;
+ Point3F interf;
+
+ bool doesSersect = intersectPlanes(mFaces.mPlaneList[i], mFaces.mPlaneList[j], mFaces.mPlaneList[k], &interd);
+
+ interf = Point3F(interd.x, interd.y, interd.z);
+
+ if (doesSersect)
+ {
+ // Check to make sure that the point is behind or on all of the
+ // planes
+ bool inOrOn = true;
+
+ for (U32 l = 0; l < mFaces.mPlaneList.size(); l++)
+ {
+ if (mFaces.mPlaneList[l].whichSide(interf) == PlaneF::Front)
+ {
+ inOrOn = false;
+ break;
+ }
+ }
+
+ if (inOrOn == true)
+ {
+ U32 vdx = mFaces.addPoint(interf);
+
+ addIndex(faceIndices[i], vdx);
+ addIndex(faceIndices[j], vdx);
+ addIndex(faceIndices[k], vdx);
+ }
+ }
+ }
+ }
+ }
+
+ // Add the indices for the faces
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ // Set the starting index
+ mFaces.mPolyList[i].vertexStart = mFaces.mIndexList.size();
+ // Set the number of indexes
+ mFaces.mPolyList[i].vertexCount = faceIndices[i].size();
+
+ // Copy the indexes into the index list
+ for (U32 j = 0; j < faceIndices[i].size(); j++)
+ {
+ mFaces.mIndexList.push_back(faceIndices[i][j]);
+ }
+ }
+
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ faceIndices[i].clear();
+
+ delete [] faceIndices;
+
+ return true;
+}
+
+bool ConvexBrush::intersectPlanes(const PlaneF& plane1, const PlaneF& plane2, const PlaneF& plane3, Point3D* pOutput)
+{
+ F64 bc = (plane2.y * plane3.z) - (plane3.y * plane2.z);
+ F64 ac = (plane2.x * plane3.z) - (plane3.x * plane2.z);
+ F64 ab = (plane2.x * plane3.y) - (plane3.x * plane2.y);
+ F64 det = (plane1.x * bc) - (plane1.y * ac) + (plane1.z * ab);
+
+ if (mFabs(det) < 1e-7)
+ {
+ // Parallel planes
+ return false;
+ }
+
+ F64 dc = (plane2.d * plane3.z) - (plane3.d * plane2.z);
+ F64 db = (plane2.d * plane3.y) - (plane3.d * plane2.y);
+ F64 ad = (plane3.d * plane2.x) - (plane2.d * plane3.x);
+ F64 detInv = 1.0 / det;
+
+ pOutput->x = ((plane1.y * dc) - (plane1.d * bc) - (plane1.z * db)) * detInv;
+ pOutput->y = ((plane1.d * ac) - (plane1.x * dc) - (plane1.z * ad)) * detInv;
+ pOutput->z = ((plane1.y * ad) + (plane1.x * db) - (plane1.d * ab)) * detInv;
+
+ return true;
+}
+
+bool ConvexBrush::createWinding(const Vector& rPoints, const Point3F& normal, Vector& rWinding)
+{
+ if (rPoints.size() < 3)
+ return false;
+
+ Vector modPoints;
+ Point3F centroid(0, 0, 0);
+ Point3F projcent(0, 0, 0);
+
+ modPoints = rPoints;
+
+ // Create a centroid first...
+ for (U32 i = 0; i < modPoints.size(); i++)
+ centroid += mFaces.mVertexList[modPoints[i]];
+ centroid /= F32(modPoints.size());
+
+ // Create a point projected along the normal
+ projcent = centroid + normal;
+
+ // Ok, we have a centroid. We (arbitrarily) start with the last point
+ rWinding.increment();
+ rWinding.last() = modPoints[modPoints.size() - 1];
+ modPoints.erase(modPoints.size() - 1);
+
+ while (modPoints.size() > 0)
+ {
+ // Get our last winding point
+ Point3F& currPoint = mFaces.mVertexList[rWinding.last()];
+
+ // Calculate the vector between the centroid and the current point
+ VectorF currVector = currPoint - centroid;
+ currVector.normalize();
+
+ // Create a plane with the winding point, the centroid, and the projected centroid
+ PlaneF testPlane(currPoint, centroid, projcent);
+
+ F32 maxDot = -1e10;
+ S32 maxIdx = -1;
+
+ for (U32 i = 0; i < modPoints.size(); i++)
+ {
+ Point3F& testPoint = mFaces.mVertexList[modPoints[i]];
+
+ if (testPlane.whichSide(testPoint) == PlaneF::Front)
+ {
+ // Get the vector between this point and the centroid
+ VectorF testVector = testPoint - centroid;
+ testVector.normalize();
+
+ F32 dot = mDot(testVector, currVector);
+
+ if (dot > maxDot)
+ {
+ maxDot = dot;
+ maxIdx = i;
+ }
+ }
+ }
+
+ if (maxIdx == -1)
+ {
+ // Sometimes the last vertex doesn't make it onto the list
+ if (modPoints.size() == 1)
+ {
+ rWinding.increment();
+ rWinding.last() = modPoints.last();
+ return true;
+ }
+ else
+ {
+ Con::errorf("ConvexBrush::createWinding(): was unable to find the next winding point");
+
+ // For debugging
+ U32 pt = 0;
+ for (U32 i = 0; i < rWinding.size(); i++)
+ {
+ Point3F& point = mFaces.mVertexList[rWinding[i]];
+
+ Con::printf(" point %d (%g, %g, %g)", pt, point.x, point.y, point.z);
+ }
+
+ for (U32 i = 0; i < modPoints.size(); i++)
+ {
+ Point3F& point = mFaces.mVertexList[modPoints[i]];
+
+ Con::printf(" point %d (%g, %g, %g)", pt, point.x, point.y, point.z);
+ }
+
+ return false;
+ }
+ }
+
+ rWinding.increment();
+ rWinding.last() = modPoints[maxIdx];
+ modPoints.erase(maxIdx);
+ }
+
+ modPoints.clear();
+
+ return true;
+}
+
+bool ConvexBrush::render(bool genColors)
+{
+ // Renders it in colors only
+ if (genColors)
+ gRandGen.setSeed(1978);
+
+ glEnable(GL_CULL_FACE);
+
+ // Pick a random color for our brush
+ if (!genColors)
+ {
+ ColorF color(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f));
+ glColor3f(color.red, color.green, color.blue);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+ }
+
+ for (U32 j = 0; j < mFaces.mPolyList.size(); j++)
+ {
+ S32 tx = mFaces.mPolyList[j].material;
+
+ if (tx == -2)
+ continue;
+
+ glBegin(GL_TRIANGLES);
+ for (U32 k = 1; k < mFaces.mPolyList[j].vertexCount - 1; k++)
+ {
+ U32 i0 = mFaces.mIndexList[mFaces.mPolyList[j].vertexStart];
+ U32 i1 = mFaces.mIndexList[mFaces.mPolyList[j].vertexStart + k];
+ U32 i2 = mFaces.mIndexList[mFaces.mPolyList[j].vertexStart + k + 1];
+
+ Point3F p0 = mFaces.mVertexList[i0];
+ Point3F p1 = mFaces.mVertexList[i1];
+ Point3F p2 = mFaces.mVertexList[i2];
+
+ if (genColors)
+ {
+ ColorF color(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f));
+ glColor3f(color.red, color.green, color.blue);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+ }
+
+ PlaneF normal = mFaces.mPlaneList[mFaces.mPolyList[k].plane];
+ glNormal3f(normal.x, normal.y, normal.z);
+
+ F32 s, t;
+
+ PlaneF texGenX = mTexInfos[k].texGens[0];
+ PlaneF texGenY = mTexInfos[k].texGens[1];
+
+ s = texGenX.distToPlane(p0 * mBrushScale);
+ t = texGenY.distToPlane(p0 * mBrushScale);
+
+ glTexCoord2f(s, t);
+ glVertex3f(p0.x, p0.y, p0.z);
+
+ s = texGenX.distToPlane(p1 * mBrushScale);
+ t = texGenY.distToPlane(p1 * mBrushScale);
+
+ glTexCoord2f(s, t);
+ glVertex3f(p1.x, p1.y, p1.z);
+
+ s = texGenX.distToPlane(p2 * mBrushScale);
+ t = texGenY.distToPlane(p2 * mBrushScale);
+
+ glTexCoord2f(s, t);
+ glVertex3f(p2.x, p2.y, p2.z);
+ }
+ glEnd();
+ }
+
+ return true;
+}
+
+bool ConvexBrush::renderFace(U32 face, bool renderLighting)
+{
+ if (face > mFaces.mPolyList.size())
+ {
+ Con::errorf("ConvexBrush::debugRenderFace(): face is outside range");
+ return false;
+ }
+
+ if (mFaces.mPolyList[face].vertexCount < 3)
+ return false;
+
+ const OptimizedPolyList::TriangleLighting *lighting = NULL;
+ Point3F tp;
+ PlaneF lmGenX, lmGenY;
+ if(renderLighting)
+ glActiveTextureARB(GL_TEXTURE1_ARB);
+
+ for (U32 k = 1; k < mFaces.mPolyList[face].vertexCount - 1; k++)
+ {
+ U32 i0 = mFaces.mIndexList[mFaces.mPolyList[face].vertexStart];
+ U32 i1 = mFaces.mIndexList[mFaces.mPolyList[face].vertexStart + k];
+ U32 i2 = mFaces.mIndexList[mFaces.mPolyList[face].vertexStart + k + 1];
+
+ Point3F p0 = mFaces.mVertexList[i0];
+ Point3F p1 = mFaces.mVertexList[i1];
+ Point3F p2 = mFaces.mVertexList[i2];
+
+ if((renderLighting) && ((lighting = mFaces.getTriangleLighting(mFaces.mPolyList[face].triangleLightingStartIndex + (k - 1))) != NULL))
+ {
+ lmGenX = lighting->lightMapEquationX;
+ lmGenY = lighting->lightMapEquationY;
+ glBindTexture(GL_TEXTURE_2D, lighting->lightMapId);
+ }
+
+ glBegin(GL_TRIANGLES);
+
+ PlaneF normal = mFaces.mPlaneList[mFaces.mPolyList[face].plane];
+ glNormal3f(normal.x, normal.y, normal.z);
+
+ F32 s, t;
+
+ PlaneF texGenX = mTexInfos[face].texGens[0];
+ PlaneF texGenY = mTexInfos[face].texGens[1];
+
+ tp = p0;
+ s = texGenX.distToPlane(tp);
+ t = texGenY.distToPlane(tp);
+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t);
+
+ if(lighting)
+ {
+ mLightingTransform.mulP(tp);
+ s = lmGenX.distToPlane(tp);
+ t = lmGenY.distToPlane(tp);
+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, t);
+ }
+
+ glVertex3f(p0.x, p0.y, p0.z);
+
+ tp = p1;
+ s = texGenX.distToPlane(tp);
+ t = texGenY.distToPlane(tp);
+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t);
+
+ if(lighting)
+ {
+ mLightingTransform.mulP(tp);
+ s = lmGenX.distToPlane(tp);
+ t = lmGenY.distToPlane(tp);
+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, t);
+ }
+
+ glVertex3f(p1.x, p1.y, p1.z);
+
+ tp = p2;
+ s = texGenX.distToPlane(tp);
+ t = texGenY.distToPlane(tp);
+ glMultiTexCoord2fARB(GL_TEXTURE0_ARB, s, t);
+
+ if(lighting)
+ {
+ mLightingTransform.mulP(tp);
+ s = lmGenX.distToPlane(tp);
+ t = lmGenY.distToPlane(tp);
+ glMultiTexCoord2fARB(GL_TEXTURE1_ARB, s, t);
+ }
+
+ glVertex3f(p2.x, p2.y, p2.z);
+
+ glEnd();
+ }
+
+ if(renderLighting)
+ glActiveTextureARB(GL_TEXTURE0_ARB);
+
+ return true;
+}
+
+bool ConvexBrush::renderEdges(ColorF color)
+{
+ // Save ModelView Matrix so we can restore Canonical state at exit.
+ glPushMatrix();
+
+ // Transform by the objects' transform e.g move it.
+ dglMultMatrix(&mTransform);
+
+ // Scale
+ //glScalef(mScale.x, mScale.y, mScale.z);
+
+ glBegin(GL_LINES);
+
+ for (U32 i = 0; i < mFaces.mEdgeList.size(); i++)
+ {
+ OptimizedPolyList::Edge ed = mFaces.mEdgeList[i];
+ //if (mFaces.mEdgeList[i].type == OptimizedPolyList::Convex)
+ // glColor3f(0.0f, 1.0f, 0.0f);
+ //else if (mFaces.mEdgeList[i].type == OptimizedPolyList::Concave)
+ // glColor3f(1.0f, 0.0f, 0.0f);
+ //else
+ // glColor3f(0.0f, 0.0f, 1.0f);
+
+ glColor4f(color.red, color.green, color.blue, color.alpha);
+
+ U32 i0 = mFaces.mEdgeList[i].vertexes[0];
+ U32 i1 = mFaces.mEdgeList[i].vertexes[1];
+
+ Point3F p0 = mFaces.mVertexList[i0];
+ Point3F p1 = mFaces.mVertexList[i1];
+
+ glVertex3f(p0.x, p0.y, p0.z);
+ glVertex3f(p1.x, p1.y, p1.z);
+ }
+
+ glEnd();
+
+ glPopMatrix();
+
+ return true;
+}
+
+bool ConvexBrush::calcBounds()
+{
+ // Rebuild our bounds
+ if (mFaces.mVertexList.size() > 0)
+ {
+ mBounds.min.set(1e10, 1e10, 1e10);
+ mBounds.max.set(-1e10, -1e10, -1e10);
+
+ // Build our bounds
+ for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
+ {
+ mBounds.min.setMin(mFaces.mVertexList[i]);
+ mBounds.max.setMax(mFaces.mVertexList[i]);
+ }
+ }
+
+ return true;
+}
+
+bool ConvexBrush::setScale(F32 scale)
+{
+ mBrushScale = scale;
+
+ // Scale our verts
+ for (U32 i = 0; i < mFaces.mVertexList.size(); i++)
+ mFaces.mVertexList[i] /= mBrushScale;
+
+ // Scale the planes
+ for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
+ mFaces.mPlaneList[i].d /= mBrushScale;
+
+ calcBounds();
+
+ return true;
+}
+
+void ConvexBrush::addEdge(U16 zero, U16 one, U32 face)
+{
+ // Sort the edges
+ U16 newEdge0, newEdge1;
+
+ newEdge0 = getMin(zero, one);
+ newEdge1 = getMax(zero, one);
+
+ // Check the current edges
+ bool found = false;
+ U32 index = 0;
+
+ for (index = 0; index < mFaces.mEdgeList.size(); index++)
+ {
+ if (mFaces.mEdgeList[index].vertexes[0] == newEdge0 && mFaces.mEdgeList[index].vertexes[1] == newEdge1)
+ {
+ found = true;
+ break;
+ }
+ }
+
+ // If we didn't find it add a new edge
+ if (!found)
+ {
+ index = mFaces.mEdgeList.size();
+ mFaces.mEdgeList.increment();
+ mFaces.mEdgeList.last().vertexes[0] = newEdge0;
+ mFaces.mEdgeList.last().vertexes[1] = newEdge1;
+ mFaces.mEdgeList.last().faces[0] = -1;
+ mFaces.mEdgeList.last().faces[1] = -1;
+ mFaces.mEdgeList.last().type = OptimizedPolyList::NonShared;
+ }
+
+ // Add the face
+ if (mFaces.mEdgeList[index].faces[0] == -1)
+ mFaces.mEdgeList[index].faces[0] = face;
+ else if (mFaces.mEdgeList[index].faces[1] == -1)
+ mFaces.mEdgeList[index].faces[1] = face;
+};
+
+bool ConvexBrush::generateEdgelist()
+{
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ // Add all of the edges of the polygon
+ for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount - 1; j++)
+ {
+ U32 i0 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j];
+ U32 i1 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j + 1];
+
+ addEdge(i0, i1, i);
+ }
+
+ // Add the connecting edge from the last vertex to the first
+ U32 i0 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart];
+ U32 i1 = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + mFaces.mPolyList[i].vertexCount - 1];
+
+ addEdge(i0, i1, i);
+ }
+
+ return true;
+}
+
+U32 ConvexBrush::getPolySide(S32 side)
+{
+ if (side == PlaneF::Back)
+ return Back;
+ else if (side == PlaneF::Front)
+ return Front;
+ else if (side == PlaneF::On)
+ return On;
+ else
+ return Unknown;
+}
+
+bool ConvexBrush::isInsideBox(PlaneF left, PlaneF right, PlaneF top, PlaneF bottom)
+{
+ if (mStatus == Deleted)
+ return false;
+
+ // If the brush is in front of any of the planes then it is outside
+ U32 side = whichSide(left);
+ if (side == Front || side == Unknown)
+ return false;
+ side = whichSide(right);
+ if (side == Front || side == Unknown)
+ return false;
+ side = whichSide(top);
+ if (side == Front || side == Unknown)
+ return false;
+ side = whichSide(bottom);
+ if (side == Front || side == Unknown)
+ return false;
+
+ return true;
+}
+
+U32 ConvexBrush::whichSide(PlaneF pln)
+{
+ if (mFaces.mPolyList.size() == 0)
+ return Unknown;
+
+ // Find out which side the first face is on
+ U32 side = On;
+
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ U32 nsd = whichSide(pln, i);
+
+ if (nsd == Split)
+ return Split;
+ else if (nsd != On)
+ side = nsd;
+ }
+
+ if (side == On)
+ Con::errorf("ConvexBrush::whichSide(): brush has no volume");
+
+ return side;
+}
+
+U32 ConvexBrush::whichSide(PlaneF pln, U32 faceIndex)
+{
+ if (faceIndex > mFaces.mPolyList.size())
+ return Unknown;
+
+ OptimizedPolyList::Poly* face = &mFaces.mPolyList[faceIndex];
+
+ Point3F currv, nextv;
+ S32 csd, nsd;
+
+ U32 side = On;
+
+ // Find out which side the first vert is on
+ U32 idx = mFaces.mIndexList[face->vertexStart];
+ currv = mFaces.mVertexList[idx];
+
+ csd = pln.whichSide(currv);
+
+ if (csd != PlaneF::On)
+ side = getPolySide(csd);
+
+ for (U32 k = 1; k < face->vertexCount; k++)
+ {
+ idx = mFaces.mIndexList[face->vertexStart + k];
+ nextv = mFaces.mVertexList[idx];
+
+ nsd = pln.whichSide(nextv);
+
+ if ((csd == PlaneF::Back && nsd == PlaneF::Front) ||
+ (csd == PlaneF::Front && nsd == PlaneF::Back))
+ {
+ return Split;
+ }
+ else if (nsd != PlaneF::On)
+ side = getPolySide(nsd);
+
+ currv = nextv;
+ csd = nsd;
+ }
+
+ // Loop back to the first vert
+ idx = mFaces.mIndexList[face->vertexStart];
+ nextv = mFaces.mVertexList[idx];
+
+ nsd = pln.whichSide(nextv);
+
+ if ((csd == PlaneF::Back && nsd == PlaneF::Front) ||
+ (csd == PlaneF::Front && nsd == PlaneF::Back))
+ {
+ return Split;
+ }
+ else if (nsd != PlaneF::On)
+ side = getPolySide(nsd);
+
+ return side;
+}
+
+bool ConvexBrush::getPolyList(AbstractPolyList* list)
+{
+ if (mFaces.mVertexList.size() == 0 || mFaces.mPolyList.size() == 0)
+ {
+ Con::errorf("ConvexBrush::getPolyList(): no verts or faces");
+ return false;
+ }
+
+ PlaneTransformer trans;
+ trans.set(mTransform, Point3F(1.0f, 1.0f, 1.0f));
+
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ if (mFaces.mPolyList[i].vertexCount < 3)
+ {
+ Con::errorf("ConvexBrush::getPolyList(): ran into a face with less than 3 verts");
+ continue;
+ }
+
+ list->begin(0, i);
+
+ for (U32 j = 0; j < mFaces.mPolyList[i].vertexCount; j++)
+ {
+ U32 idx = mFaces.mIndexList[mFaces.mPolyList[i].vertexStart + j];
+ Point3F pt = mFaces.mVertexList[idx];
+
+ // Transform the point by the brush transform
+ mTransform.mulP(pt);
+
+ U32 newidx = list->addPoint(pt);
+ list->vertex(newidx);
+ }
+
+ PlaneF pln = mFaces.mPlaneList[mFaces.mPolyList[i].plane];
+ //pln.invert();
+
+ PlaneF temp;
+ trans.transform(pln, temp);
+
+ U32 pdx = list->addPlane(temp);
+ list->plane(pdx);
+
+ list->end();
+ }
+
+ return true;
+}
+
+bool ConvexBrush::splitBrush(PlaneF pln, ConvexBrush* fbrush, ConvexBrush* bbrush)
+{
+ F32 scale = mBrushScale;
+
+ // Scale the brush up
+ setScale(1.0f / scale);
+ pln.d *= scale;
+
+ // Loop through the brush faces and sort them
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ U32 side = whichSide(pln, i);
+
+ if (side == Front)
+ fbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
+ else if (side == Back)
+ bbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
+ else if (side == Split)
+ {
+ fbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
+ bbrush->addPlane(mFaces.mPlaneList[mFaces.mPolyList[i].plane]);
+ }
+ else
+ {
+ Con::errorf("BspTree::splitBrush(): this brush can not be split");
+ return false;
+ }
+ }
+
+ // Next push the split plane into both
+ bbrush->addPlane(pln);
+ fbrush->addPlane(PlaneF(-pln.x, -pln.y, -pln.z, -pln.d));
+
+ // Now generate the brushes with the planes supplied
+ fbrush->processBrush();
+ bbrush->processBrush();
+
+ // Now that we've generated the new polys copy over the properties
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ // We can match the new polys up with the old ones using their planes
+ PlaneF oldpln = mFaces.mPlaneList[mFaces.mPolyList[i].plane];
+
+ for (U32 j = 0; j < fbrush->mFaces.mPolyList.size(); j++)
+ {
+ PlaneF newpln = fbrush->mFaces.mPlaneList[fbrush->mFaces.mPolyList[j].plane];
+
+ if (oldpln == newpln && oldpln.d == newpln.d)
+ {
+ // Copy over the properties
+ fbrush->mFaces.mPolyList[j].object = mFaces.mPolyList[i].object;
+ fbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].material;
+ fbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].surfaceKey;
+ }
+ }
+
+ for (U32 j = 0; j < bbrush->mFaces.mPolyList.size(); j++)
+ {
+ PlaneF newpln = bbrush->mFaces.mPlaneList[bbrush->mFaces.mPolyList[j].plane];
+
+ if (oldpln == newpln && oldpln.d == newpln.d)
+ {
+ // Copy over the properties
+ bbrush->mFaces.mPolyList[j].object = mFaces.mPolyList[i].object;
+ bbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].material;
+ bbrush->mFaces.mPolyList[j].material = mFaces.mPolyList[i].surfaceKey;
+ }
+ }
+ }
+
+ // Now find the new poly and flag it as a non-solid face
+ for (U32 i = 0; i < fbrush->mFaces.mPolyList.size(); i++)
+ {
+ PlaneF newpln = fbrush->mFaces.mPlaneList[fbrush->mFaces.mPolyList[i].plane];
+
+ if (pln.x == -newpln.x && pln.y == -newpln.y && pln.z == -newpln.z && pln.d == -newpln.d)
+ fbrush->mFaces.mPolyList[i].material = -2;
+ }
+
+ for (U32 i = 0; i < bbrush->mFaces.mPolyList.size(); i++)
+ {
+ PlaneF newpln = bbrush->mFaces.mPlaneList[bbrush->mFaces.mPolyList[i].plane];
+
+ if (pln == newpln && pln.d == newpln.d)
+ bbrush->mFaces.mPolyList[i].material = -2;
+ }
+
+ // scale everything back down
+ fbrush->setScale(scale);
+ bbrush->setScale(scale);
+
+ setScale(scale);
+
+ return true;
+}
+
+bool ConvexBrush::castRay(const Point3F& s, const Point3F& e, RayInfo* info)
+{
+ if (mStatus == Deleted)
+ return false;
+
+ // First transform the start and end points into the brushes space
+ Point3F start, end;
+ MatrixF invTrans = mTransform;
+ invTrans.inverse();
+ invTrans.mulP(s, &start);
+ invTrans.mulP(e, &end);
+ start.convolveInverse(mScale);
+ end.convolveInverse(mScale);
+
+ // Ganked from TSMesh::castRay()
+ // Keep track of startTime and endTime. They start out at just under 0 and just over 1, respectively.
+ // As we check against each plane, prune start and end times back to represent current intersection of
+ // line with all the planes (or rather with all the half-spaces defined by the planes).
+ // But, instead of explicitly keeping track of startTime and endTime, keep track as numerator and denominator
+ // so that we can avoid as many divisions as possible.
+
+ // F32 startTime = -0.01f;
+ F32 startNum = -0.01f;
+ F32 startDen = 1.00f;
+ // F32 endTime = 1.01f;
+ F32 endNum = 1.01f;
+ F32 endDen = 1.00f;
+
+ S32 curPlane = 0;
+ U32 curMaterial = 0;
+ bool found = false;
+
+ // the following block of code is an optimization...
+ // it isn't necessary if the longer version of the main loop is used
+ bool tmpFound;
+ S32 tmpPlane;
+ F32 sgn = -1.0f;
+ F32 * pnum = &startNum;
+ F32 * pden = &startDen;
+ S32 * pplane = &curPlane;
+ bool * pfound = &found;
+
+ for (U32 i = 0; i < mFaces.mPlaneList.size(); i++)
+ {
+ Point3F planeNormal = mFaces.mPlaneList[i];
+ F32 planeConstant = mFaces.mPlaneList[i].d;
+
+ // if start & end outside, no collision
+ // if start & end inside, continue
+ // if start outside, end inside, or visa versa, find intersection of line with plane
+ // then update intersection of line with hull (using startTime and endTime)
+ F32 dot1 = mDot(planeNormal, start) + planeConstant;
+ F32 dot2 = mDot(planeNormal, end) + planeConstant;
+ if (dot1*dot2>0.0f)
+ {
+ // same side of the plane...which side -- dot==0 considered inside
+ if (dot1>0.0f)
+ // start and end outside of this plane, no collision
+ return false;
+ // start and end inside plane, continue
+ continue;
+ }
+
+ AssertFatal(dot1/(dot1-dot2)>=0.0f && dot1/(dot1-dot2)<=1.0f,"ConvexBrush::castRay (1)");
+
+ // find intersection (time) with this plane...
+ // F32 time = dot1 / (dot1-dot2);
+ F32 num = mFabs(dot1);
+ F32 den = mFabs(dot1-dot2);
+
+ // the following block of code is an optimized version...
+ // this can be commented out and the following block of code used instead
+ // if debugging a problem in this code, that should probably be done
+ // if you want to see how this works, look at the following block of code,
+ // not this one...
+ // Note that this does not get optimized appropriately...it is included this way
+ // as an idea for future optimization.
+ if (sgn*dot1>=0)
+ {
+ sgn *= -1.0f;
+ pnum = (F32*) ((dsize_t)pnum ^ (dsize_t)&endNum ^ (dsize_t)&startNum);
+ pden = (F32*) ((dsize_t)pden ^ (dsize_t)&endDen ^ (dsize_t)&startDen);
+ pplane = (S32*) ((dsize_t)pplane ^ (dsize_t)&tmpPlane ^ (dsize_t)&curPlane);
+ pfound = (bool*) ((dsize_t)pfound ^ (dsize_t)&tmpFound ^ (dsize_t)&found);
+ }
+ bool noCollision = num*endDen*sgnt = (F32)startNum/(F32)startDen; // finally divide...
+ info->normal = mFaces.mPlaneList[curPlane];
+ // Find the face that uses this plane and its material
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ if (mFaces.mPolyList[i].plane == curPlane)
+ {
+ info->material = mFaces.mPolyList[i].material;
+ break;
+ }
+ }
+
+ return true;
+ }
+ else if (found)
+ return true;
+
+ // only way to get here is if start is inside hull...
+ // we could return null and just plug in garbage for the material and normal...
+ return false;
+}
+
+void ConvexBrush::resetBrush()
+{
+ mBounds.min = Point3F(0, 0, 0);
+ mBounds.max = Point3F(0, 0, 0);
+
+ mCentroid = Point3F(0.0f, 0.0f, 0.0f);
+ mStatus = Unprocessed;
+
+ // Set our transform to no translation or rotation
+ mTransform.identity();
+
+ mScale.set(1.0f, 1.0f, 1.0f);
+ mRotation.set(EulerF(0.0f, 0.0f, 0.0f));
+
+ // Data
+ mFaces.clear();
+ mTexInfos.clear();
+}
+
+OptimizedPolyList ConvexBrush::getIntersectingPolys(OptimizedPolyList* list)
+{
+ OptimizedPolyList ret;
+
+ for (U32 i = 0; i < list->mPolyList.size(); i++)
+ {
+ if (isPolyInside(list, i))
+ list->copyPolyToList(&ret, i);
+ }
+
+ return ret;
+}
+
+OptimizedPolyList ConvexBrush::getNonIntersectingPolys(OptimizedPolyList* list)
+{
+ OptimizedPolyList ret;
+
+ for (U32 i = 0; i < list->mPolyList.size(); i++)
+ {
+ if (!isPolyInside(list, i))
+ list->copyPolyToList(&ret, i);
+ }
+
+ return ret;
+}
+
+bool ConvexBrush::isPolyInside(OptimizedPolyList* list, U32 pdx)
+{
+ // This relies on the brush to be convex
+ bool inside = true;
+
+ OptimizedPolyList::Poly& poly = list->mPolyList[pdx];
+
+ for (U32 i = 0; i < mFaces.mPolyList.size(); i++)
+ {
+ OptimizedPolyList::Poly& face = mFaces.mPolyList[i];
+ PlaneF& pln = mFaces.mPlaneList[face.plane];
+
+ bool behind = true;
+
+ for (U32 j = 0; j < poly.vertexCount; j++)
+ {
+ U32 idx = list->mIndexList[j + poly.vertexStart];
+ Point3F& pt = list->mVertexList[idx];
+
+ U32 tside = pln.whichSide(pt);
+
+ if (tside == PlaneF::Front)
+ {
+ behind = false;
+ break;
+ }
+ }
+
+ if (!behind)
+ {
+ inside = false;
+ break;
+ }
+ }
+
+ return inside;
+}
+
diff --git a/engine/collision/convexBrush.h b/engine/collision/convexBrush.h
new file mode 100755
index 0000000..c8a648a
--- /dev/null
+++ b/engine/collision/convexBrush.h
@@ -0,0 +1,127 @@
+#ifndef _CONVEXBRUSH_H_
+#define _CONVEXBRUSH_H_
+
+#include "platform/platform.h"
+#include "core/tVector.h"
+#include "math/mPoint.h"
+#include "math/mBox.h"
+#include "math/mPlane.h"
+#include "collision/abstractPolyList.h"
+#include "collision/optimizedPolyList.h"
+#include "sim/sceneObject.h"
+#include "interior/interiorMapRes.h"
+
+#define WINDINGSCALE 10000
+
+class ConvexBrush
+{
+public:
+ // Constructor
+ ConvexBrush();
+ // Destructor
+ ~ConvexBrush();
+
+ enum
+ {
+ Front = 0,
+ Back = 1,
+ On = 2,
+ Split = 3,
+ Unknown = 4
+ };
+
+ enum
+ {
+ Unprocessed = 0,
+ Good,
+ BadWinding,
+ ShortPlanes,
+ BadFaces,
+ Malformed,
+ Concave,
+ Deleted
+ };
+
+ // Supporting structures
+ struct TexInfo
+ {
+ StringTableEntry texture;
+ PlaneF texGens[2];
+ F32 scale[2];
+ F32 rot;
+ F32 texDiv[2];
+ };
+
+ // The brushes owner entity
+ InteriorMapResource::Entity* mOwner;
+
+ // Bounding box
+ Box3F mBounds;
+ Point3F mCentroid;
+ MatrixF mTransform;
+ Point3F mScale;
+ QuatF mRotation;
+
+ // lighting info...
+ MatrixF mLightingTransform;
+
+ // Some useful values
+ F32 mBrushScale;
+ S32 mID;
+ U32 mType;
+ U32 mStatus;
+ U32 mPrevStatus;
+ bool mSelected;
+ StringTableEntry mDebugInfo;
+
+ // Scratch buffer storage
+ MatrixF mTransformScratch;
+ QuatF mRotationScratch;
+
+
+ // Data
+ OptimizedPolyList mFaces;
+ Vector mTexInfos;
+
+ // Setup functions
+ bool addPlane(PlaneF pln);
+ bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture);
+ bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], char* texture, U32 matIdx);
+ bool addFace(PlaneF pln, PlaneF texGen[2], F32 scale[2], U32 matIdx);
+ bool processBrush();
+ bool setScale(F32 scale);
+
+ // Utility functions
+ bool selfClip();
+ bool intersectPlanes(const PlaneF& plane1, const PlaneF& plane2, const PlaneF& plane3, Point3D* pOutput);
+ bool createWinding(const Vector& rPoints, const Point3F& normal, Vector& pWinding);
+ void addEdge(U16 zero, U16 one, U32 face);
+ bool generateEdgelist();
+ void validateWindings();
+ bool validateEdges();
+ void addIndex(Vector& indices, U32 index);
+ void calcCentroid();
+ void setupTransform();
+ void resetBrush();
+
+ U32 getPolySide(S32 side);
+ U32 whichSide(PlaneF pln);
+ U32 whichSide(PlaneF pln, U32 faceIndex);
+ bool isInsideBox(PlaneF left, PlaneF right, PlaneF top, PlaneF bottom);
+ bool splitBrush(PlaneF pln, ConvexBrush* fbrush, ConvexBrush* bbrush);
+ bool calcBounds();
+ bool castRay(const Point3F& s, const Point3F& e, RayInfo* info);
+
+ OptimizedPolyList getIntersectingPolys(OptimizedPolyList* list);
+ OptimizedPolyList getNonIntersectingPolys(OptimizedPolyList* list);
+ bool isPolyInside(OptimizedPolyList* list, U32 pdx);
+
+ bool getPolyList(AbstractPolyList* list);
+
+ // Debug render functions
+ bool render(bool genColors);
+ bool renderFace(U32 face, bool renderLighting);
+ bool renderEdges(ColorF color);
+};
+
+#endif
diff --git a/engine/collision/depthSortList.cc b/engine/collision/depthSortList.cc
new file mode 100755
index 0000000..6f69ac0
--- /dev/null
+++ b/engine/collision/depthSortList.cc
@@ -0,0 +1,903 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "console/console.h"
+#include "collision/depthSortList.h"
+#include "core/color.h"
+#include "core/fileStream.h" // TODO, remove this
+
+//----------------------------------------------------------------------------
+
+// some defines and global parameters that affect poly split routine
+F32 SPLIT_TOL = 0.0005f;
+bool ALWAYS_RETURN_FRONT_AND_BACK = false; // if false, split routine will return polys only if a split occurs
+
+// more global parameters
+F32 XZ_TOL = 0.0f;
+F32 DEPTH_TOL = 0.01f;
+#define MIN_Y_DOT 0.05f
+DepthSortList * gCurrentSort = NULL;
+S32 gForceOverlap = -1; // force an overlap test to result in an overlap
+S32 gNoOverlapCount;
+S32 gBadSpots = 0;
+
+// if polys are correctly sorted then writing depth values should result in no
+// overlap of polys when looking down from camera...otoh, if polys are out of
+// order, we should see overlap
+bool DepthSortList::renderWithDepth = false;
+
+//----------------------------------------------------------------------------
+// following copied from shapeBase.cc because I didn't want to reference
+// something from the test program in the core. This should really be a
+// stand-alone function, but...
+
+static ColorF cubeColors[8] = {
+ ColorF(0, 0, 0), ColorF(1, 0, 0), ColorF(0, 1, 0), ColorF(0, 0, 1),
+ ColorF(1, 1, 0), ColorF(1, 0, 1), ColorF(0, 1, 1), ColorF(1, 1, 1)
+};
+
+static Point3F cubePoints[8] = {
+ Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1),
+ Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1)
+};
+
+static U32 cubeFaces[6][4] = {
+ { 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 },
+ { 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 }
+};
+
+void DepthSortList::wireCube(const Point3F& size, const Point3F& pos)
+{
+ glDisable(GL_CULL_FACE);
+
+ for(int i = 0; i < 6; i++) {
+ glBegin(GL_LINE_LOOP);
+ for(int vert = 0; vert < 4; vert++) {
+ int idx = cubeFaces[i][vert];
+ glColor3f(cubeColors[idx].red, cubeColors[idx].green, cubeColors[idx].blue);
+ glVertex3f(cubePoints[idx].x * size.x + pos.x, cubePoints[idx].y * size.y + pos.y, cubePoints[idx].z * size.z + pos.z);
+ }
+ glEnd();
+ }
+}
+
+//----------------------------------------------------------------------------
+
+DepthSortList::DepthSortList()
+{
+ VECTOR_SET_ASSOCIATION(mPolyExtentsList);
+ VECTOR_SET_ASSOCIATION(mPolyIndexList);
+}
+
+DepthSortList::~DepthSortList()
+{
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::clear()
+{
+ Parent::clear();
+
+ mPolyExtentsList.clear();
+ mPolyIndexList.clear();
+
+ clearSort();
+}
+
+void DepthSortList::clearSort()
+{
+ mBase = -1;
+ mMaxTouched = 0;
+ gNoOverlapCount = 0;
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::end()
+{
+ S32 numPoly = mPolyList.size();
+
+ if (mPolyList.last().plane.y >= -MIN_Y_DOT)
+ {
+ mIndexList.setSize(mPolyList.last().vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+
+ Parent::end();
+
+ // we deleted this poly, be sure not to add anything more...
+ if (mPolyList.size()!=numPoly)
+ return;
+
+ AssertFatal(mPolyList.last().vertexCount>2,"DepthSortList::end: only two vertices in poly");
+
+ mPolyExtentsList.increment();
+ setExtents(mPolyList.last(),mPolyExtentsList.last());
+ mPolyIndexList.push_back(numPoly-1);
+}
+
+//----------------------------------------------------------------------------
+
+bool DepthSortList::getMapping(MatrixF * mat, Box3F * box)
+{
+ // return list transform and bounds in list space...optional
+ *mat = mMatrix;
+ mat->inverse();
+ box->min.set(-mExtent.x, 0.0f, -mExtent.z);
+ box->max.set( mExtent.x, 2.0f * mExtent.y, mExtent.z);
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::render()
+{
+ glPushMatrix();
+ glPushAttrib(GL_DEPTH_BUFFER_BIT);
+
+ MatrixF mat = mBaseMatrix;
+ mat.inverse();
+ dglMultMatrix(&mat);
+
+ glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glColor4f(1,0,0,0.25);
+ glEnable(GL_BLEND);
+ glEnable(GL_CULL_FACE);
+ glEnable(GL_DEPTH_TEST);
+ if (renderWithDepth)
+ glDepthMask(GL_TRUE);
+ else
+ glDepthMask(GL_FALSE);
+ glDisable(GL_CULL_FACE);
+ glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+
+ Poly * p;
+ S32 only = Con::getIntVariable("$only",-1);
+ for (S32 i=0; i=0 && only!=i)
+ continue;
+ p = &mPolyList[mPolyIndexList[i]];
+ glDrawElements(GL_POLYGON,p->vertexCount,
+ GL_UNSIGNED_INT,&mIndexList[p->vertexStart]);
+ }
+
+ glDisable(GL_BLEND);
+ glDisable(GL_CULL_FACE);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ // draw outline around clip zone...
+ wireCube(mExtent,Point3F(0,mExtent.y*0.5f,0));
+
+ glPopMatrix();
+ glPopAttrib();
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::setExtents(Poly & poly, PolyExtents & polyExtents)
+{
+ Point3F p = mVertexList[mIndexList[poly.vertexStart]].point;
+ polyExtents.xMin = polyExtents.xMax = p.x;
+ polyExtents.yMin = polyExtents.yMax = p.y;
+ polyExtents.zMin = polyExtents.zMax = p.z;
+ for (S32 i=poly.vertexStart+1; i polyExtents.xMax)
+ polyExtents.xMax = p.x;
+
+ // y
+ if (p.y < polyExtents.yMin)
+ polyExtents.yMin = p.y;
+ else if (p.y > polyExtents.yMax)
+ polyExtents.yMax = p.y;
+
+ // z
+ if (p.z < polyExtents.zMin)
+ polyExtents.zMin = p.z;
+ else if (p.z > polyExtents.zMax)
+ polyExtents.zMax = p.z;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+// function for comparing two poly indices
+S32 FN_CDECL compareYExtents( const void* e1, const void* e2)
+{
+ DepthSortList::PolyExtents & poly1 = gCurrentSort->getExtents(*(U32*)e1);
+ DepthSortList::PolyExtents & poly2 = gCurrentSort->getExtents(*(U32*)e2);
+
+ if (poly1.yMin < poly2.yMin)
+ return -1;
+ if (poly2.yMin < poly1.yMin)
+ return 1;
+ return 0;
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::sortByYExtents()
+{
+ gCurrentSort = this;
+ dQsort(mPolyIndexList.address(),mPolyIndexList.size(),sizeof(U32),compareYExtents);
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::set(const MatrixF & mat, Point3F & extents)
+{
+ setBaseTransform(mat);
+ mNormal.set(0,1,0); // ignore polys not facing up...
+ mExtent = extents;
+ mExtent *= 0.5f;
+
+ // set clip planes
+ mPlaneList.clear();
+
+ mPlaneList.increment();
+ mPlaneList.last().set(-1.0f, 0.0f, 0.0f);
+ mPlaneList.last().d = -mExtent.x;
+ mPlaneList.increment();
+ mPlaneList.last().set( 1.0f, 0.0f, 0.0f);
+ mPlaneList.last().d = -mExtent.x;
+
+ mPlaneList.increment();
+ mPlaneList.last().set( 0.0f,-1.0f, 0.0f);
+ mPlaneList.last().d = 0;
+ mPlaneList.increment();
+ mPlaneList.last().set( 0.0f, 1.0f, 0.0f);
+ mPlaneList.last().d = -2.0f * mExtent.y;
+
+ mPlaneList.increment();
+ mPlaneList.last().set( 0.0f, 0.0f,-1.0f);
+ mPlaneList.last().d = -mExtent.z;
+ mPlaneList.increment();
+ mPlaneList.last().set( 0.0f, 0.0f, 1.0f);
+ mPlaneList.last().d = -mExtent.z;
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::setBase(S32 base)
+{
+ mBase = base;
+ getOrderedPoly(mBase, &mBasePoly, &mBaseExtents);
+ mBaseNormal = &mBasePoly->plane;
+ mBaseDot = -mBasePoly->plane.d;
+ mBaseYMax = mBaseExtents->yMax;
+}
+
+//----------------------------------------------------------------------------
+
+bool DepthSortList::sortNext()
+{
+ // find the next poly in the depth order
+ // NOTE: a closer poly may occur before a farther away poly so long as
+ // they don't overlap in the x-z plane...
+ if (++mBase>=mPolyIndexList.size())
+ return false;
+
+ setBase(mBase);
+
+ gBadSpots = 0;
+ ALWAYS_RETURN_FRONT_AND_BACK = false; // split routine will return polys only if a split occurs
+
+ bool switched = false; // haven't switched base poly yet
+ S32 i = 0; // currently comparing base to base+i
+ Poly * testPoly;
+ PolyExtents * testExtents;
+
+ while (mBase+i+1plane;
+ F32 testDot = -testPoly->plane.d;
+
+ // if base poly's y extents don't overlap test poly's, base poly can stay where it is...
+ if (mBase+i>mMaxTouched && mBaseYMax<=testExtents->yMin+DEPTH_TOL)
+ break;
+
+ // if base poly and test poly don't have overlapping x & z extents, then order doesn't matter...stay the same
+ if (mBaseExtents->xMin>=testExtents->xMax-XZ_TOL || mBaseExtents->xMax<=testExtents->xMin+XZ_TOL ||
+ mBaseExtents->zMin>=testExtents->zMax-XZ_TOL || mBaseExtents->zMax<=testExtents->zMin+XZ_TOL)
+ continue;
+
+ // is test poly completely behind base poly? if so, order is fine as it is
+ S32 v;
+ for (v=0; vvertexCount; v++)
+ if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)>mBaseDot+DEPTH_TOL)
+ break;
+ if (v==testPoly->vertexCount)
+ // test behind base
+ continue;
+
+ // is base poly completely in front of test poly? if so, order is fine as it is
+ for (v=0; vvertexCount; v++)
+ if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)vertexCount)
+ // base in front of test
+ continue;
+
+ // if the polys don't overlap in the x-z plane, then order doesn't matter, stay as we are
+ if (!overlap(mBasePoly,testPoly))
+ {
+ gNoOverlapCount++;
+ if (gNoOverlapCount!=gForceOverlap)
+ continue;
+ }
+
+ // handle switching/splitting of polys due to overlap
+ handleOverlap(testPoly,testNormal,testDot,i,switched);
+ }
+ return true;
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::sort()
+{
+ // depth sort mPolyIndexList -- entries are indices into mPolyList (where poly is found) & mPolyExtentsList
+
+ // sort by min y extent
+ sortByYExtents();
+
+ mBase = -1;
+
+ while (sortNext())
+ ;
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched)
+{
+ // first reverse the plane tests (i.e., test to see if basePoly behind testPoly or testPoly in front of basePoly...
+ // if either succeeds, switch poly
+ // if they both fail, split base poly
+ // But split anyway if basePoly has already been switched...
+ bool doSwitch = false;
+
+ if (!switched)
+ {
+ S32 v;
+ for (v=0; vvertexCount; v++)
+ if (mDot(mVertexList[mIndexList[mBasePoly->vertexStart+v]].point,testNormal)>testDot+DEPTH_TOL)
+ break;
+ if (v==mBasePoly->vertexCount)
+ doSwitch = true;
+ else
+ {
+ for (v=0; vvertexCount; v++)
+ if (mDot(mVertexList[mIndexList[testPoly->vertexStart+v]].point,*mBaseNormal)vertexCount)
+ doSwitch = true;
+ }
+ }
+
+ // try to split base poly along plane of test poly
+ Poly frontPoly, backPoly;
+ bool splitBase = false, splitTest = false;
+ if (!doSwitch)
+ {
+ splitBase = splitPoly(*mBasePoly,testNormal,testDot,frontPoly,backPoly);
+ if (!splitBase)
+ // didn't take...no splitting happened...try splitting test poly by base poly
+ splitTest = splitPoly(*testPoly,*mBaseNormal,mBaseDot,frontPoly,backPoly);
+ }
+
+ U32 testIdx = mPolyIndexList[mBase+testOffset];
+
+ // should we switch order of test and base poly? Might have to even if we
+ // don't want to if there's no splitting to do...
+ // Note: possibility that infinite loop can be introduced here...if that happens,
+ // then we need to split along edges of polys
+ if (doSwitch || (!splitTest && !splitBase))
+ {
+ if (!doSwitch && gBadSpots++ > (mPolyIndexList.size()-mBase)<<1)
+ // got here one too many times...just leave and don't touch poly -- avoid infinite loop
+ return;
+
+ // move test poly to the front of the order
+ dMemmove(&mPolyIndexList[mBase+1],&mPolyIndexList[mBase],testOffset*sizeof(U32));
+ mPolyIndexList[mBase] = testIdx;
+
+ // base poly changed...
+ setBase(mBase);
+
+ if (mBase+testOffset>mMaxTouched)
+ mMaxTouched=mBase+testOffset;
+
+ testOffset=1; // don't need to compare against old base
+ switched=true;
+ return;
+ }
+
+ if (splitBase)
+ {
+ // need one more spot...frontPoly and backPoly replace basePoly
+ setExtents(frontPoly,mPolyExtentsList[mPolyIndexList[mBase]]);
+ mPolyExtentsList.increment();
+ setExtents(backPoly,mPolyExtentsList.last());
+
+ mPolyList[mPolyIndexList[mBase]] = frontPoly;
+ mPolyIndexList.insert(mBase+1);
+ mPolyIndexList[mBase+1] = mPolyList.size();
+ mPolyList.push_back(backPoly);
+
+ // new base poly...
+ setBase(mBase);
+
+ // increase tsetOffset & mMaxTouched because of insertion of back poly
+ testOffset++;
+ mMaxTouched++;
+
+ //
+ switched=false;
+ return;
+ }
+
+ // splitTest -- no other way to get here
+ AssertFatal(splitTest,"DepthSortList::handleOverlap: how did we get here like this?");
+
+ // put frontPoly in front of basePoly, leave backPoly where testPoly was
+
+ // we need one more spot (testPoly broken into front and back)
+ // and we need to shift everything from base up to test down one spot
+ mPolyIndexList.insert(mBase);
+
+ // need one more poly for front poly
+ mPolyIndexList[mBase] = mPolyList.size();
+ mPolyList.push_back(frontPoly);
+ mPolyExtentsList.increment();
+ setExtents(mPolyList.last(),mPolyExtentsList.last());
+
+ // set up back poly
+ mPolyList[testIdx] = backPoly;
+ setExtents(mPolyList[testIdx],mPolyExtentsList[testIdx]);
+
+ // new base poly...
+ setBase(mBase);
+
+ // we inserted an element, increase mMaxTouched...
+ mMaxTouched++;
+
+ testOffset=0;
+ switched = false;
+}
+
+//----------------------------------------------------------------------------
+
+bool DepthSortList::overlap(Poly * poly1, Poly * poly2)
+{
+ // check for overlap without any shortcuts
+ S32 sz1 = poly1->vertexCount;
+ S32 sz2 = poly2->vertexCount;
+
+ Point3F * a1, * b1;
+ Point3F * a2, * b2;
+ Point2F norm;
+ F32 dot;
+ b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point;
+ S32 i;
+ for (i=0; ivertexStart+i]].point;
+
+ // get the mid-point of this edge
+ Point3F mid1 = *a1+*b1;
+ mid1 *= 0.5f;
+ bool midOutside = false;
+
+ b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point;
+ for (S32 j=0; jvertexStart+j]].point;
+
+ // test for intersection of a1-b1 and a2-b2 (on x-z plane)
+
+ // they intersect if a1 & b1 are on opp. sides of line a2-b2
+ // and a2 & b2 are on opp. sides of line a1-b1
+
+ norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2
+ dot = norm.x * a2->x + norm.y * a2->z; // dot of a2 and norm
+ if (norm.x * mid1.x + norm.y * mid1.z - dot >= 0) // special check for midpoint of line
+ midOutside = true;
+ if ( ((norm.x * a1->x + norm.y * a1->z) - dot) * ((norm.x * b1->x + norm.y * b1->z) - dot) >= 0 )
+ // a1 & b1 are on the same side of the line a2-b2...edges don't overlap
+ continue;
+
+ norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1
+ dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm
+ if ( ((norm.x * a2->x + norm.y * a2->z) - dot) * ((norm.x * b2->x + norm.y * b2->z) - dot) >= 0 )
+ // a2 & b2 are on the same side of the line a1-b1...edges don't overlap
+ continue;
+
+ return true; // edges overlap, so polys overlap
+ }
+ if (!midOutside)
+ return true; // midpoint of a1-b1 is inside the poly
+ }
+
+ // edges don't overlap...but one poly might be contained inside the other
+ Point3F center = mVertexList[mIndexList[poly2->vertexStart]].point;
+ for (i=1; ivertexStart+i]].point;
+ center *= 1.0f / (F32)poly2->vertexCount;
+ b1 = &mVertexList[mIndexList[poly1->vertexStart+sz1-1]].point;
+ for (i=0; ivertexStart+i]].point;
+
+ norm.set(a1->z - b1->z, b1->x - a1->x); // normal to line a1-b1
+ dot = norm.x * a1->x + norm.y * a1->z; // dot of a1 and norm
+ if (center.x * norm.x + center.z * norm.y > dot)
+ // center is outside this edge, poly2 is not inside poly1
+ break;
+ }
+ if (i==sz1)
+ return true; // first vert of poly2 inside poly1 (so all of poly2 inside poly1)
+
+ center = mVertexList[mIndexList[poly1->vertexStart]].point;
+ for (i=1; ivertexStart+i]].point;
+ center *= 1.0f / (F32)poly1->vertexCount;
+ b2 = &mVertexList[mIndexList[poly2->vertexStart+sz2-1]].point;
+ for (i=0; ivertexStart+i]].point;
+
+ norm.set(a2->z - b2->z, b2->x - a2->x); // normal to line a2-b2
+ dot = norm.x * a2->x + norm.y * a2->z; // dot of a1 and norm
+ if (center.x * norm.x + center.z * norm.y > dot)
+ // v is outside this edge, poly1 is not inside poly2
+ break;
+ }
+ if (i==sz2)
+ return true; // first vert of poly1 inside poly2 (so all of poly1 inside poly2)
+
+ return false; // we survived, no overlap
+}
+
+//----------------------------------------------------------------------------
+
+Vector frontVerts(__FILE__, __LINE__);
+Vector backVerts(__FILE__, __LINE__);
+
+// Split source poly into front and back. If either front or back is degenerate, don't do anything.
+// If we have a front and a back, then add the verts to our vertex list and fill out the poly structures.
+bool DepthSortList::splitPoly(const Poly & src, Point3F & normal, F32 k, Poly & frontPoly, Poly & backPoly)
+{
+ frontVerts.clear();
+ backVerts.clear();
+
+ // already degenerate...
+ AssertFatal(src.vertexCount>=3,"DepthSortList::splitPoly - Don't need to split a triangle!");
+
+ S32 startSize = mVertexList.size();
+
+ // Assume back and front are degenerate polygons until proven otherwise.
+ bool backDegen = true, frontDegen = true;
+
+ U32 bIdx;
+ Point3F * a, * b;
+ F32 dota, dotb;
+ S32 signA, signB;
+
+ F32 splitTolSq = SPLIT_TOL * SPLIT_TOL * mDot(normal,normal);
+
+ bIdx = mIndexList[src.vertexStart+src.vertexCount-1];
+ b = &mVertexList[bIdx].point;
+ dotb = mDot(normal,*b)-k;
+
+ // Sign variable coded as follows: 1 for outside, 0 on the plane and -1 for inside.
+ if (dotb*dotb > splitTolSq)
+ signB = dotb > 0.0f ? 1 : -1;
+ else
+ signB = 0;
+
+ S32 i;
+ for (i = 0; i splitTolSq)
+ signB = dotb > 0.0f ? 1 : -1;
+ else
+ signB = 0;
+
+ switch(signA*3 + signB + 4) // +4 is to make values go from 0 up...hopefully enticing compiler to make a jump-table
+ {
+ case 0: // A-, B-
+ case 3: // A., B-
+ backVerts.push_back(bIdx);
+ backDegen = false;
+ break;
+ case 8: // A+, B+
+ case 5: // A., B+
+ frontVerts.push_back(bIdx);
+ frontDegen = false;
+ break;
+
+ case 1: // A-, B.
+ case 4: // A., B.
+ case 7: // A+, B.
+ backVerts.push_back(bIdx);
+ frontVerts.push_back(bIdx);
+ break;
+
+ case 2: // A-, B+
+ {
+ // intersect line A-B with plane
+ F32 dotA = mDot(*a,normal);
+ F32 dotB = mDot(*b,normal);
+ Vertex v;
+ v.point = *a-*b;
+ v.point *= (k-dotB)/(dotA-dotB);
+ v.point += *b;
+ frontVerts.push_back(mVertexList.size());
+ backVerts.push_back(mVertexList.size());
+ frontVerts.push_back(bIdx);
+ mVertexList.push_back(v);
+ b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector
+ frontDegen = false;
+ break;
+ }
+ case 6: // A+, B-
+ {
+ // intersect line A-B with plane
+ F32 dotA = mDot(*a,normal);
+ F32 dotB = mDot(*b,normal);
+ Vertex v;
+ v.point = *a-*b;
+ v.point *= (k-dotB)/(dotA-dotB);
+ v.point += *b;
+ frontVerts.push_back(mVertexList.size());
+ backVerts.push_back(mVertexList.size());
+ backVerts.push_back(bIdx);
+ mVertexList.push_back(v);
+ b = &mVertexList[bIdx].point; // better get this pointer again since we just incremented vector
+ backDegen = false;
+ break;
+ }
+ }
+ }
+
+ // Check for degeneracy.
+
+ if (!ALWAYS_RETURN_FRONT_AND_BACK)
+ {
+ if (frontVerts.size()<3 || backVerts.size()<3 || frontDegen || backDegen)
+ {
+ // didn't need to be split...return two empty polys
+ // and restore vertex list to how it was
+ mVertexList.setSize(startSize);
+ frontPoly.vertexCount = backPoly.vertexCount = 0;
+ return false;
+ }
+ }
+ else
+ {
+ if (frontDegen)
+ frontVerts.clear();
+ if (backDegen)
+ backVerts.clear();
+ }
+
+ // front poly
+ frontPoly.plane = src.plane;
+ frontPoly.object = src.object;
+ frontPoly.material = src.material;
+ frontPoly.vertexStart = mIndexList.size();
+ frontPoly.vertexCount = frontVerts.size();
+ frontPoly.surfaceKey = src.surfaceKey;
+
+ // back poly
+ backPoly.plane = src.plane;
+ backPoly.object = src.object;
+ backPoly.material = src.material;
+ backPoly.vertexStart = frontPoly.vertexStart + frontPoly.vertexCount;
+ backPoly.vertexCount = backVerts.size();
+ backPoly.surfaceKey = src.surfaceKey;
+
+ // add indices
+ mIndexList.setSize(backPoly.vertexStart+backPoly.vertexCount);
+
+ if( frontPoly.vertexCount ) {
+ dMemcpy(&mIndexList[frontPoly.vertexStart],
+ frontVerts.address(),
+ sizeof(U32)*frontPoly.vertexCount);
+ }
+
+ if( backPoly.vertexCount ) {
+ dMemcpy(&mIndexList[backPoly.vertexStart],
+ backVerts.address(),
+ sizeof(U32)*backPoly.vertexCount);
+ }
+
+ return true;
+}
+
+//----------------------------------------------------------------------------
+
+Vector gWorkListA(256);
+Vector gWorkListB(256);
+
+void DepthSortList::depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector & partition, Vector & partitionVerts)
+{
+ // create the depth partition of the passed poly
+ // a depth partition is a partition of the poly on the
+ // x-z plane so that each sub-poly in the partition can be
+ // mapped onto exactly one plane in the depth list (i.e.,
+ // those polys found in mPolyIndexList... the ones that are
+ // depth sorted). The plane the sub-polys are mapped onto
+ // is the plane of the closest facing poly.
+ //
+ // y-coord of input polys are ignored, and are remapped
+ // on output to put the output polys on the
+ // corresponding planes.
+
+ // This routine is confusing because there are three lists of polys.
+ //
+ // The source list (passed in as a single poly, but becomes a list as
+ // it is split up) comprises the poly to be partitioned. Verts for sourcePoly
+ // are held in sourceVerts when passed to this routine, but immediately copied
+ // to mVertexList (and indices are added for each vert to mIndexList).
+ //
+ // The scraps list is generated from the source poly (it contains the outside
+ // piece of each cut that is made). Indices for polys in the scraps list are
+ // found in mIndexList and verts are found in mVerts list. Note that the depthPartition
+ // routine will add verts and indices to the member lists, but not polys.
+ //
+ // Finally, the partition list is the end result -- the depth partition. These
+ // polys are not indexed polys. The vertexStart field indexes directly into partitionVerts
+ // array.
+
+ if (mBase<0)
+ // begin the depth sort
+ sortByYExtents();
+
+ // apply cookie cutter to these polys
+ Vector * sourceList = &gWorkListA;
+ sourceList->clear();
+
+ // add source poly for to passed verts
+ sourceList->increment();
+ sourceList->last().vertexStart = mIndexList.size();
+ sourceList->last().vertexCount = numVerts;
+
+ // add verts of source poly to mVertexList and mIndexList
+ mVertexList.setSize(mVertexList.size()+numVerts);
+ mIndexList.setSize(mIndexList.size()+numVerts);
+ for (S32 v=0; v * scraps = &gWorkListB;
+ scraps->clear();
+
+ S32 i=0;
+ while (sourceList->size())
+ {
+ if (i>=mBase && !sortNext())
+ // that's it, no more polys to sort
+ break;
+ AssertFatal(i<=mBase,"DepthSortList::depthPartition - exceeded mBase.");
+
+ // use the topmost poly as the cookie cutter
+ Poly & cutter = mPolyList[mPolyIndexList[i]];
+ S32 startVert = partitionVerts.size();
+ S32 j;
+ for (j=0; jsize(); j++)
+ {
+ Poly & toCut = (*sourceList)[j];
+ cookieCutter(cutter,toCut,*scraps,partition,partitionVerts);
+ }
+
+ // project all the new verts onto the cutter's plane
+ AssertFatal(mFabs(cutter.plane.y)>=MIN_Y_DOT,"DepthSortList::depthPartition - below MIN_Y_DOT.");
+ F32 invY = -1.0f / cutter.plane.y;
+ for (j=startVert; jclear();
+
+ // swap work lists -- scraps become source for next closest poly
+ Vector * tmpListPtr = sourceList;
+ sourceList = scraps;
+ scraps = tmpListPtr;
+ i++;
+ }
+}
+
+//----------------------------------------------------------------------------
+
+void DepthSortList::cookieCutter(Poly & cutter, Poly & cuttee,
+ Vector & scraps, // outsides
+ Vector & cookies, Vector & cookieVerts) // insides
+{
+ // perhaps going too far with the cookie cutter analogy, but...
+ // cutter is used to cut cuttee
+ //
+ // the part that is inside of all cutter edges (on x-z plane)
+ // is put into the "cookie" list, parts that are outside are put
+ // onto the "scraps" list. "scraps" are indexed polys with indices
+ // and vertices in mIndexList and mVertexList. Cookies aren't indexed
+ // and points go into cookieVerts list.
+
+ ALWAYS_RETURN_FRONT_AND_BACK = true; // split routine will return polys even if no split occurs
+
+ // save off current state in case nothing inside all the edges of cutter (i.e., no "cookie")
+ S32 vsStart = cuttee.vertexStart;
+ S32 vcStart = cuttee.vertexCount;
+ S32 milStart = mIndexList.size();
+ S32 mvlStart = mVertexList.size();
+ S32 scStart = scraps.size();
+
+ Point3F a, b;
+ Poly scrap;
+ a = mVertexList[mIndexList[cutter.vertexStart+cutter.vertexCount-1]].point;
+ for (S32 i=0; i PolyExtentsList;
+ typedef Vector PolyIndexList;
+
+ // Internal data
+ PolyExtentsList mPolyExtentsList;
+ PolyIndexList mPolyIndexList;
+ Point3F mExtent; // dimensions of the sort area
+ S32 mBase; // base position in the list...everything before this is sorted correctly
+ Poly * mBasePoly; // poly currently in base position
+ Point3F * mBaseNormal; // normal of poly currently in base position
+ F32 mBaseDot; // dot of basePoly with baseNormal
+ F32 mBaseYMax; // max y extent of base poly
+ S32 mMaxTouched; // highest index swapped into thus far...y-extents may be improperly sorted before this index
+ PolyExtents * mBaseExtents; // x,y,z extents of basePoly
+
+ // set the base position -- everything before this point should be correctly sorted
+ void setBase(S32);
+
+ // some utilities used for sorting
+ bool splitPoly(const Poly & sourcePoly, Point3F & normal, F32 k, Poly & front, Poly & back);
+ bool overlap(Poly *, Poly *);
+ void handleOverlap(Poly * testPoly, Point3F & testNormal, F32 testDot, S32 & testOffset, bool & switched);
+ void sortByYExtents();
+ void setExtents(Poly &, PolyExtents &);
+
+ // one iteration of the sort routine -- finds a poly to fill current base position
+ bool sortNext();
+
+ // used by depthPartition
+ void cookieCutter(Poly & cutter, Poly & cuttee,
+ Vector & scraps,
+ Vector & cookies, Vector & cookieVerts);
+
+ void wireCube(const Point3F & size, const Point3F & pos);
+
+ public:
+
+ //
+ DepthSortList();
+ ~DepthSortList();
+ void set(const MatrixF & mat, Point3F & extents);
+ void clear();
+ void clearSort();
+
+ // the point of this class
+ void sort();
+ void depthPartition(const Point3F * sourceVerts, U32 numVerts, Vector & partition, Vector & partitionVerts);
+
+ // Virtual methods
+ void end();
+ // U32 addPoint(const Point3F& p);
+ // bool isEmpty() const;
+ // void begin(U32 material,U32 surfaceKey);
+ // void plane(U32 v1,U32 v2,U32 v3);
+ // void plane(const PlaneF& p);
+ // void vertex(U32 vi);
+ bool getMapping(MatrixF *, Box3F *);
+
+ // access to the polys in order (note: returned pointers are volatile, may change if polys added).
+ void getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent);
+
+ // unordered access
+ PolyExtents & getExtents(U32 idx) { return mPolyExtentsList[idx]; }
+ Poly & getPoly(U32 idx) { return mPolyList[idx]; }
+
+ //
+ void render();
+
+ static bool renderWithDepth;
+};
+
+inline void DepthSortList::getOrderedPoly(U32 ith, Poly ** poly, PolyExtents ** polyExtent)
+{
+ *poly = &mPolyList[mPolyIndexList[ith]];
+ *polyExtent = &mPolyExtentsList[mPolyIndexList[ith]];
+}
+
+#endif
diff --git a/engine/collision/earlyOutPolyList.cc b/engine/collision/earlyOutPolyList.cc
new file mode 100755
index 0000000..afc4e57
--- /dev/null
+++ b/engine/collision/earlyOutPolyList.cc
@@ -0,0 +1,267 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "console/console.h"
+#include "collision/earlyOutPolyList.h"
+
+
+//----------------------------------------------------------------------------
+
+EarlyOutPolyList::EarlyOutPolyList()
+{
+ VECTOR_SET_ASSOCIATION(mPolyList);
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mIndexList);
+ VECTOR_SET_ASSOCIATION(mPolyPlaneList);
+ VECTOR_SET_ASSOCIATION(mPlaneList);
+
+ mNormal.set(0, 0, 0);
+ mIndexList.reserve(100);
+
+ mEarlyOut = false;
+}
+
+EarlyOutPolyList::~EarlyOutPolyList()
+{
+
+}
+
+
+//----------------------------------------------------------------------------
+void EarlyOutPolyList::clear()
+{
+ // Only clears internal data
+ mPolyList.clear();
+ mVertexList.clear();
+ mIndexList.clear();
+ mPolyPlaneList.clear();
+
+ mEarlyOut = false;
+}
+
+bool EarlyOutPolyList::isEmpty() const
+{
+ return mEarlyOut == false;
+}
+
+
+//----------------------------------------------------------------------------
+
+U32 EarlyOutPolyList::addPoint(const Point3F& p)
+{
+ if (mEarlyOut == true)
+ return 0;
+
+ mVertexList.increment();
+ Vertex& v = mVertexList.last();
+ v.point.x = p.x * mScale.x;
+ v.point.y = p.y * mScale.y;
+ v.point.z = p.z * mScale.z;
+ mMatrix.mulP(v.point);
+
+ // Build the plane mask
+ v.mask = 0;
+ for (U32 i = 0; i < mPlaneList.size(); i++)
+ if (mPlaneList[i].distToPlane(v.point) > 0)
+ v.mask |= 1 << i;
+
+ // If the point is inside all the planes, then we're done!
+ if (v.mask == 0)
+ mEarlyOut = true;
+
+ return mVertexList.size() - 1;
+}
+
+
+U32 EarlyOutPolyList::addPlane(const PlaneF& plane)
+{
+ mPolyPlaneList.increment();
+ mPlaneTransformer.transform(plane, mPolyPlaneList.last());
+
+ return mPolyPlaneList.size() - 1;
+}
+
+
+//----------------------------------------------------------------------------
+
+void EarlyOutPolyList::begin(U32 material,U32 surfaceKey)
+{
+ if (mEarlyOut == true)
+ return;
+
+ mPolyList.increment();
+ Poly& poly = mPolyList.last();
+ poly.object = mCurrObject;
+ poly.material = material;
+ poly.vertexStart = mIndexList.size();
+ poly.surfaceKey = surfaceKey;
+}
+
+
+//----------------------------------------------------------------------------
+
+void EarlyOutPolyList::plane(U32 v1,U32 v2,U32 v3)
+{
+ if (mEarlyOut == true)
+ return;
+
+ mPolyList.last().plane.set(mVertexList[v1].point,
+ mVertexList[v2].point,mVertexList[v3].point);
+}
+
+void EarlyOutPolyList::plane(const PlaneF& p)
+{
+ if (mEarlyOut == true)
+ return;
+
+ mPlaneTransformer.transform(p, mPolyList.last().plane);
+}
+
+void EarlyOutPolyList::plane(const U32 index)
+{
+ if (mEarlyOut == true)
+ return;
+
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ mPolyList.last().plane = mPolyPlaneList[index];
+}
+
+const PlaneF& EarlyOutPolyList::getIndexedPlane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ return mPolyPlaneList[index];
+}
+
+
+//----------------------------------------------------------------------------
+
+void EarlyOutPolyList::vertex(U32 vi)
+{
+ if (mEarlyOut == true)
+ return;
+
+ mIndexList.push_back(vi);
+}
+
+
+//----------------------------------------------------------------------------
+
+void EarlyOutPolyList::end()
+{
+ if (mEarlyOut == true)
+ return;
+
+ Poly& poly = mPolyList.last();
+
+ // Anything facing away from the mNormal is rejected
+ if (mDot(poly.plane,mNormal) > 0) {
+ mIndexList.setSize(poly.vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+
+ // Build intial inside/outside plane masks
+ U32 indexStart = poly.vertexStart;
+ U32 vertexCount = mIndexList.size() - indexStart;
+
+ U32 frontMask = 0,backMask = 0;
+ U32 i;
+ for (i = indexStart; i < mIndexList.size(); i++) {
+ U32 mask = mVertexList[mIndexList[i]].mask;
+ frontMask |= mask;
+ backMask |= ~mask;
+ }
+
+ // Trivial accept if all the vertices are on the backsides of
+ // all the planes.
+ if (!frontMask) {
+ poly.vertexCount = vertexCount;
+ mEarlyOut = true;
+ return;
+ }
+
+ // Trivial reject if any plane not crossed has all it's points
+ // on the front.
+ U32 crossMask = frontMask & backMask;
+ if (~crossMask & frontMask) {
+ mIndexList.setSize(poly.vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+
+ // Need to do some clipping
+ for (U32 p = 0; p < mPlaneList.size(); p++) {
+ U32 pmask = 1 << p;
+ // Only test against this plane if we have something
+ // on both sides
+ if (crossMask & pmask) {
+ U32 indexEnd = mIndexList.size();
+ U32 i1 = indexEnd - 1;
+ U32 mask1 = mVertexList[mIndexList[i1]].mask;
+
+ for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
+ U32 mask2 = mVertexList[mIndexList[i2]].mask;
+ if ((mask1 ^ mask2) & pmask) {
+ //
+ mVertexList.increment();
+ VectorF& v1 = mVertexList[mIndexList[i1]].point;
+ VectorF& v2 = mVertexList[mIndexList[i2]].point;
+ VectorF vv = v2 - v1;
+ F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
+
+ mIndexList.push_back(mVertexList.size() - 1);
+ Vertex& iv = mVertexList.last();
+ iv.point.x = v1.x + vv.x * t;
+ iv.point.y = v1.y + vv.y * t;
+ iv.point.z = v1.z + vv.z * t;
+ iv.mask = 0;
+
+ // Test against the remaining planes
+ for (U32 i = p + 1; i < mPlaneList.size(); i++)
+ if (mPlaneList[i].distToPlane(iv.point) > 0) {
+ iv.mask = 1 << i;
+ break;
+ }
+ }
+ if (!(mask2 & pmask)) {
+ U32 index = mIndexList[i2];
+ mIndexList.push_back(index);
+ }
+ mask1 = mask2;
+ i1 = i2;
+ }
+
+ // Check for degenerate
+ indexStart = indexEnd;
+ if (mIndexList.size() - indexStart < 3) {
+ mIndexList.setSize(poly.vertexStart);
+ mPolyList.decrement();
+ return;
+ }
+ }
+ }
+
+ // If we reach here, then there's a poly!
+ mEarlyOut = true;
+
+ // Emit what's left and compress the index list.
+ poly.vertexCount = mIndexList.size() - indexStart;
+ memcpy(&mIndexList[poly.vertexStart],
+ &mIndexList[indexStart],poly.vertexCount);
+ mIndexList.setSize(poly.vertexStart + poly.vertexCount);
+}
+
+
+//----------------------------------------------------------------------------
+
+void EarlyOutPolyList::memcpy(U32* dst, U32* src,U32 size)
+{
+ U32* end = src + size;
+ while (src != end)
+ *dst++ = *src++;
+}
+
diff --git a/engine/collision/earlyOutPolyList.h b/engine/collision/earlyOutPolyList.h
new file mode 100755
index 0000000..ae102a9
--- /dev/null
+++ b/engine/collision/earlyOutPolyList.h
@@ -0,0 +1,80 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _EARLYOUTPOLYLIST_H_
+#define _EARLYOUTPOLYLIST_H_
+
+#ifndef _ABSTRACTPOLYLIST_H_
+#include "collision/abstractPolyList.h"
+#endif
+
+
+/// Early out check PolyList
+///
+/// This class is used primarily for triggers and similar checks. It checks to see
+/// if any of the geometry you feed it is inside its area, and if it is, it stops
+/// checking for any more data and returns a true value. This is good if you want
+/// to know if anything is in your "trigger" area, for instance.
+///
+/// @see AbstractPolyList
+class EarlyOutPolyList : public AbstractPolyList
+{
+ void memcpy(U32* d, U32* s,U32 size);
+
+ // Internal data
+ struct Vertex {
+ Point3F point;
+ U32 mask;
+ };
+
+ struct Poly {
+ PlaneF plane;
+ SceneObject* object;
+ U32 material;
+ U32 vertexStart;
+ U32 vertexCount;
+ U32 surfaceKey;
+ };
+
+ public:
+ typedef Vector PlaneList;
+ private:
+ typedef Vector VertexList;
+ typedef Vector PolyList;
+ typedef Vector IndexList;
+
+ PolyList mPolyList;
+ VertexList mVertexList;
+ IndexList mIndexList;
+ bool mEarlyOut;
+
+ PlaneList mPolyPlaneList;
+
+ public:
+ // Data set by caller
+ PlaneList mPlaneList;
+ VectorF mNormal;
+
+ public:
+ EarlyOutPolyList();
+ ~EarlyOutPolyList();
+ void clear();
+
+ // Virtual methods
+ bool isEmpty() const;
+ U32 addPoint(const Point3F& p);
+ U32 addPlane(const PlaneF& plane);
+ void begin(U32 material,U32 surfaceKey);
+ void plane(U32 v1,U32 v2,U32 v3);
+ void plane(const PlaneF& p);
+ void plane(const U32 index);
+ void vertex(U32 vi);
+ void end();
+
+ protected:
+ const PlaneF& getIndexedPlane(const U32 index);
+};
+
+#endif // _H_EARLYOUTPOLYLIST_
diff --git a/engine/collision/extrudedPolyList.cc b/engine/collision/extrudedPolyList.cc
new file mode 100755
index 0000000..6c78127
--- /dev/null
+++ b/engine/collision/extrudedPolyList.cc
@@ -0,0 +1,492 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "console/console.h"
+#include "collision/extrudedPolyList.h"
+#include "collision/polyhedron.h"
+#include "collision/collision.h"
+
+
+const F32 sgFrontEpsilon = 0.01;
+
+//----------------------------------------------------------------------------
+
+// use table for U64 shifts of the form: 1 << N
+// because compiler makes it a function call if done directly...
+U32 U32leftShift[33] =
+{
+ U32(1) << 0,
+ U32(1) << 1,
+ U32(1) << 2,
+ U32(1) << 3,
+ U32(1) << 4,
+ U32(1) << 5,
+ U32(1) << 6,
+ U32(1) << 7,
+ U32(1) << 8,
+ U32(1) << 9,
+
+ U32(1) << 10,
+ U32(1) << 11,
+ U32(1) << 12,
+ U32(1) << 13,
+ U32(1) << 14,
+ U32(1) << 15,
+ U32(1) << 16,
+ U32(1) << 17,
+ U32(1) << 18,
+ U32(1) << 19,
+
+ U32(1) << 20,
+ U32(1) << 21,
+ U32(1) << 22,
+ U32(1) << 23,
+ U32(1) << 24,
+ U32(1) << 25,
+ U32(1) << 26,
+ U32(1) << 27,
+ U32(1) << 28,
+ U32(1) << 29,
+
+ U32(1) << 30,
+ U32(1) << 31,
+ U32(0) // one more for good measure
+};
+
+// Minimum distance from a face
+F32 ExtrudedPolyList::FaceEpsilon = 0.01;
+
+// Value used to compare collision times
+F32 ExtrudedPolyList::EqualEpsilon = 0.0001;
+
+ExtrudedPolyList::ExtrudedPolyList()
+{
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mIndexList);
+ VECTOR_SET_ASSOCIATION(mExtrudedList);
+ VECTOR_SET_ASSOCIATION(mPlaneList);
+ VECTOR_SET_ASSOCIATION(mPolyPlaneList);
+
+ mVelocity.set(0,0,0);
+ mIndexList.reserve(128);
+ mVertexList.reserve(64);
+ mPolyPlaneList.reserve(64);
+ mPlaneList.reserve(64);
+ mCollisionList = 0;
+}
+
+ExtrudedPolyList::~ExtrudedPolyList()
+{
+}
+
+
+//----------------------------------------------------------------------------
+
+bool ExtrudedPolyList::isEmpty() const
+{
+ return mCollisionList->count == 0;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ExtrudedPolyList::extrude(const Polyhedron& pt, const VectorF& vector)
+{
+ // Clear state
+ mIndexList.clear();
+ mVertexList.clear();
+ mPlaneList.clear();
+ mPolyPlaneList.clear();
+ mFaceShift = 0;
+
+ // Determine which faces will be extruded.
+ mExtrudedList.setSize(pt.planeList.size());
+ for (U32 f = 0; f < pt.planeList.size(); f++) {
+ const PlaneF& face = pt.planeList[f];
+ ExtrudedFace& eface = mExtrudedList[f];
+ F32 dot = mDot(face,vector);
+ eface.active = dot > EqualEpsilon;
+ if (eface.active) {
+ eface.faceShift = FaceEpsilon / dot;
+ eface.maxDistance = dot;
+ eface.plane = face;
+ eface.planeMask = U32leftShift[mPlaneList.size()]; // U64(1) << mPlaneList.size();
+
+ // Add the face as a plane to clip against.
+ mPlaneList.increment(2);
+ PlaneF* plane = mPlaneList.end() - 2;
+ plane[0] = plane[1] = face;
+ plane[0].invert();
+ }
+ }
+
+ // Produce extruded planes for bounding and internal edges
+ for (U32 e = 0; e < pt.edgeList.size(); e++) {
+ Polyhedron::Edge const& edge = pt.edgeList[e];
+ ExtrudedFace& ef1 = mExtrudedList[edge.face[0]];
+ ExtrudedFace& ef2 = mExtrudedList[edge.face[1]];
+ if (ef1.active || ef2.active) {
+
+ // Assumes that the edge points are clockwise
+ // for face[0].
+ const Point3F& p1 = pt.pointList[edge.vertex[1]];
+ const Point3F &p2 = pt.pointList[edge.vertex[0]];
+ Point3F p3 = p2 + vector;
+
+ mPlaneList.increment(2);
+ PlaneF* plane = mPlaneList.end() - 2;
+ plane[0].set(p3,p2,p1);
+ plane[1] = plane[0];
+ plane[1].invert();
+
+ U32 pmask = U32leftShift[mPlaneList.size()-2]; // U64(1) << (mPlaneList.size()-2)
+ ef1.planeMask |= pmask;
+ ef2.planeMask |= pmask << 1;
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+void ExtrudedPolyList::setCollisionList(CollisionList* info)
+{
+ mCollisionList = info;
+ mCollisionList->count = 0;
+ mCollisionList->t = 2;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ExtrudedPolyList::adjustCollisionTime()
+{
+ // Called after all the polys have been added.
+ // There was a reason for doing it here instead of subtracting
+ // the face Epsilon (faceShift) when the closest point is calculated,
+ // but I can't remember what it is...
+ if (mCollisionList->count) {
+ mCollisionList->t -= mFaceShift;
+ if (mCollisionList->t > 1)
+ mCollisionList->t = 1;
+ else
+ if (mCollisionList->t < 0)
+ mCollisionList->t = 0;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+U32 ExtrudedPolyList::addPoint(const Point3F& p)
+{
+ mVertexList.increment();
+ Vertex& v = mVertexList.last();
+
+ v.point.x = p.x * mScale.x;
+ v.point.y = p.y * mScale.y;
+ v.point.z = p.z * mScale.z;
+ mMatrix.mulP(v.point);
+
+ // Build the plane mask, planes come in pairs
+ v.mask = 0;
+ for (U32 i = 0; i < mPlaneList.size(); i += 2)
+ {
+ F32 dist = mPlaneList[i].distToPlane(v.point);
+ if (dist >= sgFrontEpsilon)
+ v.mask |= U32leftShift[i]; // U64(1) << i;
+ else
+ v.mask |= U32leftShift[i+1]; // U64(2) << i;
+ }
+ return mVertexList.size() - 1;
+}
+
+
+U32 ExtrudedPolyList::addPlane(const PlaneF& plane)
+{
+ mPolyPlaneList.increment();
+ mPlaneTransformer.transform(plane, mPolyPlaneList.last());
+
+ return mPolyPlaneList.size() - 1;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ExtrudedPolyList::begin(U32 material, U32 /*surfaceKey*/)
+{
+ mPoly.object = mCurrObject;
+ mPoly.material = material;
+ mIndexList.clear();
+}
+
+void ExtrudedPolyList::plane(U32 v1, U32 v2, U32 v3)
+{
+ mPoly.plane.set(mVertexList[v1].point,
+ mVertexList[v2].point,
+ mVertexList[v3].point);
+}
+
+void ExtrudedPolyList::plane(const PlaneF& p)
+{
+ mPlaneTransformer.transform(p, mPoly.plane);
+}
+
+void ExtrudedPolyList::plane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ mPoly.plane = mPolyPlaneList[index];
+}
+
+const PlaneF& ExtrudedPolyList::getIndexedPlane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ return mPolyPlaneList[index];
+}
+
+
+void ExtrudedPolyList::vertex(U32 vi)
+{
+ mIndexList.push_back(vi);
+}
+
+void ExtrudedPolyList::end()
+{
+ // Anything facing away from the mVelocity is rejected
+ if (mCollisionList->count >= CollisionList::MaxCollisions ||
+ mDot(mPoly.plane, mNormalVelocity) > 0)
+ return;
+
+ // Test the mPoly against the planes each extruded face.
+ U32 cFaceCount = 0;
+ ExtrudedFace* cFace[30];
+ bool cEdgeColl[30];
+ ExtrudedFace* face = mExtrudedList.begin();
+ ExtrudedFace* end = mExtrudedList.end();
+ for (; face != end; face++)
+ {
+ if (face->active && (face->faceDot = -mDot(face->plane,mPoly.plane)) > 0)
+ {
+ if (testPoly(*face)) {
+ cFace[cFaceCount] = face;
+ cEdgeColl[cFaceCount++] = false;
+ }
+ }
+ }
+ if (!cFaceCount)
+ {
+ face = mExtrudedList.begin();
+ end = mExtrudedList.end();
+ for (; face != end; face++)
+ {
+ if (face->active && (face->faceDot = -mDot(face->plane,mPoly.plane)) <= 0)
+ {
+ if (testPoly(*face)) {
+ cFace[cFaceCount] = face;
+ cEdgeColl[cFaceCount++] = true;
+ }
+ }
+ }
+ }
+ if (!cFaceCount)
+ return;
+
+ // Pick the best collision face
+ face = cFace[0];
+ bool edge = cEdgeColl[0];
+ for (U32 f = 1; f < cFaceCount; f++)
+ {
+ if (cFace[f]->faceDot > face->faceDot)
+ {
+ face = cFace[f];
+ edge = cEdgeColl[f];
+ }
+ }
+ // Add it to the collision list if it's better and/or equal
+ // to our current best.
+ if (face->time <= mCollisionList->t + EqualEpsilon) {
+ if (face->time < mCollisionList->t - EqualEpsilon) {
+ mFaceShift = face->faceShift;
+ mCollisionList->t = face->time;
+ mCollisionList->count = 0;
+ mCollisionList->maxHeight = face->height;
+ }
+ else {
+ if (face->height > mCollisionList->maxHeight)
+ mCollisionList->maxHeight = face->height;
+ if (face->faceShift > mFaceShift)
+ mFaceShift = face->faceShift;
+ }
+ Collision& collision = mCollisionList->collision[mCollisionList->count++];
+ collision.point = face->point;
+ collision.faceDot = face->faceDot;
+ collision.face = face - mExtrudedList.begin();
+ collision.object = mPoly.object;
+ collision.normal = mPoly.plane;
+ collision.material = mPoly.material;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+bool ExtrudedPolyList::testPoly(ExtrudedFace& face)
+{
+ // Build intial inside/outside plane masks
+ U32 indexStart = 0;
+ U32 indexEnd = mIndexList.size();
+ U32 oVertexSize = mVertexList.size();
+ U32 oIndexSize = mIndexList.size();
+
+ U32 frontMask = 0,backMask = 0;
+ for (U32 i = indexStart; i < indexEnd; i++) {
+ U32 mask = mVertexList[mIndexList[i]].mask & face.planeMask;
+ frontMask |= mask;
+ backMask |= ~mask;
+ }
+
+ // Clip the mPoly against the planes that bound the face...
+ // Trivial accept if all the vertices are on the backsides of
+ // all the planes.
+ if (frontMask) {
+ // Trivial reject if any plane not crossed has all it's points
+ // on the front.
+ U32 crossMask = frontMask & backMask;
+ if (~crossMask & frontMask)
+ return false;
+
+ // Need to do some clipping
+ for (U32 p = 0; p < mPlaneList.size(); p++) {
+ U32 pmask = U32leftShift[p]; // U64(1) << p;
+ U32 newStart = mIndexList.size();
+
+ // Only test against this plane if we have something
+ // on both sides
+ if (face.planeMask & crossMask & pmask) {
+ U32 i1 = indexEnd - 1;
+ U32 mask1 = mVertexList[mIndexList[i1]].mask;
+
+ for (U32 i2 = indexStart; i2 < indexEnd; i2++) {
+ U32 mask2 = mVertexList[mIndexList[i2]].mask;
+ if ((mask1 ^ mask2) & pmask) {
+ //
+ mVertexList.increment();
+ VectorF& v1 = mVertexList[mIndexList[i1]].point;
+ VectorF& v2 = mVertexList[mIndexList[i2]].point;
+ VectorF vv = v2 - v1;
+ F32 t = -mPlaneList[p].distToPlane(v1) / mDot(mPlaneList[p],vv);
+
+ mIndexList.push_back(mVertexList.size() - 1);
+ Vertex& iv = mVertexList.last();
+ iv.point.x = v1.x + vv.x * t;
+ iv.point.y = v1.y + vv.y * t;
+ iv.point.z = v1.z + vv.z * t;
+ iv.mask = 0;
+
+ // Test against the remaining planes
+ for (U32 i = p + 1; i < mPlaneList.size(); i++) {
+ U32 mask = U32leftShift[i]; // U64(1) << i;
+ if (face.planeMask & mask &&
+ mPlaneList[i].distToPlane(iv.point) > 0) {
+ iv.mask = mask;
+ break;
+ }
+ }
+ }
+ if (!(mask2 & pmask)) {
+ U32 index = mIndexList[i2];
+ mIndexList.push_back(index);
+ }
+ mask1 = mask2;
+ i1 = i2;
+ }
+
+ // Check for degenerate
+ indexStart = newStart;
+ indexEnd = mIndexList.size();
+ if (mIndexList.size() - indexStart < 3) {
+ mVertexList.setSize(oVertexSize);
+ mIndexList.setSize(oIndexSize);
+ return false;
+ }
+ }
+ }
+ }
+
+ // Find closest point on the mPoly
+ Point3F bp;
+ F32 bd = 1E30;
+ F32 height = -1E30;
+ for (U32 b = indexStart; b < indexEnd; b++) {
+ Vertex& vertex = mVertexList[mIndexList[b]];
+ F32 dist = face.plane.distToPlane(vertex.point);
+ if (dist <= bd) {
+ bd = (dist < 0)? 0: dist;
+ bp = vertex.point;
+ }
+ // Since we don't clip against the back plane, we'll
+ // only include vertex heights that are within range.
+ if (vertex.point.z > height && dist < face.maxDistance)
+ height = vertex.point.z;
+ }
+
+ // Remove temporary data
+ mVertexList.setSize(oVertexSize);
+ mIndexList.setSize(oIndexSize);
+
+ // Add it to the collision list if it's better and/or equal
+ // to our current best.
+ if (bd < face.maxDistance + FaceEpsilon) {
+ face.time = bd / face.maxDistance;
+ face.height = height;
+ face.point = bp;
+ return true;
+ }
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+
+void ExtrudedPolyList::render()
+{
+ if (!mCollisionList)
+ return;
+
+ glBegin(GL_LINES);
+ glColor3f(1,1,0);
+
+ for (U32 d = 0; d < mCollisionList->count; d++) {
+ Collision& face = mCollisionList->collision[d];
+ Point3F ep = face.point;
+ ep += face.normal;
+ glVertex3fv(face.point);
+ glVertex3fv(ep);
+ }
+
+ glEnd();
+}
+
+//--------------------------------------------------------------------------
+void ExtrudedPolyList::setVelocity(const VectorF& velocity)
+{
+ mVelocity = velocity;
+ if (velocity.isZero() == false)
+ {
+ mNormalVelocity = velocity;
+ mNormalVelocity.normalize();
+ setInterestNormal(mNormalVelocity);
+ }
+ else
+ {
+ mNormalVelocity.set(0, 0, 0);
+ clearInterestNormal();
+ }
+}
+
+
diff --git a/engine/collision/extrudedPolyList.h b/engine/collision/extrudedPolyList.h
new file mode 100755
index 0000000..38da399
--- /dev/null
+++ b/engine/collision/extrudedPolyList.h
@@ -0,0 +1,107 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _EXTRUDEDPOLYLIST_H_
+#define _EXTRUDEDPOLYLIST_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+#ifndef _ABSTRACTPOLYLIST_H_
+#include "collision/abstractPolyList.h"
+#endif
+
+struct Polyhedron;
+struct CollisionList;
+
+//----------------------------------------------------------------------------
+/// Extruded Polytope PolyList
+///
+/// This class is used primarily for collision detection, by objects which need
+/// to check for obstructions along their path. You feed it a polytope to
+/// extrude along the direction of movement, and it gives you a list of collisions.
+///
+/// @see AbstractPolyList
+class ExtrudedPolyList: public AbstractPolyList
+{
+public:
+ struct Vertex {
+ Point3F point;
+ U32 mask;
+ };
+
+ struct Poly {
+ PlaneF plane;
+ SceneObject* object;
+ U32 material;
+ };
+
+ struct ExtrudedFace {
+ bool active;
+ PlaneF plane;
+ F32 maxDistance;
+ U32 planeMask;
+ F32 faceDot;
+ F32 faceShift;
+ F32 time;
+ Point3F point;
+ F32 height;
+ };
+
+ typedef Vector ExtrudedList;
+ typedef Vector PlaneList;
+ typedef Vector VertexList;
+ typedef Vector IndexList;
+
+ static F32 EqualEpsilon;
+ static F32 FaceEpsilon;
+
+ // Internal data
+ VertexList mVertexList;
+ IndexList mIndexList;
+ ExtrudedList mExtrudedList;
+ PlaneList mPlaneList;
+ VectorF mVelocity;
+ VectorF mNormalVelocity;
+ F32 mFaceShift;
+ Poly mPoly;
+
+ // Returned info
+ CollisionList* mCollisionList;
+
+ PlaneList mPolyPlaneList;
+
+ //
+private:
+ bool testPoly(ExtrudedFace&);
+
+public:
+ ExtrudedPolyList();
+ ~ExtrudedPolyList();
+ void extrude(const Polyhedron&, const VectorF& vec);
+ void setVelocity(const VectorF& velocity);
+ void setCollisionList(CollisionList*);
+ void adjustCollisionTime();
+ void render();
+
+ // Virtual methods
+ bool isEmpty() const;
+ U32 addPoint(const Point3F& p);
+ U32 addPlane(const PlaneF& plane);
+ void begin(U32 material, U32 surfaceKey);
+ void plane(U32 v1,U32 v2,U32 v3);
+ void plane(const PlaneF& p);
+ void plane(const U32 index);
+ void vertex(U32 vi);
+ void end();
+
+ protected:
+ const PlaneF& getIndexedPlane(const U32 index);
+};
+
+#endif
diff --git a/engine/collision/gjk.cc b/engine/collision/gjk.cc
new file mode 100755
index 0000000..72dfdca
--- /dev/null
+++ b/engine/collision/gjk.cc
@@ -0,0 +1,422 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//
+// The core algorithms in this file are based on code written
+// by G. van den Bergen for his interference detection library,
+// "SOLID 2.0"
+//-----------------------------------------------------------------------------
+
+#include "dgl/dgl.h"
+#include "core/dataChunker.h"
+#include "collision/collision.h"
+#include "sceneGraph/sceneGraph.h"
+#include "sim/sceneObject.h"
+#include "terrain/terrData.h"
+#include "collision/convex.h"
+#include "collision/gjk.h"
+
+
+//----------------------------------------------------------------------------
+
+static F32 rel_error = 1E-5; // relative error in the computed distance
+static F32 sTolerance = 1E-3; // Distance tolerance
+static F32 sEpsilon2 = 1E-20; // Zero length vector
+static U32 sIteration = 15; // Stuck in a loop?
+
+S32 num_iterations = 0;
+S32 num_irregularities = 0;
+
+
+//----------------------------------------------------------------------------
+
+GjkCollisionState::GjkCollisionState()
+{
+ a = b = 0;
+}
+
+GjkCollisionState::~GjkCollisionState()
+{
+}
+
+
+//----------------------------------------------------------------------------
+
+void GjkCollisionState::swap()
+{
+ Convex* t = a; a = b; b = t;
+ CollisionStateList* l = mLista; mLista = mListb; mListb = l;
+ v.neg();
+}
+
+
+//----------------------------------------------------------------------------
+
+void GjkCollisionState::compute_det()
+{
+ // Dot new point with current set
+ for (int i = 0, bit = 1; i < 4; ++i, bit <<=1)
+ if (bits & bit)
+ dp[i][last] = dp[last][i] = mDot(y[i], y[last]);
+ dp[last][last] = mDot(y[last], y[last]);
+
+ // Calulate the determinent
+ det[last_bit][last] = 1;
+ for (int j = 0, sj = 1; j < 4; ++j, sj <<= 1) {
+ if (bits & sj) {
+ int s2 = sj | last_bit;
+ det[s2][j] = dp[last][last] - dp[last][j];
+ det[s2][last] = dp[j][j] - dp[j][last];
+ for (int k = 0, sk = 1; k < j; ++k, sk <<= 1) {
+ if (bits & sk) {
+ int s3 = sk | s2;
+ det[s3][k] = det[s2][j] * (dp[j][j] - dp[j][k]) +
+ det[s2][last] * (dp[last][j] - dp[last][k]);
+ det[s3][j] = det[sk | last_bit][k] * (dp[k][k] - dp[k][j]) +
+ det[sk | last_bit][last] * (dp[last][k] - dp[last][j]);
+ det[s3][last] = det[sk | sj][k] * (dp[k][k] - dp[k][last]) +
+ det[sk | sj][j] * (dp[j][k] - dp[j][last]);
+ }
+ }
+ }
+ }
+
+ if (all_bits == 15) {
+ det[15][0] = det[14][1] * (dp[1][1] - dp[1][0]) +
+ det[14][2] * (dp[2][1] - dp[2][0]) +
+ det[14][3] * (dp[3][1] - dp[3][0]);
+ det[15][1] = det[13][0] * (dp[0][0] - dp[0][1]) +
+ det[13][2] * (dp[2][0] - dp[2][1]) +
+ det[13][3] * (dp[3][0] - dp[3][1]);
+ det[15][2] = det[11][0] * (dp[0][0] - dp[0][2]) +
+ det[11][1] * (dp[1][0] - dp[1][2]) +
+ det[11][3] * (dp[3][0] - dp[3][2]);
+ det[15][3] = det[7][0] * (dp[0][0] - dp[0][3]) +
+ det[7][1] * (dp[1][0] - dp[1][3]) +
+ det[7][2] * (dp[2][0] - dp[2][3]);
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+inline void GjkCollisionState::compute_vector(int bits, VectorF& v)
+{
+ F32 sum = 0;
+ v.set(0, 0, 0);
+ for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
+ if (bits & bit) {
+ sum += det[bits][i];
+ v += y[i] * det[bits][i];
+ }
+ }
+ v *= 1 / sum;
+}
+
+
+//----------------------------------------------------------------------------
+
+inline bool GjkCollisionState::valid(int s)
+{
+ for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
+ if (all_bits & bit) {
+ if (s & bit) {
+ if (det[s][i] <= 0)
+ return false;
+ }
+ else
+ if (det[s | bit][i] > 0)
+ return false;
+ }
+ }
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+
+inline bool GjkCollisionState::closest(VectorF& v)
+{
+ compute_det();
+ for (int s = bits; s; --s) {
+ if ((s & bits) == s) {
+ if (valid(s | last_bit)) {
+ bits = s | last_bit;
+ if (bits != 15)
+ compute_vector(bits, v);
+ return true;
+ }
+ }
+ }
+ if (valid(last_bit)) {
+ bits = last_bit;
+ v = y[last];
+ return true;
+ }
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+
+inline bool GjkCollisionState::degenerate(const VectorF& w)
+{
+ for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1)
+ if ((all_bits & bit) && y[i] == w)
+ return true;
+ return false;
+}
+
+
+//----------------------------------------------------------------------------
+
+inline void GjkCollisionState::nextBit()
+{
+ last = 0;
+ last_bit = 1;
+ while (bits & last_bit) {
+ ++last;
+ last_bit <<= 1;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+//----------------------------------------------------------------------------
+
+//----------------------------------------------------------------------------
+
+void GjkCollisionState::set(Convex* aa, Convex* bb,
+ const MatrixF& a2w, const MatrixF& b2w)
+{
+ a = aa;
+ b = bb;
+
+ bits = 0;
+ all_bits = 0;
+ reset(a2w,b2w);
+
+ // link
+ mLista = CollisionStateList::alloc();
+ mLista->mState = this;
+ mListb = CollisionStateList::alloc();
+ mListb->mState = this;
+}
+
+
+//----------------------------------------------------------------------------
+
+void GjkCollisionState::reset(const MatrixF& a2w, const MatrixF& b2w)
+{
+ VectorF zero(0,0,0),sa,sb;
+ a2w.mulP(a->support(zero),&sa);
+ b2w.mulP(b->support(zero),&sb);
+ v = sa - sb;
+ dist = v.len();
+}
+
+
+//----------------------------------------------------------------------------
+
+void GjkCollisionState::getCollisionInfo(const MatrixF& mat, Collision* info)
+{
+ AssertFatal(false, "GjkCollisionState::getCollisionInfo() - There remain scaling problems here.");
+ // This assumes that the shapes do not intersect
+ Point3F pa,pb;
+ if (bits) {
+ getClosestPoints(pa,pb);
+ mat.mulP(pa,&info->point);
+ b->getTransform().mulP(pb,&pa);
+ info->normal = info->point - pa;
+ }
+ else {
+ mat.mulP(p[last],&info->point);
+ info->normal = v;
+ }
+ info->normal.normalize();
+ info->object = b->getObject();
+}
+
+void GjkCollisionState::getClosestPoints(Point3F& p1, Point3F& p2)
+{
+ F32 sum = 0;
+ p1.set(0, 0, 0);
+ p2.set(0, 0, 0);
+ for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
+ if (bits & bit) {
+ sum += det[bits][i];
+ p1 += p[i] * det[bits][i];
+ p2 += q[i] * det[bits][i];
+ }
+ }
+ F32 s = 1 / sum;
+ p1 *= s;
+ p2 *= s;
+}
+
+
+//----------------------------------------------------------------------------
+
+bool GjkCollisionState::intersect(const MatrixF& a2w, const MatrixF& b2w)
+{
+ num_iterations = 0;
+ MatrixF w2a,w2b;
+
+ w2a = a2w;
+ w2b = b2w;
+ w2a.inverse();
+ w2b.inverse();
+ reset(a2w,b2w);
+
+ bits = 0;
+ all_bits = 0;
+
+ do {
+ nextBit();
+
+ VectorF va,sa;
+ w2a.mulV(-v,&va);
+ p[last] = a->support(va);
+ a2w.mulP(p[last],&sa);
+
+ VectorF vb,sb;
+ w2b.mulV(v,&vb);
+ q[last] = b->support(vb);
+ b2w.mulP(q[last],&sb);
+
+ VectorF w = sa - sb;
+ if (mDot(v,w) > 0)
+ return false;
+ if (degenerate(w)) {
+ ++num_irregularities;
+ return false;
+ }
+
+ y[last] = w;
+ all_bits = bits | last_bit;
+
+ ++num_iterations;
+ if (!closest(v) || num_iterations > sIteration) {
+ ++num_irregularities;
+ return false;
+ }
+ }
+ while (bits < 15 && v.lenSquared() > sEpsilon2);
+ return true;
+}
+
+F32 GjkCollisionState::distance(const MatrixF& a2w, const MatrixF& b2w,
+ const F32 dontCareDist, const MatrixF* _w2a, const MatrixF* _w2b)
+{
+ num_iterations = 0;
+ MatrixF w2a,w2b;
+
+ if (_w2a == NULL || _w2b == NULL) {
+ w2a = a2w;
+ w2b = b2w;
+ w2a.inverse();
+ w2b.inverse();
+ }
+ else {
+ w2a = *_w2a;
+ w2b = *_w2b;
+ }
+
+ reset(a2w,b2w);
+ bits = 0;
+ all_bits = 0;
+ F32 mu = 0;
+
+ do {
+ nextBit();
+
+ VectorF va,sa;
+ w2a.mulV(-v,&va);
+ p[last] = a->support(va);
+ a2w.mulP(p[last],&sa);
+
+ VectorF vb,sb;
+ w2b.mulV(v,&vb);
+ q[last] = b->support(vb);
+ b2w.mulP(q[last],&sb);
+
+ VectorF w = sa - sb;
+ F32 nm = mDot(v, w) / dist;
+ if (nm > mu)
+ mu = nm;
+ if (mu > dontCareDist)
+ return mu;
+ if (mFabs(dist - mu) <= dist * rel_error)
+ return dist;
+
+ ++num_iterations;
+ if (degenerate(w) || num_iterations > sIteration) {
+ ++num_irregularities;
+ return dist;
+ }
+
+ y[last] = w;
+ all_bits = bits | last_bit;
+
+ if (!closest(v)) {
+ ++num_irregularities;
+ return dist;
+ }
+
+ dist = v.len();
+ }
+ while (bits < 15 && dist > sTolerance) ;
+
+ if (bits == 15 && mu <= 0)
+ dist = 0;
+ return dist;
+}
+
+
+//----------------------------------------------------------------------------
+
+inline void renderCross(const Point3F x)
+{
+ glVertex3fv(x + VectorF(0,0,+0.1));
+ glVertex3fv(x + VectorF(0,0,-0.1));
+ glVertex3fv(x + VectorF(0,+0.1,0));
+ glVertex3fv(x + VectorF(0,-0.1,0));
+ glVertex3fv(x + VectorF(-0.1,0,0));
+ glVertex3fv(x + VectorF(+0.1,0,0));
+}
+
+void GjkCollisionState::render()
+{
+ glBegin(GL_LINES);
+ glColor3f(1,1,0);
+
+ // Cross for each witness point
+ for (int i = 0, bit = 1; i < 4; ++i, bit <<= 1) {
+ Point3F x;
+ if (bits & bit) {
+ a->getTransform().mulP(p[i],&x);
+ renderCross(x);
+ b->getTransform().mulP(q[i],&x);
+ renderCross(x);
+ }
+ }
+
+ // Cross and line for closest points
+ Point3F sp,ep;
+ if (bits) {
+ Point3F p1,p2;
+ getClosestPoints(p1,p2);
+ a->getTransform().mulP(p1,&sp);
+ b->getTransform().mulP(p2,&ep);
+ }
+ else {
+ a->getTransform().mulP(p[0],&sp);
+ b->getTransform().mulP(q[0],&ep);
+ }
+ renderCross(sp);
+ renderCross(ep);
+ glVertex3fv(sp);
+ glVertex3fv(ep);
+
+ glEnd();
+}
diff --git a/engine/collision/gjk.h b/engine/collision/gjk.h
new file mode 100755
index 0000000..8b5d1c0
--- /dev/null
+++ b/engine/collision/gjk.h
@@ -0,0 +1,66 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _GJK_H_
+#define _GJK_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+#ifndef _CONVEX_H_
+#include "collision/convex.h"
+#endif
+
+class Convex;
+struct CollisionStateList;
+struct Collision;
+
+//----------------------------------------------------------------------------
+
+struct GjkCollisionState: public CollisionState
+{
+ /// @name Temporary values
+ /// @{
+ Point3F p[4]; ///< support points of object A in local coordinates
+ Point3F q[4]; ///< support points of object B in local coordinates
+ VectorF y[4]; ///< support points of A - B in world coordinates
+
+ S32 bits; ///< identifies current simplex
+ S32 all_bits; ///< all_bits = bits | last_bit
+ F32 det[16][4]; ///< cached sub-determinants
+ F32 dp[4][4]; ///< cached dot products
+
+ S32 last; ///< identifies last found support point
+ S32 last_bit; ///< last_bit = 1<vertexCount, GL_UNSIGNED_INT, &mIndexList[p->vertexStart]);
+
+ //glDisableClientState(GL_VERTEX_ARRAY);
+
+ // If you need normals
+ for (U32 i = 0; i < mPolyList.size(); i++)
+ {
+ if (mPolyList[i].vertexCount < 3)
+ continue;
+
+ ColorF color(gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f), gRandGen.randF(0.0f, 1.0f));
+ glColor3f(color.red, color.green, color.blue);
+ glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, color);
+
+ glBegin(GL_TRIANGLES);
+ for (U32 j = 1; j < mPolyList[i].vertexCount - 1; j++)
+ {
+ U32 i0 = mIndexList[mPolyList[i].vertexStart];
+ U32 i1 = mIndexList[mPolyList[i].vertexStart + j];
+ U32 i2 = mIndexList[mPolyList[i].vertexStart + j + 1];
+
+ Point3F p0 = mVertexList[i0];
+ Point3F p1 = mVertexList[i1];
+ Point3F p2 = mVertexList[i2];
+
+ PlaneF normal = mPlaneList[mPolyList[i].plane];
+ glNormal3f(normal.x, normal.y, normal.z);
+
+ glVertex3f(p0.x, p0.y, p0.z);
+ glVertex3f(p1.x, p1.y, p1.z);
+ glVertex3f(p2.x, p2.y, p2.z);
+ }
+ glEnd();
+ }
+}
+
+void OptimizedPolyList::copyPolyToList(OptimizedPolyList* target, U32 pdx) const
+{
+ target->mPolyList.increment();
+ OptimizedPolyList::Poly& tpoly = target->mPolyList.last();
+
+ tpoly.material = mPolyList[pdx].material;
+ tpoly.object = mPolyList[pdx].object;
+ tpoly.surfaceKey = mPolyList[pdx].surfaceKey;
+ tpoly.vertexCount = mPolyList[pdx].vertexCount;
+
+ PlaneF pln = mPlaneList[mPolyList[pdx].plane];
+ tpoly.plane = target->addPlane(pln);
+
+ tpoly.vertexStart = target->mIndexList.size();
+
+ for (U32 i = 0; i < mPolyList[pdx].vertexCount; i++)
+ {
+ U32 idx = mIndexList[mPolyList[pdx].vertexStart + i];
+ Point3F pt = mVertexList[idx];
+
+ target->mIndexList.increment();
+ target->mIndexList.last() = target->addPoint(pt);
+ }
+}
+
+void OptimizedPolyList::copyPolyToList(OptimizedPolyList* target, const FullPoly& poly) const
+{
+ target->mPolyList.increment();
+ OptimizedPolyList::Poly& tpoly = target->mPolyList.last();
+
+ tpoly.material = poly.material;
+ tpoly.object = poly.object;
+ tpoly.surfaceKey = poly.surfaceKey;
+ tpoly.vertexCount = poly.indexes.size();
+
+ PlaneF pln = poly.plane;
+ tpoly.plane = target->addPlane(pln);
+
+ tpoly.vertexStart = target->mIndexList.size();
+
+ for (U32 i = 0; i < poly.indexes.size(); i++)
+ {
+ Point3F pt = poly.vertexes[poly.indexes[i]];
+
+ target->mIndexList.increment();
+ target->mIndexList.last() = target->addPoint(pt);
+ }
+}
+
+OptimizedPolyList::FullPoly OptimizedPolyList::getFullPoly(U32 pdx)
+{
+ FullPoly ret;
+
+ OptimizedPolyList::Poly& poly = mPolyList[pdx];
+
+ ret.material = poly.material;
+ ret.object = poly.object;
+ ret.surfaceKey = poly.surfaceKey;
+
+ for (U32 i = 0; i < poly.vertexCount; i++)
+ {
+ U32 idx = mIndexList[poly.vertexStart + i];
+ Point3F& pt = mVertexList[idx];
+
+ S32 pdx = -1;
+
+ for (U32 j = 0; j < ret.vertexes.size(); j++)
+ {
+ if (pt == ret.vertexes[j])
+ {
+ pdx = j;
+ break;
+ }
+ }
+
+ if (pdx == -1)
+ {
+ pdx = ret.vertexes.size();
+ ret.vertexes.push_back(pt);
+ }
+
+ ret.indexes.push_back(pdx);
+ }
+
+ return ret;
+}
\ No newline at end of file
diff --git a/engine/collision/optimizedPolyList.h b/engine/collision/optimizedPolyList.h
new file mode 100755
index 0000000..dbadb72
--- /dev/null
+++ b/engine/collision/optimizedPolyList.h
@@ -0,0 +1,128 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _OPTIMIZEDPOLYLIST_H_
+#define _OPTIMIZEDPOLYLIST_H_
+
+#ifndef _ABSTRACTPOLYLIST_H_
+#include "collision/abstractPolyList.h"
+#endif
+
+#define DEV 0.01
+
+/// A concrete, renderable PolyList
+///
+/// This class is used to store geometry from a PolyList query.
+///
+/// It allows you to render this data, as well.
+///
+/// @see AbstractPolyList
+class OptimizedPolyList : public AbstractPolyList
+{
+ public:
+
+ struct Poly
+ {
+ S32 plane;
+ SceneObject* object;
+ S32 material;
+ U32 vertexStart;
+ U32 vertexCount;
+ U32 surfaceKey;
+
+ U32 triangleLightingStartIndex;
+
+ Poly::Poly() { plane = -1; vertexCount = 0; material = -1; };
+ };
+
+ struct FullPoly
+ {
+ PlaneF plane;
+ SceneObject* object;
+ S32 material;
+ U32 surfaceKey;
+
+ Vector indexes;
+ Vector vertexes;
+ };
+
+ enum
+ {
+ NonShared = 0,
+ Concave,
+ Convex
+ };
+
+ struct Edge
+ {
+ U32 type;
+ S32 vertexes[2];
+ S32 faces[2];
+ };
+
+ struct TriangleLighting
+ {
+ U32 lightMapId;
+ PlaneF lightMapEquationX;
+ PlaneF lightMapEquationY;
+ };
+
+ Vector mTriangleLightingList;
+
+ const TriangleLighting *getTriangleLighting(const U32 index)
+ {
+ if(index >= mTriangleLightingList.size())
+ return NULL;
+ return &mTriangleLightingList[index];
+ }
+
+ typedef Vector PlaneList;
+ typedef Vector VertexList;
+ typedef Vector PolyList;
+ typedef Vector IndexList;
+ typedef Vector EdgeList;
+
+ PolyList mPolyList;
+ VertexList mVertexList;
+ IndexList mIndexList;
+ PlaneList mPlaneList;
+ EdgeList mEdgeList;
+
+ PlaneList mPolyPlaneList;
+
+ public:
+ OptimizedPolyList();
+ ~OptimizedPolyList();
+ void clear();
+
+ // Virtual methods
+ U32 addPoint(const Point3F& p);
+ U32 addPlane(const PlaneF& plane);
+ void begin(U32 material,U32 surfaceKey);
+ void plane(U32 v1,U32 v2,U32 v3);
+ void plane(const PlaneF& p);
+ void plane(const U32 index);
+ void vertex(U32 vi);
+ void end();
+
+ inline bool isEqual(Point3F& a, Point3F& b)
+ {
+ return( ( mFabs( a.x - b.x ) < DEV ) &&
+ ( mFabs( a.y - b.y ) < DEV ) &&
+ ( mFabs( a.z - b.z ) < DEV ) );
+ }
+
+ void copyPolyToList(OptimizedPolyList* target, U32 pdx) const;
+ void copyPolyToList(OptimizedPolyList* target, const FullPoly& poly) const;
+ FullPoly getFullPoly(U32 pdx);
+
+ void render();
+ bool isEmpty() const;
+
+ protected:
+ const PlaneF& getIndexedPlane(const U32 index);
+};
+
+#endif // _OPTIMIZEDPOLYLIST_H_
diff --git a/engine/collision/planeExtractor.cc b/engine/collision/planeExtractor.cc
new file mode 100755
index 0000000..1fb943d
--- /dev/null
+++ b/engine/collision/planeExtractor.cc
@@ -0,0 +1,117 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "console/console.h"
+#include "collision/planeExtractor.h"
+
+//----------------------------------------------------------------------------
+// Plane matching parameters
+static F32 NormalEpsilon = 0.93969; // 20 deg.
+static F32 DistanceEpsilon = 100;
+
+
+//----------------------------------------------------------------------------
+
+PlaneExtractorPolyList::PlaneExtractorPolyList()
+{
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mPolyPlaneList);
+}
+
+PlaneExtractorPolyList::~PlaneExtractorPolyList()
+{
+}
+
+
+//----------------------------------------------------------------------------
+
+void PlaneExtractorPolyList::clear()
+{
+ mVertexList.clear();
+ mPolyPlaneList.clear();
+}
+
+U32 PlaneExtractorPolyList::addPoint(const Point3F& p)
+{
+ mVertexList.increment();
+ Point3F& v = mVertexList.last();
+ v.x = p.x * mScale.x;
+ v.y = p.y * mScale.y;
+ v.z = p.z * mScale.z;
+ mMatrix.mulP(v);
+ return mVertexList.size() - 1;
+}
+
+U32 PlaneExtractorPolyList::addPlane(const PlaneF& plane)
+{
+ mPolyPlaneList.increment();
+ mPlaneTransformer.transform(plane, mPolyPlaneList.last());
+
+ return mPolyPlaneList.size() - 1;
+}
+
+
+//----------------------------------------------------------------------------
+
+void PlaneExtractorPolyList::plane(U32 v1,U32 v2,U32 v3)
+{
+ mPlaneList->last().set(mVertexList[v1],
+ mVertexList[v2],mVertexList[v3]);
+}
+
+void PlaneExtractorPolyList::plane(const PlaneF& p)
+{
+ mPlaneTransformer.transform(p, mPlaneList->last());
+}
+
+void PlaneExtractorPolyList::plane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ mPlaneList->last() = mPolyPlaneList[index];
+}
+
+const PlaneF& PlaneExtractorPolyList::getIndexedPlane(const U32 index)
+{
+ AssertFatal(index < mPolyPlaneList.size(), "Out of bounds index!");
+ return mPolyPlaneList[index];
+}
+
+
+//----------------------------------------------------------------------------
+
+bool PlaneExtractorPolyList::isEmpty() const
+{
+ return true;
+}
+
+void PlaneExtractorPolyList::begin(U32,U32)
+{
+ mPlaneList->increment();
+}
+
+void PlaneExtractorPolyList::end()
+{
+ // See if there are any duplicate planes
+ PlaneF &plane = mPlaneList->last();
+ PlaneF *ptr = mPlaneList->begin();
+ for (; ptr != &plane; ptr++)
+ if (mFabs(ptr->d - plane.d) < DistanceEpsilon &&
+ mDot(*ptr,plane) > NormalEpsilon) {
+ mPlaneList->decrement();
+ return;
+ }
+}
+
+void PlaneExtractorPolyList::vertex(U32)
+{
+}
+
+void PlaneExtractorPolyList::render()
+{
+}
+
diff --git a/engine/collision/planeExtractor.h b/engine/collision/planeExtractor.h
new file mode 100755
index 0000000..7f837a7
--- /dev/null
+++ b/engine/collision/planeExtractor.h
@@ -0,0 +1,62 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _PLANEEXTRACTOR_H_
+#define _PLANEEXTRACTOR_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+#ifndef _ABSTRACTPOLYLIST_H_
+#include "collision/abstractPolyList.h"
+#endif
+
+
+//----------------------------------------------------------------------------
+
+/// Fill a Vector with the planes from the geometry passed through this
+/// PolyList.
+///
+/// @see AbstractPolyList
+class PlaneExtractorPolyList: public AbstractPolyList
+{
+ void memcpy(U32* d, U32* s,U32 size);
+
+public:
+ // Internal data
+ typedef Vector VertexList;
+ VertexList mVertexList;
+
+ Vector mPolyPlaneList;
+
+ // Set by caller
+ Vector* mPlaneList;
+
+ //
+ PlaneExtractorPolyList();
+ ~PlaneExtractorPolyList();
+ void clear();
+ void render();
+
+ // Virtual methods
+ bool isEmpty() const;
+ U32 addPoint(const Point3F& p);
+ U32 addPlane(const PlaneF& plane);
+ void begin(U32 material,U32 surfaceKey);
+ void plane(U32 v1,U32 v2,U32 v3);
+ void plane(const PlaneF& p);
+ void plane(const U32 index);
+ void vertex(U32 vi);
+ void end();
+
+ protected:
+ const PlaneF& getIndexedPlane(const U32 index);
+};
+
+
+#endif
diff --git a/engine/collision/polyhedron.cc b/engine/collision/polyhedron.cc
new file mode 100755
index 0000000..35e688b
--- /dev/null
+++ b/engine/collision/polyhedron.cc
@@ -0,0 +1,150 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "dgl/dgl.h"
+#include "console/console.h"
+#include "collision/polyhedron.h"
+
+
+//----------------------------------------------------------------------------
+
+void Polyhedron::buildBox(const MatrixF& transform,const Box3F& box)
+{
+ // Box is assumed to be axis aligned in the source space.
+ // Transform into geometry space
+ Point3F xvec,yvec,zvec,min;
+ transform.getColumn(0,&xvec);
+ xvec *= box.len_x();
+ transform.getColumn(1,&yvec);
+ yvec *= box.len_y();
+ transform.getColumn(2,&zvec);
+ zvec *= box.len_z();
+ transform.mulP(box.min,&min);
+
+ // Initial vertices
+ pointList.setSize(8);
+ pointList[0] = min;
+ pointList[1] = min + yvec;
+ pointList[2] = min + xvec + yvec;
+ pointList[3] = min + xvec;
+ pointList[4] = pointList[0] + zvec;
+ pointList[5] = pointList[1] + zvec;
+ pointList[6] = pointList[2] + zvec;
+ pointList[7] = pointList[3] + zvec;
+
+ // Initial faces
+ planeList.setSize(6);
+ planeList[0].set(pointList[0],xvec);
+ planeList[0].invert();
+ planeList[1].set(pointList[2],yvec);
+ planeList[2].set(pointList[2],xvec);
+ planeList[3].set(pointList[0],yvec);
+ planeList[3].invert();
+ planeList[4].set(pointList[0],zvec);
+ planeList[4].invert();
+ planeList[5].set(pointList[4],zvec);
+
+ // The edges are constructed so that the vertices
+ // are oriented clockwise for face[0]
+ edgeList.setSize(12);
+ Edge* edge = edgeList.begin();
+ S32 nextEdge = 0;
+ for (int i = 0; i < 4; i++) {
+ S32 n = (i == 3)? 0: i + 1;
+ S32 p = (i == 0)? 3: i - 1;
+ edge->vertex[0] = i;
+ edge->vertex[1] = n;
+ edge->face[0] = i;
+ edge->face[1] = 4;
+ edge++;
+ edge->vertex[0] = 4 + i;
+ edge->vertex[1] = 4 + n;
+ edge->face[0] = 5;
+ edge->face[1] = i;
+ edge++;
+ edge->vertex[0] = i;
+ edge->vertex[1] = 4 + i;
+ edge->face[0] = p;
+ edge->face[1] = i;
+ edge++;
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+void Polyhedron::render()
+{
+ glVertexPointer(3,GL_FLOAT,sizeof(Point3F),pointList.address());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glColor3f(1, 0, 1);
+
+ for (S32 e = 0; e < edgeList.size(); e++)
+ glDrawElements(GL_LINES,2,GL_UNSIGNED_INT,&edgeList[e].vertex);
+
+ for (U32 i = 0; i < planeList.size(); i++) {
+ Point3F origin(0, 0, 0);
+ U32 num = 0;
+ for (U32 j = 0; j < edgeList.size(); j++) {
+ if (edgeList[j].face[0] == i || edgeList[j].face[1] == i) {
+ origin += pointList[edgeList[j].vertex[0]];
+ origin += pointList[edgeList[j].vertex[1]];
+ num += 2;
+ }
+ }
+ origin /= F32(num);
+
+ glColor3f(1, 1, 1);
+ glBegin(GL_LINES);
+ glVertex3fv(origin);
+ glVertex3f(origin.x + planeList[i].x * 0.2,
+ origin.y + planeList[i].y * 0.2,
+ origin.z + planeList[i].z * 0.2);
+ glEnd();
+ }
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+void Polyhedron::render(const VectorF& vec,F32 time)
+{
+ bool faceVisible[50];
+ for (int f = 0; f < planeList.size(); f++)
+ faceVisible[f] = mDot(planeList[f],vec) > 0;
+
+ VectorF tvec = vec;
+ tvec *= time;
+ for (int e = 0; e < edgeList.size(); e++) {
+ Polyhedron::Edge const& edge = edgeList[e];
+ if (faceVisible[edge.face[0]] || faceVisible[edge.face[1]]) {
+ Point3F pp;
+
+ glBegin(GL_LINE_LOOP);
+ glColor3f(0.5,0.5,0.5);
+ const Point3F& p1 = pointList[edge.vertex[0]];
+ const Point3F& p2 = pointList[edge.vertex[1]];
+ glVertex3fv(p1);
+ glVertex3fv(p2);
+ pp = p2 + vec;
+ glVertex3fv(pp);
+ pp = p1 + vec;
+ glVertex3fv(pp);
+ glEnd();
+
+ if (time <= 1.0) {
+ glBegin(GL_LINES);
+ glColor3f(0.5,0.5,0);
+ pp = pointList[edge.vertex[0]];
+ pp += tvec;
+ glVertex3fv(pp);
+ pp = pointList[edge.vertex[1]];
+ pp += tvec;
+ glVertex3fv(pp);
+ glEnd();
+ }
+ }
+ }
+}
diff --git a/engine/collision/polyhedron.h b/engine/collision/polyhedron.h
new file mode 100755
index 0000000..38c79d5
--- /dev/null
+++ b/engine/collision/polyhedron.h
@@ -0,0 +1,45 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _POLYHEDRON_H_
+#define _POLYHEDRON_H_
+
+#ifndef _MMATH_H_
+#include "math/mMath.h"
+#endif
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+
+//----------------------------------------------------------------------------
+
+struct Polyhedron
+{
+ struct Edge
+ {
+ // Edge vertices must be oriented clockwise
+ // for face[0]
+ U32 face[2];
+ U32 vertex[2];
+ };
+
+ Vector pointList;
+ Vector planeList;
+ Vector edgeList;
+
+ // Misc support methods
+ Polyhedron() {
+ VECTOR_SET_ASSOCIATION(pointList);
+ VECTOR_SET_ASSOCIATION(planeList);
+ VECTOR_SET_ASSOCIATION(edgeList);
+ }
+
+ void buildBox(const MatrixF& mat, const Box3F& box);
+ void render();
+ void render(const VectorF& vec,F32 time);
+};
+
+
+#endif
diff --git a/engine/collision/polytope.cc b/engine/collision/polytope.cc
new file mode 100755
index 0000000..f764674
--- /dev/null
+++ b/engine/collision/polytope.cc
@@ -0,0 +1,433 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#include "platform/platform.h"
+#include "dgl/dgl.h"
+#include "math/mMath.h"
+#include "collision/collision.h"
+#include "collision/polytope.h"
+
+//----------------------------------------------------------------------------
+
+Polytope::Polytope()
+{
+ VECTOR_SET_ASSOCIATION(mEdgeList);
+ VECTOR_SET_ASSOCIATION(mFaceList);
+ VECTOR_SET_ASSOCIATION(mVertexList);
+ VECTOR_SET_ASSOCIATION(mVolumeList);
+
+ mVertexList.reserve(100);
+ mFaceList.reserve(200);
+ mEdgeList.reserve(100);
+ mVolumeList.reserve(20);
+ sideCount = 0;
+}
+
+
+//----------------------------------------------------------------------------
+// Box should be axis aligned in the transform space provided.
+
+void Polytope::buildBox(const MatrixF& transform,const Box3F& box)
+{
+ // Box is assumed to be axis aligned in the source space.
+ // Transform into geometry space
+ Point3F xvec,yvec,zvec,min;
+ transform.getColumn(0,&xvec);
+ xvec *= box.len_x();
+ transform.getColumn(1,&yvec);
+ yvec *= box.len_y();
+ transform.getColumn(2,&zvec);
+ zvec *= box.len_z();
+ transform.mulP(box.min,&min);
+
+ // Initial vertices
+ mVertexList.setSize(8);
+ mVertexList[0].point = min;
+ mVertexList[1].point = min + yvec;
+ mVertexList[2].point = min + xvec + yvec;
+ mVertexList[3].point = min + xvec;
+ mVertexList[4].point = mVertexList[0].point + zvec;
+ mVertexList[5].point = mVertexList[1].point + zvec;
+ mVertexList[6].point = mVertexList[2].point + zvec;
+ mVertexList[7].point = mVertexList[3].point + zvec;
+ S32 i;
+ for (i = 0; i < 8; i++)
+ mVertexList[i].side = 0;
+
+ // Initial faces
+ mFaceList.setSize(6);
+ for (S32 f = 0; f < 6; f++) {
+ Face& face = mFaceList[f];
+ face.original = true;
+ face.vertex = 0;
+ }
+
+ mFaceList[0].plane.set(mVertexList[0].point,xvec);
+ mFaceList[0].plane.invert();
+ mFaceList[1].plane.set(mVertexList[2].point,yvec);
+ mFaceList[2].plane.set(mVertexList[2].point,xvec);
+ mFaceList[3].plane.set(mVertexList[0].point,yvec);
+ mFaceList[3].plane.invert();
+ mFaceList[4].plane.set(mVertexList[0].point,zvec);
+ mFaceList[4].plane.invert();
+ mFaceList[5].plane.set(mVertexList[4].point,zvec);
+
+ // Initial edges
+ mEdgeList.setSize(12);
+ Edge* edge = mEdgeList.begin();
+ S32 nextEdge = 0;
+ for (i = 0; i < 4; i++) {
+ S32 n = (i == 3)? 0: i + 1;
+ S32 p = (i == 0)? 3: i - 1;
+ edge->vertex[0] = i;
+ edge->vertex[1] = n;
+ edge->face[0] = i;
+ edge->face[1] = 4;
+ edge->next = ++nextEdge;
+ edge++;
+ edge->vertex[0] = 4 + i;
+ edge->vertex[1] = 4 + n;
+ edge->face[0] = i;
+ edge->face[1] = 5;
+ edge->next = ++nextEdge;
+ edge++;
+ edge->vertex[0] = i;
+ edge->vertex[1] = 4 + i;
+ edge->face[0] = i;
+ edge->face[1] = p;
+ edge->next = ++nextEdge;
+ edge++;
+ }
+ edge[-1].next = -1;
+
+ // Volume
+ mVolumeList.setSize(1);
+ Volume& volume = mVolumeList.last();
+ volume.edgeList = 0;
+ volume.material = -1;
+ volume.object = 0;
+ sideCount = 0;
+}
+
+
+//----------------------------------------------------------------------------
+
+void Polytope::intersect(SimObject* object,const BSPNode* root)
+{
+ AssertFatal(mVolumeList.size() > 0,"Polytope::intersect: Missing initial volume");
+
+ // Always clips the first volume against the BSP
+ VolumeStack stack;
+ stack.reserve(50);
+ stack.increment();
+ stack.last().edgeList = mVolumeList[0].edgeList;
+ stack.last().node = root;
+
+ while (!stack.empty()) {
+ StackElement volume = stack.last();
+ stack.pop_back();
+
+ // If it's a solid node, export the volume
+ const BSPNode* node = volume.node;
+ if (!node->backNode && !node->frontNode) {
+ mVolumeList.increment();
+ Volume& vol = mVolumeList.last();
+ vol.object = object;
+ vol.material = node->material;
+ vol.edgeList = volume.edgeList;
+ continue;
+ }
+
+ // New front and back faces
+ mFaceList.increment(2);
+ Face& frontFace = mFaceList.last();
+ Face& backFace = *(&frontFace - 1);
+
+ backFace.original = frontFace.original = false;
+ backFace.vertex = frontFace.vertex = 0;
+ backFace.plane = frontFace.plane = node->plane;
+ backFace.plane.invert();
+
+ // New front and back volumes
+ StackElement frontVolume,backVolume;
+ frontVolume.edgeList = backVolume.edgeList = -1;
+
+ const PlaneF& plane = node->plane;
+ S32 startVertex = mVertexList.size();
+
+ // Test & clip all the edges
+ S32 sideBase = ++sideCount << 1;
+ for (S32 i = volume.edgeList; i >= 0; i = mEdgeList[i].next) {
+
+ // Copy into tmp first to avoid problems with the array.
+ Edge edge = mEdgeList[i];
+
+ Vertex& v0 = mVertexList[edge.vertex[0]];
+ if (v0.side < sideBase)
+ v0.side = sideBase + ((plane.distToPlane(v0.point) >= 0)? 0: 1);
+ Vertex& v1 = mVertexList[edge.vertex[1]];
+ if (v1.side < sideBase)
+ v1.side = sideBase + ((plane.distToPlane(v1.point) >= 0)? 0: 1);
+
+ if (v0.side != v1.side) {
+ S32 s = v0.side - sideBase;
+ intersect(plane,v0.point,v1.point);
+
+ // Split the edge into each volume
+ mEdgeList.increment(2);
+ Edge& e0 = mEdgeList.last();
+ e0.next = frontVolume.edgeList;
+ frontVolume.edgeList = mEdgeList.size() - 1;
+
+ Edge& e1 = *(&e0 - 1);
+ e1.next = backVolume.edgeList;
+ backVolume.edgeList = frontVolume.edgeList - 1;
+
+ e0.vertex[0] = edge.vertex[s];
+ e1.vertex[0] = edge.vertex[s ^ 1];
+ e0.vertex[1] = e1.vertex[1] = mVertexList.size() - 1;
+ e0.face[0] = e1.face[0] = edge.face[0];
+ e0.face[1] = e1.face[1] = edge.face[1];
+
+ // Add new edges on the plane, one to each volume
+ for (S32 f = 0; f < 2; f++) {
+ Face& face = mFaceList[edge.face[f]];
+ if (face.vertex < startVertex)
+ face.vertex = mVertexList.size() - 1;
+ else {
+ mEdgeList.increment(2);
+ Edge& e0 = mEdgeList.last();
+ e0.next = frontVolume.edgeList;
+ frontVolume.edgeList = mEdgeList.size() - 1;
+
+ Edge& e1 = *(&e0 - 1);
+ e1.next = backVolume.edgeList;
+ backVolume.edgeList = frontVolume.edgeList - 1;
+
+ e1.vertex[0] = e0.vertex[0] = face.vertex;
+ e1.vertex[1] = e0.vertex[1] = mVertexList.size() - 1;
+ e1.face[0] = e0.face[0] = edge.face[f];
+ e1.face[1] = mFaceList.size() - 1;
+ e0.face[1] = e1.face[1] - 1;
+ }
+ }
+ }
+ else
+ if (v0.side == sideBase) {
+ mEdgeList.push_back(edge);
+ Edge& ne = mEdgeList.last();
+ ne.next = frontVolume.edgeList;
+ frontVolume.edgeList = mEdgeList.size() - 1;
+ }
+ else {
+ mEdgeList.push_back(edge);
+ Edge& ne = mEdgeList.last();
+ ne.next = backVolume.edgeList;
+ backVolume.edgeList = mEdgeList.size() - 1;
+ }
+ }
+
+ // Push the front and back nodes
+ if (node->frontNode && frontVolume.edgeList >= 0) {
+ frontVolume.node = node->frontNode;
+ stack.push_back(frontVolume);
+ }
+ if (node->backNode && backVolume.edgeList >= 0) {
+ backVolume.node = node->backNode;
+ stack.push_back(backVolume);
+ }
+ }
+}
+
+
+//----------------------------------------------------------------------------
+
+bool Polytope::intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep)
+{
+ // If den == 0 then the line and plane are parallel.
+ F32 den;
+ Point3F dt = ep - sp;
+ if ((den = plane.x * dt.x + plane.y * dt.y + plane.z * dt.z) == 0)
+ return false;
+
+ mVertexList.increment();
+ Vertex& v = mVertexList.last();
+ F32 s = -(plane.x * sp.x + plane.y * sp.y + plane.z * sp.z + plane.d) / den;
+ v.point.x = sp.x + dt.x * s;
+ v.point.y = sp.y + dt.y * s;
+ v.point.z = sp.z + dt.z * s;
+ v.side = 0;
+ return true;
+}
+
+
+//----------------------------------------------------------------------------
+
+void Polytope::render()
+{
+ glVertexPointer(3,GL_FLOAT,sizeof(Vertex),mVertexList.address());
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glColor3f(0.5, 0.5, 0.5);
+
+ for (VolumeList::iterator itr = mVolumeList.begin();
+ itr < mVolumeList.end(); itr++) {
+ for (S32 e = itr->edgeList; e >= 0; e = mEdgeList[e].next)
+ glDrawElements(GL_LINES,2,GL_UNSIGNED_INT,&mEdgeList[e].vertex);
+ glColor3f(1, 1, 1);
+ }
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
+
+
+//----------------------------------------------------------------------------
+
+void Polytope::extrudeFace(int faceIdx,const VectorF& vec,Polytope* out)
+{
+ // Assumes the face belongs to the first volume.
+ out->mVertexList.clear();
+ out->mFaceList.clear();
+ out->mEdgeList.clear();
+ out->mVolumeList.clear();
+ sideCount++;
+
+ // Front & end faces
+ Face nface;
+ nface.original = true;
+ nface.vertex = 0;
+ nface.plane = mFaceList[faceIdx].plane;
+ out->mFaceList.setSize(2);
+ out->mFaceList[0] = out->mFaceList[1] = nface;
+ out->mFaceList[0].plane.invert();
+
+ for (S32 e = mVolumeList[0].edgeList; e >= 0; e = mEdgeList[e].next) {
+ Edge& edge = mEdgeList[e];
+ if (edge.face[0] == faceIdx || edge.face[1] == faceIdx) {
+
+ // Build face for this edge
+ // Should think about calulating the plane
+ S32 fi = out->mFaceList.size();
+ out->mFaceList.push_back(nface);
+
+ // Reserve 4 entries to make sure the ve[] pointers
+ // into the list don't get invalidated.
+ out->mEdgeList.reserve(out->mEdgeList.size() + 4);
+ Edge* ve[2];
+
+ // Build edges for each vertex
+ for (S32 v = 0; v < 2; v++) {
+ if (mVertexList[edge.vertex[v]].side < sideCount) {
+ mVertexList[edge.vertex[v]].side = sideCount + out->mEdgeList.size();
+
+ out->mVertexList.increment(2);
+ out->mVertexList.end()[-1] =
+ out->mVertexList.end()[-2] =
+ mVertexList[edge.vertex[v]];
+ out->mVertexList.last().point += vec;
+
+ out->mEdgeList.increment();
+ Edge& ne = out->mEdgeList.last();
+ ne.next = out->mEdgeList.size();
+ ne.vertex[1] = out->mVertexList.size() - 1;
+ ne.vertex[0] = ne.vertex[1] - 1;
+ ne.face[0] = ne.face[1] = -1;
+ ve[v] = ≠
+ }
+ else {
+ S32 ei = mVertexList[edge.vertex[v]].side - sideCount;
+ ve[v] = &out->mEdgeList[ei];
+ }
+
+ // Edge should share this face
+ if (ve[v]->face[0] == -1)
+ ve[v]->face[0] = fi;
+ else
+ ve[v]->face[1] = fi;
+ }
+
+ // Build parallel edges
+ out->mEdgeList.increment(2);
+ for (S32 i = 0; i < 2; i++ ) {
+ Edge& ne = out->mEdgeList.end()[i - 2];
+ ne.next = out->mEdgeList.size() - 1 + i;
+ ne.vertex[0] = ve[0]->vertex[i];
+ ne.vertex[1] = ve[1]->vertex[i];
+ ne.face[0] = i;
+ ne.face[1] = fi;
+ }
+ }
+ }
+
+ out->mEdgeList.last().next = -1;
+ out->mVolumeList.increment();
+ Volume& nv = out->mVolumeList.last();
+ nv.edgeList = 0;
+ nv.object = 0;
+ nv.material = -1;
+}
+
+
+//----------------------------------------------------------------------------
+
+bool Polytope::findCollision(const VectorF& vec,Polytope::Collision *best)
+{
+ if (mVolumeList.size() <= 1)
+ return false;
+ if (!best->object)
+ best->distance = 1.0E30;
+ int bestVertex = -1;
+ Volume* bestVolume;
+ sideCount++;
+
+ // Find the closest point
+ for (Volume* vol = mVolumeList.begin() + 1;
+ vol < mVolumeList.end(); vol++) {
+ for (S32 e = vol->edgeList; e >= 0; e = mEdgeList[e].next) {
+ Edge& edge = mEdgeList[e];
+ if (mFaceList[edge.face[0]].original &&
+ mFaceList[edge.face[1]].original)
+ continue;
+ for (S32 v = 0; v < 2; v++) {
+ S32 vi = edge.vertex[v];
+ Vertex& vr = mVertexList[vi];
+ if (vr.side != sideCount) {
+ vr.side = sideCount;
+ F32 dist = mDot(vr.point,vec);
+ if (dist < best->distance) {
+ best->distance = dist;
+ bestVertex = vi;
+ bestVolume = vol;
+ }
+ }
+ }
+ }
+ }
+
+ if (bestVertex == -1)
+ return false;
+
+ // Fill in the return value
+ best->point = mVertexList[bestVertex].point;
+ best->object = bestVolume->object;
+ best->material = bestVolume->material;
+
+ // Pick the best face
+ F32 bestFaceDot = 1;
+ for (S32 e = bestVolume->edgeList; e >= 0; e = mEdgeList[e].next) {
+ Edge& edge = mEdgeList[e];
+ if (edge.vertex[0] == bestVertex || edge.vertex[1] == bestVertex) {
+ for (S32 f = 0; f < 2; f++) {
+ Face& tf = mFaceList[edge.face[f]];
+ F32 fd = mDot(tf.plane,vec);
+ if (fd < bestFaceDot) {
+ bestFaceDot = fd;
+ best->plane = tf.plane;
+ }
+ }
+ }
+ }
+ return true;
+}
+
diff --git a/engine/collision/polytope.h b/engine/collision/polytope.h
new file mode 100755
index 0000000..25b73ad
--- /dev/null
+++ b/engine/collision/polytope.h
@@ -0,0 +1,96 @@
+//-----------------------------------------------------------------------------
+// Torque Game Engine
+// Copyright (C) GarageGames.com, Inc.
+//-----------------------------------------------------------------------------
+
+#ifndef _POLYTOPE_H_
+#define _POLYTOPE_H_
+
+#ifndef _TVECTOR_H_
+#include "core/tVector.h"
+#endif
+
+
+//----------------------------------------------------------------------------
+
+class SimObject;
+
+
+//----------------------------------------------------------------------------
+
+class Polytope
+{
+ // Convex Polyhedron
+public:
+ struct Vertex {
+ Point3F point;
+ /// Temp BSP clip info
+ S32 side;
+ };
+ struct Edge {
+ S32 vertex[2];
+ S32 face[2];
+ S32 next;
+ };
+ struct Face {
+ PlaneF plane;
+ S32 original;
+ /// Temp BSP clip info
+ S32 vertex;
+ };
+ struct Volume
+ {
+ S32 edgeList;
+ S32 material;
+ SimObject* object;
+ };
+ struct StackElement
+ {
+ S32 edgeList;
+ const BSPNode *node;
+ };
+ struct Collision {
+ SimObject* object;
+ S32 material;
+ PlaneF plane;
+ Point3F point;
+ F32 distance;
+
+ Collision()
+ {
+ object = NULL;
+ distance = 0.0;
+ }
+ };
+
+ typedef Vector EdgeList;
+ typedef Vector FaceList;
+ typedef Vector VertexList;
+ typedef Vector VolumeList;
+ typedef Vector VolumeStack;
+
+ //
+ S32 sideCount;
+ EdgeList mEdgeList;
+ FaceList mFaceList;
+ VertexList mVertexList;
+ VolumeList mVolumeList;
+
+private:
+ bool intersect(const PlaneF& plane,const Point3F& sp,const Point3F& ep);
+
+public:
+ //
+ Polytope();
+ void buildBox(const MatrixF& transform,const Box3F& box);
+ void intersect(SimObject*, const BSPNode* node);
+ inline bool didIntersect() { return mVolumeList.size() > 1; }
+ void extrudeFace(int fi,const VectorF& vec,Polytope* out);
+ bool findCollision(const VectorF& vec,Polytope::Collision *best);
+ void render();
+};
+
+
+
+
+#endif
diff --git a/engine/console/BASgram.cc b/engine/console/BASgram.cc
new file mode 100755
index 0000000..21b1462
--- /dev/null
+++ b/engine/console/BASgram.cc
@@ -0,0 +1,1951 @@
+
+/* A Bison parser, made from basgram.y with Bison version GNU Bison version 1.24
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define yyparse BASparse
+#define yylex BASlex
+#define yyerror BASerror
+#define yylval BASlval
+#define yychar BASchar
+#define yydebug BASdebug
+#define yynerrs BASnerrs
+#define rwDEFINE 258
+#define rwENDDEF 259
+#define rwDECLARE 260
+#define rwBREAK 261
+#define rwELSE 262
+#define rwCONTINUE 263
+#define rwGLOBAL 264
+#define rwIF 265
+#define rwNIL 266
+#define rwRETURN 267
+#define rwWHILE 268
+#define rwENDIF 269
+#define rwENDWHILE 270
+#define rwENDFOR 271
+#define rwDEFAULT 272
+#define rwFOR 273
+#define rwDATABLOCK 274
+#define rwSWITCH 275
+#define rwCASE 276
+#define rwSWITCHSTR 277
+#define rwCASEOR 278
+#define rwPACKAGE 279
+#define ILLEGAL_TOKEN 280
+#define CHRCONST 281
+#define INTCONST 282
+#define TTAG 283
+#define VAR 284
+#define IDENT 285
+#define STRATOM 286
+#define TAGATOM 287
+#define FLTCONST 288
+#define opMINUSMINUS 289
+#define opPLUSPLUS 290
+#define STMT_SEP 291
+#define opSHL 292
+#define opSHR 293
+#define opPLASN 294
+#define opMIASN 295
+#define opMLASN 296
+#define opDVASN 297
+#define opMODASN 298
+#define opANDASN 299
+#define opXORASN 300
+#define opORASN 301
+#define opSLASN 302
+#define opSRASN 303
+#define opCAT 304
+#define opEQ 305
+#define opNE 306
+#define opGE 307
+#define opLE 308
+#define opAND 309
+#define opOR 310
+#define opSTREQ 311
+#define opCOLONCOLON 312
+#define opMDASN 313
+#define opNDASN 314
+#define opNTASN 315
+#define opSTRNE 316
+#define UNARY 317
+#define rwTHEN 318
+#define rwEND 319
+#define rwBEGIN 320
+#define rwCFOR 321
+#define rwTO 322
+#define rwSTEP 323
+
+#line 1 "basgram.y"
+
+// Make sure we don't get gram.h twice.
+#define _BASGRAM_H_
+#define _CMDGRAM_H_
+
+#include
+#include
+#include "console/console.h"
+#include "console/compiler.h"
+#include "console/consoleInternal.h"
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+#define YYSSIZE 350
+
+int outtext(char *fmt, ...);
+extern int serrors;
+
+#define nil 0
+#undef YY_ARGS
+#define YY_ARGS(x) x
+
+int BASlex();
+void BASerror(char *a, ...);
+
+#define alloca dMalloc
+
+#line 31 "basgram.y"
+
+ /* Reserved Word Definitions */
+
+/* IMPORTANT: SEE COMMENTS BELOW IF YOU ARE THINKING ABOUT MODIFYING TOKENS */
+
+/*
+ ALL TOKENS BETWEEN HERE AND ADDITIONAL TOKENS BELOW MUST BE IDENTICAL TO THOSE
+ IN CS_CMD.y OTHERWISE BAD STUFF HAPPENS
+*/
+#line 48 "basgram.y"
+
+ /* Constants and Identifier Definitions */
+#line 60 "basgram.y"
+
+ /* Operator Definitions */
+
+#line 72 "basgram.y"
+typedef union {
+ char c;
+ int i;
+ const char *s;
+ char *str;
+ double f;
+ StmtNode *stmt;
+ ExprNode *expr;
+ SlotAssignNode *slist;
+ VarNode *var;
+ SlotDecl slot;
+ ObjectBlockDecl odcl;
+ ObjectDeclNode *od;
+ AssignDecl asn;
+ IfStmtNode *ifnode;
+} YYSTYPE;
+#line 143 "basgram.y"
+
+/*
+ Additional tokens
+
+NOTE: These MUST be here, otherwise the definitions of the usual TorqueScript tokens changes
+which will break the compiler. Double check the standard TS tokens arent being redefined before
+testing a build.
+*/
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 278
+#define YYFLAG -32768
+#define YYNTBASE 94
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 323 ? yytranslate[x] : 130)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 54, 2, 2, 2, 44, 43, 2, 45,
+ 46, 36, 34, 47, 35, 41, 37, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 48, 49, 38,
+ 40, 39, 84, 55, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 80, 2, 93, 52, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 50, 42, 51, 53, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 56, 57,
+ 58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 81, 82, 83, 85, 86, 87, 88, 89,
+ 90, 91, 92
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 2, 3, 6, 8, 10, 12, 17, 19, 22,
+ 23, 26, 28, 30, 32, 34, 36, 39, 42, 45,
+ 49, 52, 57, 64, 72, 82, 83, 85, 87, 91,
+ 100, 110, 119, 120, 123, 124, 126, 127, 130, 131,
+ 133, 135, 138, 141, 145, 149, 151, 156, 161, 166,
+ 174, 180, 182, 186, 192, 200, 206, 216, 218, 220,
+ 224, 228, 232, 236, 240, 244, 248, 252, 256, 259,
+ 262, 264, 270, 274, 278, 282, 286, 290, 294, 298,
+ 302, 306, 310, 314, 318, 322, 325, 328, 330, 332,
+ 334, 336, 338, 340, 342, 344, 349, 353, 360, 362,
+ 366, 368, 370, 373, 376, 379, 382, 385, 388, 391,
+ 394, 397, 400, 402, 404, 408, 415, 418, 424, 427,
+ 431, 437, 442, 449, 456, 457, 459, 461, 465, 467,
+ 470, 475, 480, 488, 490, 494, 495
+};
+
+static const short yyrhs[] = { 95,
+ 0, 0, 95, 96, 0, 100, 0, 101, 0, 97,
+ 0, 24, 30, 98, 88, 0, 101, 0, 98, 101,
+ 0, 0, 99, 100, 0, 114, 0, 115, 0, 116,
+ 0, 104, 0, 111, 0, 6, 129, 0, 8, 129,
+ 0, 12, 129, 0, 12, 118, 129, 0, 117, 129,
+ 0, 28, 40, 118, 129, 0, 28, 40, 118, 47,
+ 118, 129, 0, 3, 30, 45, 102, 46, 99, 88,
+ 0, 3, 30, 79, 30, 45, 102, 46, 99, 88,
+ 0, 0, 103, 0, 29, 0, 103, 47, 29, 0,
+ 19, 30, 45, 30, 106, 46, 126, 88, 0, 5,
+ 120, 45, 107, 106, 108, 46, 109, 88, 0, 5,
+ 120, 45, 107, 106, 108, 46, 88, 0, 0, 48,
+ 30, 0, 0, 118, 0, 0, 47, 125, 0, 0,
+ 126, 0, 110, 0, 126, 110, 0, 105, 129, 0,
+ 110, 105, 129, 0, 89, 99, 88, 0, 100, 0,
+ 20, 118, 112, 88, 0, 22, 118, 112, 88, 0,
+ 21, 113, 48, 99, 0, 21, 113, 48, 99, 17,
+ 48, 99, 0, 21, 113, 48, 99, 112, 0, 118,
+ 0, 113, 23, 118, 0, 10, 118, 87, 99, 88,
+ 0, 10, 118, 87, 99, 7, 99, 88, 0, 13,
+ 118, 89, 99, 88, 0, 18, 118, 47, 118, 47,
+ 118, 89, 99, 88, 0, 122, 0, 122, 0, 45,
+ 118, 46, 0, 118, 52, 118, 0, 118, 44, 118,
+ 0, 118, 43, 118, 0, 118, 42, 118, 0, 118,
+ 34, 118, 0, 118, 35, 118, 0, 118, 36, 118,
+ 0, 118, 37, 118, 0, 35, 118, 0, 36, 118,
+ 0, 28, 0, 118, 84, 118, 48, 118, 0, 118,
+ 38, 118, 0, 118, 39, 118, 0, 118, 74, 118,
+ 0, 118, 75, 118, 0, 118, 72, 118, 0, 118,
+ 73, 118, 0, 118, 77, 118, 0, 118, 59, 118,
+ 0, 118, 60, 118, 0, 118, 76, 118, 0, 118,
+ 78, 118, 0, 118, 85, 118, 0, 118, 55, 118,
+ 0, 54, 118, 0, 53, 118, 0, 32, 0, 33,
+ 0, 27, 0, 6, 0, 119, 0, 30, 0, 31,
+ 0, 29, 0, 29, 80, 128, 93, 0, 118, 41,
+ 30, 0, 118, 41, 30, 80, 128, 93, 0, 30,
+ 0, 45, 118, 46, 0, 57, 0, 56, 0, 61,
+ 118, 0, 62, 118, 0, 63, 118, 0, 64, 118,
+ 0, 65, 118, 0, 66, 118, 0, 67, 118, 0,
+ 68, 118, 0, 69, 118, 0, 70, 118, 0, 123,
+ 0, 105, 0, 29, 40, 118, 0, 29, 80, 128,
+ 93, 40, 118, 0, 29, 121, 0, 29, 80, 128,
+ 93, 121, 0, 119, 121, 0, 119, 40, 118, 0,
+ 119, 40, 50, 125, 51, 0, 30, 45, 124, 46,
+ 0, 30, 79, 30, 45, 124, 46, 0, 118, 41,
+ 30, 45, 124, 46, 0, 0, 125, 0, 118, 0,
+ 125, 47, 118, 0, 127, 0, 126, 127, 0, 30,
+ 40, 118, 129, 0, 19, 40, 118, 129, 0, 30,
+ 80, 128, 93, 40, 118, 129, 0, 118, 0, 128,
+ 47, 118, 0, 0, 129, 49, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 157, 162, 164, 169, 171, 173, 178, 183, 185, 190,
+ 192, 197, 198, 199, 200, 201, 202, 204, 206, 208,
+ 210, 212, 214, 219, 221, 226, 228, 233, 235, 240,
+ 245, 247, 252, 254, 259, 261, 266, 268, 273, 275,
+ 277, 279, 284, 286, 291, 293, 298, 300, 305, 307,
+ 309, 314, 316, 321, 323, 328, 333, 338, 343, 345,
+ 347, 349, 351, 353, 355, 357, 359, 361, 363, 365,
+ 367, 369, 371, 373, 375, 377, 379, 381, 383, 385,
+ 387, 389, 391, 393, 395, 397, 399, 401, 403, 405,
+ 407, 409, 411, 413, 415, 417, 422, 424, 429, 431,
+ 436, 438, 440, 442, 444, 446, 448, 450, 452, 454,
+ 456, 458, 463, 465, 467, 469, 471, 473, 475, 477,
+ 479, 484, 486, 488, 493, 495, 500, 502, 507, 509,
+ 514, 516, 518, 523, 525, 529, 530
+};
+
+static const char * const yytname[] = { "$","error","$undefined.","rwDEFINE",
+"rwENDDEF","rwDECLARE","rwBREAK","rwELSE","rwCONTINUE","rwGLOBAL","rwIF","rwNIL",
+"rwRETURN","rwWHILE","rwENDIF","rwENDWHILE","rwENDFOR","rwDEFAULT","rwFOR","rwDATABLOCK",
+"rwSWITCH","rwCASE","rwSWITCHSTR","rwCASEOR","rwPACKAGE","ILLEGAL_TOKEN","CHRCONST",
+"INTCONST","TTAG","VAR","IDENT","STRATOM","TAGATOM","FLTCONST","'+'","'-'","'*'",
+"'/'","'<'","'>'","'='","'.'","'|'","'&'","'%'","'('","')'","','","':'","';'",
+"'{'","'}'","'^'","'~'","'!'","'@'","opMINUSMINUS","opPLUSPLUS","STMT_SEP","opSHL",
+"opSHR","opPLASN","opMIASN","opMLASN","opDVASN","opMODASN","opANDASN","opXORASN",
+"opORASN","opSLASN","opSRASN","opCAT","opEQ","opNE","opGE","opLE","opAND","opOR",
+"opSTREQ","opCOLONCOLON","'['","opMDASN","opNDASN","opNTASN","'?'","opSTRNE",
+"UNARY","rwTHEN","rwEND","rwBEGIN","rwCFOR","rwTO","rwSTEP","']'","start","decl_list",
+"decl","package_decl","fn_decl_list","statement_list","stmt","fn_decl_stmt",
+"var_list_decl","var_list","datablock_decl","object_decl","parent_block","object_name",
+"object_args","object_declare_block","object_decl_list","switch_stmt","case_block",
+"case_expr","if_stmt","while_stmt","for_stmt","expression_stmt","expr","slot_acc",
+"class_name_expr","assign_op_struct","stmt_expr","funcall_expr","expr_list_decl",
+"expr_list","slot_assign_list","slot_assign","aidx_expr","semicolon","semicolon"
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 94, 95, 95, 96, 96, 96, 97, 98, 98, 99,
+ 99, 100, 100, 100, 100, 100, 100, 100, 100, 100,
+ 100, 100, 100, 101, 101, 102, 102, 103, 103, 104,
+ 105, 105, 106, 106, 107, 107, 108, 108, 109, 109,
+ 109, 109, 110, 110, -1, -1, 111, 111, 112, 112,
+ 112, 113, 113, 114, 114, 115, 116, 117, 118, 118,
+ 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
+ 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
+ 118, 118, 118, 118, 118, 118, 118, 118, 118, 118,
+ 118, 118, 118, 118, 118, 118, 119, 119, 120, 120,
+ 121, 121, 121, 121, 121, 121, 121, 121, 121, 121,
+ 121, 121, 122, 122, 122, 122, 122, 122, 122, 122,
+ 122, 123, 123, 123, 124, 124, 125, 125, 126, 126,
+ 127, 127, 127, 128, 128, 129, 129
+};
+
+static const short yyr2[] = { 0,
+ 1, 0, 2, 1, 1, 1, 4, 1, 2, 0,
+ 2, 1, 1, 1, 1, 1, 2, 2, 2, 3,
+ 2, 4, 6, 7, 9, 0, 1, 1, 3, 8,
+ 9, 8, 0, 2, 0, 1, 0, 2, 0, 1,
+ 1, 2, 2, 3, 3, 1, 4, 4, 4, 7,
+ 5, 1, 3, 5, 7, 5, 9, 1, 1, 3,
+ 3, 3, 3, 3, 3, 3, 3, 3, 2, 2,
+ 1, 5, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 3, 3, 2, 2, 1, 1, 1,
+ 1, 1, 1, 1, 1, 4, 3, 6, 1, 3,
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 1, 1, 3, 6, 2, 5, 2, 3,
+ 5, 4, 6, 6, 0, 1, 1, 3, 1, 2,
+ 4, 4, 7, 1, 3, 0, 2
+};
+
+static const short yydefact[] = { 2,
+ 1, 0, 0, 136, 136, 0, 136, 0, 0, 0,
+ 0, 0, 0, 90, 71, 95, 93, 94, 88, 89,
+ 0, 0, 0, 0, 0, 3, 6, 4, 5, 15,
+ 114, 16, 12, 13, 14, 136, 0, 92, 58, 113,
+ 0, 99, 0, 0, 17, 18, 91, 71, 0, 59,
+ 136, 19, 0, 0, 0, 0, 0, 0, 0, 0,
+ 102, 101, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 117, 125, 0, 69, 70, 0, 87,
+ 86, 21, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 119, 26, 0, 0,
+ 35, 137, 10, 20, 10, 0, 0, 0, 0, 0,
+ 0, 8, 136, 115, 103, 104, 105, 106, 107, 108,
+ 109, 110, 111, 112, 134, 0, 127, 0, 126, 0,
+ 60, 65, 66, 67, 68, 73, 74, 97, 64, 63,
+ 62, 61, 85, 80, 81, 77, 78, 75, 76, 82,
+ 79, 83, 0, 84, 0, 120, 28, 0, 27, 0,
+ 100, 33, 36, 0, 0, 0, 33, 0, 52, 47,
+ 48, 7, 9, 0, 22, 0, 96, 122, 0, 125,
+ 125, 0, 0, 0, 10, 0, 26, 0, 37, 10,
+ 54, 11, 56, 0, 0, 0, 10, 136, 135, 0,
+ 118, 128, 0, 0, 0, 72, 121, 0, 29, 0,
+ 34, 0, 0, 0, 0, 0, 53, 49, 23, 116,
+ 123, 124, 98, 24, 10, 38, 0, 55, 10, 0,
+ 0, 0, 129, 0, 51, 0, 32, 136, 0, 41,
+ 40, 0, 0, 0, 0, 30, 130, 10, 25, 43,
+ 31, 136, 42, 57, 136, 136, 0, 50, 44, 132,
+ 131, 0, 0, 136, 133, 0, 0, 0
+};
+
+static const short yydefgoto[] = { 276,
+ 1, 26, 27, 121, 174, 202, 29, 168, 169, 30,
+ 31, 199, 172, 223, 249, 250, 32, 119, 178, 33,
+ 34, 35, 36, 37, 38, 44, 74, 50, 40, 138,
+ 139, 242, 243, 136, 45
+};
+
+static const short yypact[] = {-32768,
+ 681, 3, 2, 973,-32768, 1341, 1341, 1341, 1341, 6,
+ 1341, 1341, 10,-32768, 37, 66, -3,-32768,-32768,-32768,
+ 1341, 1341, 1341, 1341, 1341,-32768,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768, 1000, 1342, 1054,-32768,
+ 0,-32768, 1341, 33, 31, 31,-32768,-32768, 598,-32768,
+ 1000, 31, 515, 686, 63, 488, 488, 110, 1341, 1341,
+-32768,-32768, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
+ 1341, 1341, 1341,-32768, 1341, 85, 78, 78, 738, 78,
+ 78, 31, 1341, 1341, 1341, 1341, 1341, 1341, 90, 1341,
+ 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341, 1341,
+ 1341, 1341, 1341, 1341, 1341, 1330,-32768, 92, 94, 790,
+ 1341,-32768,-32768, 31,-32768, 1341, 109, 1341, 49, 52,
+ 23,-32768, 842, 1000, 1000, 1000, 1000, 1000, 1000, 1000,
+ 1000, 1000, 1000, 1000, 1000, -39, 1000, 101, 103, 108,
+-32768, 133, 133, 78, 78, 177, 177, -28, 121, 1162,
+ 78, 1135, 107, 189, 189, 1189, 1189, 177, 177, 1108,
+ 1081, 107, 894, 107, 1341, 1000,-32768, 115, 124, 130,
+-32768, 106, 1000, 269, 307, 946, 106, 5, 1000,-32768,
+-32768,-32768,-32768, 1341, 31, 1341, 1357,-32768, 1341, 1341,
+ 1341, 1341, 1341, -12,-32768, 149, 92, 153, 132,-32768,
+-32768,-32768,-32768, 1341, 139, 1341,-32768, 1000, 1000, 1341,
+-32768, 1000, 140, 146, -34, 1027,-32768, 357,-32768, 151,
+-32768, 1341, 152, 395, 542, 18, 1000, 1260, 31, 1000,
+-32768,-32768,-32768,-32768,-32768, 103, 19,-32768,-32768, 165,
+ -24, -1,-32768, 159,-32768, 445,-32768,-32768, 122, 204,
+ 25, 483, 1341, 1341, 1341,-32768,-32768,-32768,-32768, 31,
+-32768,-32768, 204,-32768, 1000, 1000, -33, 1298, 31, 31,
+ 31, 175, 1341, 1000, 31, 216, 217,-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,-32768,-32768,-32768, -90, 218, -47, 30,-32768,-32768,
+ -125, 45,-32768,-32768,-32768, -23,-32768, -56,-32768,-32768,
+-32768,-32768,-32768, -2,-32768,-32768, -35, 26,-32768, -140,
+ -150, -8, -208, -180, -5
+};
+
+
+#define YYLAST 1427
+
+
+static const short yytable[] = { 46,
+ 120, 52, 107, 49, 51, 53, 54, 186, 56, 57,
+ 122, 215, 186, 186, 194, 254, 191, 240, 77, 78,
+ 79, 80, 81, 3, 175, 2, 39, 206, 241, 3,
+ 82, 42, 41, 257, 189, 55, 240, 240, 217, 58,
+ 110, 75, 257, 240, 108, 114, 43, 241, 241, 213,
+ 214, 192, 207, 187, 241, 255, 123, 124, 233, 272,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 134,
+ 135, 236, 137, 183, 267, 76, 59, 111, 109, 112,
+ 142, 143, 144, 145, 146, 147, 256, 149, 150, 151,
+ 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
+ 162, 163, 164, 166, 218, 60, 247, 117, 173, 224,
+ 182, 248, 2, 176, 140, 179, 228, 185, 89, 148,
+ 167, 61, 62, 170, 262, 248, 63, 64, 65, 66,
+ 67, 68, 69, 70, 71, 72, 180, 262, 177, 181,
+ 83, 84, 85, 86, 246, 73, 188, 89, 252, 189,
+ 92, 211, 190, 198, 83, 84, 85, 86, 87, 88,
+ 195, 89, 137, 91, 92, 95, 96, 268, 85, 86,
+ 196, 245, 93, 89, 197, 94, 92, 219, 222, 95,
+ 96, 208, 221, 209, 226, 231, 212, 137, 137, 135,
+ 216, 232, 97, 98, 99, 100, 235, 237, 103, 39,
+ 39, 225, 229, 227, 253, 105, 258, 230, 3, 261,
+ 83, 84, 85, 86, 273, 277, 278, 89, 28, 137,
+ 92, 205, 83, 84, 85, 86, 220, 263, 251, 89,
+ 0, 94, 92, 0, 0, 95, 96, 0, 0, 0,
+ 0, 0, 260, 39, 0, 0, 0, 0, 0, 39,
+ 265, 266, 135, 39, 103, 0, 269, 0, 0, 270,
+ 271, 105, 0, 0, 0, 0, 0, 0, 275, 0,
+ 274, 39, 0, 3, 4, 200, 5, 39, 6, 0,
+ 7, 8, 0, 0, 0, 0, 9, 10, 11, 0,
+ 12, 0, 0, 39, 0, 14, 15, 16, 17, 18,
+ 19, 20, 0, 21, 22, 0, 0, 0, 0, 0,
+ 0, 3, 4, 23, 5, 0, 6, 0, 7, 8,
+ 0, 24, 25, 0, 9, 10, 11, 0, 12, 0,
+ 0, 0, 0, 14, 15, 16, 17, 18, 19, 20,
+ 0, 21, 22, 0, 0, 0, 0, 0, 0, 0,
+ 0, 23, 0, 0, 0, 0, 201, 0, 0, 24,
+ 25, 3, 4, 0, 5, 0, 6, 0, 7, 8,
+ 0, 0, 0, 0, 9, 10, 11, 0, 12, 0,
+ 0, 0, 0, 14, 15, 16, 17, 18, 19, 20,
+ 0, 21, 22, 0, 203, 0, 0, 0, 0, 3,
+ 4, 23, 5, 0, 6, 0, 7, 8, 0, 24,
+ 25, 0, 9, 10, 11, 0, 12, 0, 0, 0,
+ 0, 14, 15, 16, 17, 18, 19, 20, 0, 21,
+ 22, 0, 0, 0, 0, 0, 0, 0, 0, 23,
+ 0, 0, 0, 0, 234, 0, 0, 24, 25, 3,
+ 4, 0, 5, 0, 6, 0, 7, 8, 0, 0,
+ 0, 0, 9, 10, 11, 0, 12, 0, 0, 0,
+ 0, 14, 15, 16, 17, 18, 19, 20, 0, 21,
+ 22, 0, 238, 0, 0, 0, 0, 3, 4, 23,
+ 5, 0, 6, 0, 7, 8, 0, 24, 25, 0,
+ 9, 10, 11, 0, 12, 0, 0, 0, 118, 14,
+ 15, 16, 17, 18, 19, 20, 0, 21, 22, 0,
+ 0, 83, 84, 85, 86, 87, 88, 23, 89, 90,
+ 91, 92, 259, 0, 0, 24, 25, 0, 0, 93,
+ 0, 0, 94, 0, 0, 0, 95, 96, 83, 84,
+ 85, 86, 87, 88, 0, 89, 90, 91, 92, 97,
+ 98, 99, 100, 101, 102, 103, 93, 0, 0, 94,
+ 264, 104, 105, 95, 96, 83, 84, 85, 86, 87,
+ 88, 0, 89, 90, 91, 92, 97, 98, 99, 100,
+ 101, 102, 103, 93, 0, 0, 94, 0, 104, 105,
+ 95, 96, 0, 115, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 97, 98, 99, 100, 101, 102, 103,
+ 0, 0, 0, 0, 0, 104, 105, 0, 0, 0,
+ 239, 83, 84, 85, 86, 87, 88, 0, 89, 90,
+ 91, 92, 0, 0, 0, 0, 0, 0, 0, 93,
+ 0, 0, 94, 0, 0, 0, 95, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,
+ 98, 99, 100, 101, 102, 103, 0, 0, 0, 0,
+ 0, 104, 105, 2, 113, 3, 4, 0, 5, 0,
+ 6, 0, 7, 8, 0, 0, 0, 0, 9, 10,
+ 11, 0, 12, 0, 13, 0, 0, 14, 15, 16,
+ 17, 18, 19, 20, 0, 21, 22, 0, 0, 83,
+ 84, 85, 86, 87, 88, 23, 89, 90, 91, 92,
+ 0, 0, 116, 24, 25, 0, 0, 93, 0, 0,
+ 94, 0, 0, 0, 95, 96, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 97, 98, 99,
+ 100, 101, 102, 103, 0, 0, 0, 0, 0, 104,
+ 105, 83, 84, 85, 86, 87, 88, 0, 89, 90,
+ 91, 92, 0, 141, 0, 0, 0, 0, 0, 93,
+ 0, 0, 94, 0, 0, 0, 95, 96, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 97,
+ 98, 99, 100, 101, 102, 103, 0, 0, 0, 0,
+ 0, 104, 105, 83, 84, 85, 86, 87, 88, 0,
+ 89, 90, 91, 92, 0, 171, 0, 0, 0, 0,
+ 0, 93, 0, 0, 94, 0, 0, 0, 95, 96,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 97, 98, 99, 100, 101, 102, 103, 0, 0,
+ 0, 0, 0, 104, 105, 83, 84, 85, 86, 87,
+ 88, 0, 89, 90, 91, 92, 0, 0, 184, 0,
+ 0, 0, 0, 93, 0, 0, 94, 0, 0, 0,
+ 95, 96, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 97, 98, 99, 100, 101, 102, 103,
+ 0, 0, 0, 0, 0, 104, 105, 83, 84, 85,
+ 86, 87, 88, 0, 89, 90, 91, 92, 0, 0,
+ 0, 193, 0, 0, 0, 93, 0, 0, 94, 0,
+ 0, 0, 95, 96, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 97, 98, 99, 100, 101,
+ 102, 103, 0, 0, 0, 0, 0, 104, 105, 83,
+ 84, 85, 86, 87, 88, 0, 89, 90, 91, 92,
+ 0, 0, 204, 0, 0, 0, 0, 93, 0, 0,
+ 94, 0, 0, 0, 95, 96, -91, -91, -91, -91,
+ -91, -91, 0, -91, -91, -91, -91, 97, 98, 99,
+ 100, 101, 102, 103, -91, 0, 0, -91, 0, 104,
+ 105, -91, -91, 83, 84, 85, 86, 87, 88, 0,
+ 89, 90, 91, 92, -91, -91, -91, -91, -91, -91,
+ -91, 93, 0, 0, 94, 0, -91, -91, 95, 96,
+ 83, 84, 85, 86, 87, 88, 0, 89, 90, 91,
+ 92, 97, 98, 99, 100, 101, 102, 103, 93, 0,
+ 0, 94, 0, 104, 105, 95, 96, -59, 0, 0,
+ -59, -59, -59, 0, -59, -59, -59, -59, 97, 98,
+ 99, 100, 101, 102, 103, -59, 0, 0, -59, 0,
+ 0, 105, -59, -59, 83, 84, 85, 86, 87, 88,
+ 0, 89, 90, 91, 92, -59, -59, -59, -59, -59,
+ -59, -59, 93, 0, 0, 94, 0, -59, -59, 95,
+ 96, 83, 84, 85, 86, 87, 88, 0, 89, 90,
+ 91, 92, 97, 98, 99, 100, 101, 0, 103, 93,
+ 0, 0, 94, 0, 0, 105, 95, 96, 83, 84,
+ 85, 86, 87, 88, 0, 89, 0, 91, 92, 97,
+ 98, 99, 100, 0, 0, 103, 0, 0, 0, 94,
+ 0, 0, 105, 95, 96, 83, 84, 85, 86, 87,
+ 88, 0, 89, 0, 0, 92, 97, 98, 99, 100,
+ 0, 0, 103, 0, 0, 0, 94, 0, 0, 105,
+ 95, 96, 83, 84, 85, 86, 87, 88, 0, 89,
+ 0, 0, 92, 97, 98, 99, 100, 0, 0, 103,
+ 0, 0, 0, 94, 0, 0, 105, 95, 96, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 99, 100, 3, 4, 103, 5, 0, 6,
+ 0, 7, 8, 105, 0, 0, 244, 9, 10, 11,
+ 118, 12, 0, 0, 0, 0, 14, 15, 16, 17,
+ 18, 19, 20, 0, 21, 22, 0, 0, 0, 0,
+ 0, 0, 3, 4, 23, 5, 0, 6, 0, 7,
+ 8, 0, 24, 25, 0, 9, 10, 11, 0, 12,
+ 0, 0, 0, 0, 14, 15, 16, 17, 18, 19,
+ 20, 0, 21, 22, 3, 47, 0, 0, 0, 0,
+ 0, 0, 23, 0, 0, 3, 47, 0, 0, 0,
+ 24, 25, 0, 0, 0, 0, 14, 48, 16, 17,
+ 18, 19, 20, 0, 21, 22, 0, 14, 48, 16,
+ 17, 18, 19, 20, 23, 21, 22, 0, 0, 165,
+ 0, 106, 24, 25, 0, 23, 0, 0, 0, 0,
+ 0, 0, 0, 24, 25, 0, 210, 61, 62, 0,
+ 0, 0, 63, 64, 65, 66, 67, 68, 69, 70,
+ 71, 72, 61, 62, 0, 0, 0, 63, 64, 65,
+ 66, 67, 68, 69, 70, 71, 72
+};
+
+static const short yycheck[] = { 5,
+ 57, 7, 38, 6, 7, 8, 9, 47, 11, 12,
+ 58, 192, 47, 47, 165, 40, 45, 19, 21, 22,
+ 23, 24, 25, 5, 115, 3, 1, 23, 30, 5,
+ 36, 30, 30, 242, 47, 30, 19, 19, 51, 30,
+ 43, 45, 251, 19, 45, 51, 45, 30, 30, 190,
+ 191, 80, 48, 93, 30, 80, 59, 60, 93, 93,
+ 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 222, 75, 121, 255, 79, 40, 45, 79, 49,
+ 83, 84, 85, 86, 87, 88, 88, 90, 91, 92,
+ 93, 94, 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 195, 40, 88, 45, 111, 200,
+ 88, 237, 3, 116, 30, 118, 207, 123, 41, 30,
+ 29, 56, 57, 30, 250, 251, 61, 62, 63, 64,
+ 65, 66, 67, 68, 69, 70, 88, 263, 30, 88,
+ 34, 35, 36, 37, 235, 80, 46, 41, 239, 47,
+ 44, 187, 45, 48, 34, 35, 36, 37, 38, 39,
+ 46, 41, 165, 43, 44, 59, 60, 258, 36, 37,
+ 47, 228, 52, 41, 45, 55, 44, 29, 47, 59,
+ 60, 184, 30, 186, 46, 46, 189, 190, 191, 192,
+ 193, 46, 72, 73, 74, 75, 46, 46, 78, 174,
+ 175, 204, 208, 206, 40, 85, 48, 210, 5, 88,
+ 34, 35, 36, 37, 40, 0, 0, 41, 1, 222,
+ 44, 177, 34, 35, 36, 37, 197, 251, 237, 41,
+ -1, 55, 44, -1, -1, 59, 60, -1, -1, -1,
+ -1, -1, 248, 218, -1, -1, -1, -1, -1, 224,
+ 253, 254, 255, 228, 78, -1, 262, -1, -1, 265,
+ 266, 85, -1, -1, -1, -1, -1, -1, 274, -1,
+ 273, 246, -1, 5, 6, 7, 8, 252, 10, -1,
+ 12, 13, -1, -1, -1, -1, 18, 19, 20, -1,
+ 22, -1, -1, 268, -1, 27, 28, 29, 30, 31,
+ 32, 33, -1, 35, 36, -1, -1, -1, -1, -1,
+ -1, 5, 6, 45, 8, -1, 10, -1, 12, 13,
+ -1, 53, 54, -1, 18, 19, 20, -1, 22, -1,
+ -1, -1, -1, 27, 28, 29, 30, 31, 32, 33,
+ -1, 35, 36, -1, -1, -1, -1, -1, -1, -1,
+ -1, 45, -1, -1, -1, -1, 88, -1, -1, 53,
+ 54, 5, 6, -1, 8, -1, 10, -1, 12, 13,
+ -1, -1, -1, -1, 18, 19, 20, -1, 22, -1,
+ -1, -1, -1, 27, 28, 29, 30, 31, 32, 33,
+ -1, 35, 36, -1, 88, -1, -1, -1, -1, 5,
+ 6, 45, 8, -1, 10, -1, 12, 13, -1, 53,
+ 54, -1, 18, 19, 20, -1, 22, -1, -1, -1,
+ -1, 27, 28, 29, 30, 31, 32, 33, -1, 35,
+ 36, -1, -1, -1, -1, -1, -1, -1, -1, 45,
+ -1, -1, -1, -1, 88, -1, -1, 53, 54, 5,
+ 6, -1, 8, -1, 10, -1, 12, 13, -1, -1,
+ -1, -1, 18, 19, 20, -1, 22, -1, -1, -1,
+ -1, 27, 28, 29, 30, 31, 32, 33, -1, 35,
+ 36, -1, 88, -1, -1, -1, -1, 5, 6, 45,
+ 8, -1, 10, -1, 12, 13, -1, 53, 54, -1,
+ 18, 19, 20, -1, 22, -1, -1, -1, 21, 27,
+ 28, 29, 30, 31, 32, 33, -1, 35, 36, -1,
+ -1, 34, 35, 36, 37, 38, 39, 45, 41, 42,
+ 43, 44, 88, -1, -1, 53, 54, -1, -1, 52,
+ -1, -1, 55, -1, -1, -1, 59, 60, 34, 35,
+ 36, 37, 38, 39, -1, 41, 42, 43, 44, 72,
+ 73, 74, 75, 76, 77, 78, 52, -1, -1, 55,
+ 88, 84, 85, 59, 60, 34, 35, 36, 37, 38,
+ 39, -1, 41, 42, 43, 44, 72, 73, 74, 75,
+ 76, 77, 78, 52, -1, -1, 55, -1, 84, 85,
+ 59, 60, -1, 89, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 72, 73, 74, 75, 76, 77, 78,
+ -1, -1, -1, -1, -1, 84, 85, -1, -1, -1,
+ 89, 34, 35, 36, 37, 38, 39, -1, 41, 42,
+ 43, 44, -1, -1, -1, -1, -1, -1, -1, 52,
+ -1, -1, 55, -1, -1, -1, 59, 60, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 72,
+ 73, 74, 75, 76, 77, 78, -1, -1, -1, -1,
+ -1, 84, 85, 3, 87, 5, 6, -1, 8, -1,
+ 10, -1, 12, 13, -1, -1, -1, -1, 18, 19,
+ 20, -1, 22, -1, 24, -1, -1, 27, 28, 29,
+ 30, 31, 32, 33, -1, 35, 36, -1, -1, 34,
+ 35, 36, 37, 38, 39, 45, 41, 42, 43, 44,
+ -1, -1, 47, 53, 54, -1, -1, 52, -1, -1,
+ 55, -1, -1, -1, 59, 60, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 72, 73, 74,
+ 75, 76, 77, 78, -1, -1, -1, -1, -1, 84,
+ 85, 34, 35, 36, 37, 38, 39, -1, 41, 42,
+ 43, 44, -1, 46, -1, -1, -1, -1, -1, 52,
+ -1, -1, 55, -1, -1, -1, 59, 60, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 72,
+ 73, 74, 75, 76, 77, 78, -1, -1, -1, -1,
+ -1, 84, 85, 34, 35, 36, 37, 38, 39, -1,
+ 41, 42, 43, 44, -1, 46, -1, -1, -1, -1,
+ -1, 52, -1, -1, 55, -1, -1, -1, 59, 60,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 72, 73, 74, 75, 76, 77, 78, -1, -1,
+ -1, -1, -1, 84, 85, 34, 35, 36, 37, 38,
+ 39, -1, 41, 42, 43, 44, -1, -1, 47, -1,
+ -1, -1, -1, 52, -1, -1, 55, -1, -1, -1,
+ 59, 60, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 72, 73, 74, 75, 76, 77, 78,
+ -1, -1, -1, -1, -1, 84, 85, 34, 35, 36,
+ 37, 38, 39, -1, 41, 42, 43, 44, -1, -1,
+ -1, 48, -1, -1, -1, 52, -1, -1, 55, -1,
+ -1, -1, 59, 60, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 72, 73, 74, 75, 76,
+ 77, 78, -1, -1, -1, -1, -1, 84, 85, 34,
+ 35, 36, 37, 38, 39, -1, 41, 42, 43, 44,
+ -1, -1, 47, -1, -1, -1, -1, 52, -1, -1,
+ 55, -1, -1, -1, 59, 60, 34, 35, 36, 37,
+ 38, 39, -1, 41, 42, 43, 44, 72, 73, 74,
+ 75, 76, 77, 78, 52, -1, -1, 55, -1, 84,
+ 85, 59, 60, 34, 35, 36, 37, 38, 39, -1,
+ 41, 42, 43, 44, 72, 73, 74, 75, 76, 77,
+ 78, 52, -1, -1, 55, -1, 84, 85, 59, 60,
+ 34, 35, 36, 37, 38, 39, -1, 41, 42, 43,
+ 44, 72, 73, 74, 75, 76, 77, 78, 52, -1,
+ -1, 55, -1, 84, 85, 59, 60, 34, -1, -1,
+ 37, 38, 39, -1, 41, 42, 43, 44, 72, 73,
+ 74, 75, 76, 77, 78, 52, -1, -1, 55, -1,
+ -1, 85, 59, 60, 34, 35, 36, 37, 38, 39,
+ -1, 41, 42, 43, 44, 72, 73, 74, 75, 76,
+ 77, 78, 52, -1, -1, 55, -1, 84, 85, 59,
+ 60, 34, 35, 36, 37, 38, 39, -1, 41, 42,
+ 43, 44, 72, 73, 74, 75, 76, -1, 78, 52,
+ -1, -1, 55, -1, -1, 85, 59, 60, 34, 35,
+ 36, 37, 38, 39, -1, 41, -1, 43, 44, 72,
+ 73, 74, 75, -1, -1, 78, -1, -1, -1, 55,
+ -1, -1, 85, 59, 60, 34, 35, 36, 37, 38,
+ 39, -1, 41, -1, -1, 44, 72, 73, 74, 75,
+ -1, -1, 78, -1, -1, -1, 55, -1, -1, 85,
+ 59, 60, 34, 35, 36, 37, 38, 39, -1, 41,
+ -1, -1, 44, 72, 73, 74, 75, -1, -1, 78,
+ -1, -1, -1, 55, -1, -1, 85, 59, 60, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, 74, 75, 5, 6, 78, 8, -1, 10,
+ -1, 12, 13, 85, -1, -1, 17, 18, 19, 20,
+ 21, 22, -1, -1, -1, -1, 27, 28, 29, 30,
+ 31, 32, 33, -1, 35, 36, -1, -1, -1, -1,
+ -1, -1, 5, 6, 45, 8, -1, 10, -1, 12,
+ 13, -1, 53, 54, -1, 18, 19, 20, -1, 22,
+ -1, -1, -1, -1, 27, 28, 29, 30, 31, 32,
+ 33, -1, 35, 36, 5, 6, -1, -1, -1, -1,
+ -1, -1, 45, -1, -1, 5, 6, -1, -1, -1,
+ 53, 54, -1, -1, -1, -1, 27, 28, 29, 30,
+ 31, 32, 33, -1, 35, 36, -1, 27, 28, 29,
+ 30, 31, 32, 33, 45, 35, 36, -1, -1, 50,
+ -1, 40, 53, 54, -1, 45, -1, -1, -1, -1,
+ -1, -1, -1, 53, 54, -1, 40, 56, 57, -1,
+ -1, -1, 61, 62, 63, 64, 65, 66, 67, 68,
+ 69, 70, 56, 57, -1, -1, -1, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+ 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, 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. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 192 "bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 1:
+#line 158 "basgram.y"
+{ ;
+ break;}
+case 2:
+#line 163 "basgram.y"
+{ yyval.stmt = nil; ;
+ break;}
+case 3:
+#line 165 "basgram.y"
+{ if(!statementList) { statementList = yyvsp[0].stmt; } else { statementList->append(yyvsp[0].stmt); } ;
+ break;}
+case 4:
+#line 170 "basgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 5:
+#line 172 "basgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 6:
+#line 174 "basgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 7:
+#line 179 "basgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; for(StmtNode *walk = (yyvsp[-1].stmt);walk;walk = walk->getNext() ) walk->setPackage(yyvsp[-2].s); ;
+ break;}
+case 8:
+#line 184 "basgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 9:
+#line 186 "basgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; (yyvsp[-1].stmt)->append(yyvsp[0].stmt); ;
+ break;}
+case 10:
+#line 191 "basgram.y"
+{ yyval.stmt = nil; ;
+ break;}
+case 11:
+#line 193 "basgram.y"
+{ if(!yyvsp[-1].stmt) { yyval.stmt = yyvsp[0].stmt; } else { (yyvsp[-1].stmt)->append(yyvsp[0].stmt); yyval.stmt = yyvsp[-1].stmt; } ;
+ break;}
+case 17:
+#line 203 "basgram.y"
+{ yyval.stmt = BreakStmtNode::alloc(); ;
+ break;}
+case 18:
+#line 205 "basgram.y"
+{ yyval.stmt = ContinueStmtNode::alloc(); ;
+ break;}
+case 19:
+#line 207 "basgram.y"
+{ yyval.stmt = ReturnStmtNode::alloc(NULL); ;
+ break;}
+case 20:
+#line 209 "basgram.y"
+{ yyval.stmt = ReturnStmtNode::alloc(yyvsp[-1].expr); ;
+ break;}
+case 21:
+#line 211 "basgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; ;
+ break;}
+case 22:
+#line 213 "basgram.y"
+{ yyval.stmt = TTagSetStmtNode::alloc(yyvsp[-3].s, yyvsp[-1].expr, NULL); ;
+ break;}
+case 23:
+#line 215 "basgram.y"
+{ yyval.stmt = TTagSetStmtNode::alloc(yyvsp[-5].s, yyvsp[-3].expr, yyvsp[-1].expr); ;
+ break;}
+case 24:
+#line 220 "basgram.y"
+{ yyval.stmt = FunctionDeclStmtNode::alloc(yyvsp[-5].s, NULL, yyvsp[-3].var, yyvsp[-1].stmt); ;
+ break;}
+case 25:
+#line 222 "basgram.y"
+{ yyval.stmt = FunctionDeclStmtNode::alloc(yyvsp[-5].s, yyvsp[-7].s, yyvsp[-3].var, yyvsp[-1].stmt); ;
+ break;}
+case 26:
+#line 227 "basgram.y"
+{ yyval.var = NULL; ;
+ break;}
+case 27:
+#line 229 "basgram.y"
+{ yyval.var = yyvsp[0].var; ;
+ break;}
+case 28:
+#line 234 "basgram.y"
+{ yyval.var = VarNode::alloc(yyvsp[0].s, NULL); ;
+ break;}
+case 29:
+#line 236 "basgram.y"
+{ yyval.var = yyvsp[-2].var; ((StmtNode*)(yyvsp[-2].var))->append((StmtNode*)VarNode::alloc(yyvsp[0].s, NULL)); ;
+ break;}
+case 30:
+#line 241 "basgram.y"
+{ yyval.stmt = ObjectDeclNode::alloc(ConstantNode::alloc(yyvsp[-6].s), ConstantNode::alloc(yyvsp[-4].s), NULL, yyvsp[-3].s, yyvsp[-1].slist, NULL, true); ;
+ break;}
+case 31:
+#line 246 "basgram.y"
+{ yyval.od = ObjectDeclNode::alloc(yyvsp[-7].expr, yyvsp[-5].expr, yyvsp[-3].expr, yyvsp[-4].s, yyvsp[-1].odcl.slots, yyvsp[-1].odcl.decls, false); ;
+ break;}
+case 32:
+#line 248 "basgram.y"
+{ yyval.od = ObjectDeclNode::alloc(yyvsp[-6].expr, yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[-3].s, NULL, NULL, false); ;
+ break;}
+case 33:
+#line 253 "basgram.y"
+{ yyval.s = NULL; ;
+ break;}
+case 34:
+#line 255 "basgram.y"
+{ yyval.s = yyvsp[0].s; ;
+ break;}
+case 35:
+#line 260 "basgram.y"
+{ yyval.expr = StrConstNode::alloc("", false); ;
+ break;}
+case 36:
+#line 262 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 37:
+#line 267 "basgram.y"
+{ yyval.expr = NULL; ;
+ break;}
+case 38:
+#line 269 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 39:
+#line 274 "basgram.y"
+{ yyval.odcl.slots = NULL; yyval.odcl.decls = NULL; ;
+ break;}
+case 40:
+#line 276 "basgram.y"
+{ yyval.odcl.slots = yyvsp[0].slist; yyval.odcl.decls = NULL; ;
+ break;}
+case 41:
+#line 278 "basgram.y"
+{ yyval.odcl.slots = NULL; yyval.odcl.decls = yyvsp[0].od; ;
+ break;}
+case 42:
+#line 280 "basgram.y"
+{ yyval.odcl.slots = yyvsp[-1].slist; yyval.odcl.decls = yyvsp[0].od; ;
+ break;}
+case 43:
+#line 285 "basgram.y"
+{ yyval.od = yyvsp[-1].od; ;
+ break;}
+case 44:
+#line 287 "basgram.y"
+{ yyvsp[-2].od->append(yyvsp[-1].od); yyval.od = yyvsp[-2].od; ;
+ break;}
+case 45:
+#line 292 "basgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; ;
+ break;}
+case 46:
+#line 294 "basgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 47:
+#line 299 "basgram.y"
+{ yyval.stmt = yyvsp[-1].ifnode; yyvsp[-1].ifnode->propagateSwitchExpr(yyvsp[-2].expr, false); ;
+ break;}
+case 48:
+#line 301 "basgram.y"
+{ yyval.stmt = yyvsp[-1].ifnode; yyvsp[-1].ifnode->propagateSwitchExpr(yyvsp[-2].expr, true); ;
+ break;}
+case 49:
+#line 306 "basgram.y"
+{ yyval.ifnode = IfStmtNode::alloc(yyvsp[-3].i, yyvsp[-2].expr, yyvsp[0].stmt, NULL, false); ;
+ break;}
+case 50:
+#line 308 "basgram.y"
+{ yyval.ifnode = IfStmtNode::alloc(yyvsp[-6].i, yyvsp[-5].expr, yyvsp[-3].stmt, yyvsp[0].stmt, false); ;
+ break;}
+case 51:
+#line 310 "basgram.y"
+{ yyval.ifnode = IfStmtNode::alloc(yyvsp[-4].i, yyvsp[-3].expr, yyvsp[-1].stmt, yyvsp[0].ifnode, true); ;
+ break;}
+case 52:
+#line 315 "basgram.y"
+{ yyval.expr = yyvsp[0].expr;;
+ break;}
+case 53:
+#line 317 "basgram.y"
+{ (yyvsp[-2].expr)->append(yyvsp[0].expr); yyval.expr=yyvsp[-2].expr; ;
+ break;}
+case 54:
+#line 322 "basgram.y"
+{ yyval.stmt = IfStmtNode::alloc(yyvsp[-4].i, yyvsp[-3].expr, yyvsp[-1].stmt, NULL, false); ;
+ break;}
+case 55:
+#line 324 "basgram.y"
+{ yyval.stmt = IfStmtNode::alloc(yyvsp[-6].i, yyvsp[-5].expr, yyvsp[-3].stmt, yyvsp[-1].stmt, false); ;
+ break;}
+case 56:
+#line 329 "basgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-4].i, nil, yyvsp[-3].expr, nil, yyvsp[-1].stmt, false); ;
+ break;}
+case 57:
+#line 334 "basgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-8].i, yyvsp[-7].expr, yyvsp[-5].expr, yyvsp[-3].expr, yyvsp[-1].stmt, false); ;
+ break;}
+case 58:
+#line 339 "basgram.y"
+{ yyval.stmt = yyvsp[0].expr; ;
+ break;}
+case 59:
+#line 344 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 60:
+#line 346 "basgram.y"
+{ yyval.expr = yyvsp[-1].expr; ;
+ break;}
+case 61:
+#line 348 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 62:
+#line 350 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 63:
+#line 352 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 64:
+#line 354 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 65:
+#line 356 "basgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 66:
+#line 358 "basgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 67:
+#line 360 "basgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 68:
+#line 362 "basgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 69:
+#line 364 "basgram.y"
+{ yyval.expr = FloatUnaryExprNode::alloc(yyvsp[-1].i, yyvsp[0].expr); ;
+ break;}
+case 70:
+#line 366 "basgram.y"
+{ yyval.expr = TTagDerefNode::alloc(yyvsp[0].expr); ;
+ break;}
+case 71:
+#line 368 "basgram.y"
+{ yyval.expr = TTagExprNode::alloc(yyvsp[0].s); ;
+ break;}
+case 72:
+#line 370 "basgram.y"
+{ yyval.expr = ConditionalExprNode::alloc(yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 73:
+#line 372 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 74:
+#line 374 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 75:
+#line 376 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 76:
+#line 378 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 77:
+#line 380 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 78:
+#line 382 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 79:
+#line 384 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 80:
+#line 386 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 81:
+#line 388 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 82:
+#line 390 "basgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 83:
+#line 392 "basgram.y"
+{ yyval.expr = StreqExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr, true); ;
+ break;}
+case 84:
+#line 394 "basgram.y"
+{ yyval.expr = StreqExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr, false); ;
+ break;}
+case 85:
+#line 396 "basgram.y"
+{ yyval.expr = StrcatExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr, yyvsp[-1].i); ;
+ break;}
+case 86:
+#line 398 "basgram.y"
+{ yyval.expr = IntUnaryExprNode::alloc(yyvsp[-1].i, yyvsp[0].expr); ;
+ break;}
+case 87:
+#line 400 "basgram.y"
+{ yyval.expr = IntUnaryExprNode::alloc(yyvsp[-1].i, yyvsp[0].expr); ;
+ break;}
+case 88:
+#line 402 "basgram.y"
+{ yyval.expr = StrConstNode::alloc(yyvsp[0].str, true); ;
+ break;}
+case 89:
+#line 404 "basgram.y"
+{ yyval.expr = FloatNode::alloc(yyvsp[0].f); ;
+ break;}
+case 90:
+#line 406 "basgram.y"
+{ yyval.expr = IntNode::alloc(yyvsp[0].i); ;
+ break;}
+case 91:
+#line 408 "basgram.y"
+{ yyval.expr = ConstantNode::alloc(StringTable->insert("break")); ;
+ break;}
+case 92:
+#line 410 "basgram.y"
+{ yyval.expr = SlotAccessNode::alloc(yyvsp[0].slot.object, yyvsp[0].slot.array, yyvsp[0].slot.slotName); ;
+ break;}
+case 93:
+#line 412 "basgram.y"
+{ yyval.expr = ConstantNode::alloc(yyvsp[0].s); ;
+ break;}
+case 94:
+#line 414 "basgram.y"
+{ yyval.expr = StrConstNode::alloc(yyvsp[0].str, false); ;
+ break;}
+case 95:
+#line 416 "basgram.y"
+{ yyval.expr = (ExprNode*)VarNode::alloc(yyvsp[0].s, NULL); ;
+ break;}
+case 96:
+#line 418 "basgram.y"
+{ yyval.expr = (ExprNode*)VarNode::alloc(yyvsp[-3].s, yyvsp[-1].expr); ;
+ break;}
+case 97:
+#line 423 "basgram.y"
+{ yyval.slot.object = yyvsp[-2].expr; yyval.slot.slotName = yyvsp[0].s; yyval.slot.array = NULL; ;
+ break;}
+case 98:
+#line 425 "basgram.y"
+{ yyval.slot.object = yyvsp[-5].expr; yyval.slot.slotName = yyvsp[-3].s; yyval.slot.array = yyvsp[-1].expr; ;
+ break;}
+case 99:
+#line 430 "basgram.y"
+{ yyval.expr = ConstantNode::alloc(yyvsp[0].s); ;
+ break;}
+case 100:
+#line 432 "basgram.y"
+{ yyval.expr = yyvsp[-1].expr; ;
+ break;}
+case 101:
+#line 437 "basgram.y"
+{ yyval.asn.token = '+'; yyval.asn.expr = FloatNode::alloc(1); ;
+ break;}
+case 102:
+#line 439 "basgram.y"
+{ yyval.asn.token = '-'; yyval.asn.expr = FloatNode::alloc(1); ;
+ break;}
+case 103:
+#line 441 "basgram.y"
+{ yyval.asn.token = '+'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 104:
+#line 443 "basgram.y"
+{ yyval.asn.token = '-'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 105:
+#line 445 "basgram.y"
+{ yyval.asn.token = '*'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 106:
+#line 447 "basgram.y"
+{ yyval.asn.token = '/'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 107:
+#line 449 "basgram.y"
+{ yyval.asn.token = '%'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 108:
+#line 451 "basgram.y"
+{ yyval.asn.token = '&'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 109:
+#line 453 "basgram.y"
+{ yyval.asn.token = '^'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 110:
+#line 455 "basgram.y"
+{ yyval.asn.token = '|'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 111:
+#line 457 "basgram.y"
+{ yyval.asn.token = opSHL; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 112:
+#line 459 "basgram.y"
+{ yyval.asn.token = opSHR; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 113:
+#line 464 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 114:
+#line 466 "basgram.y"
+{ yyval.expr = yyvsp[0].od; ;
+ break;}
+case 115:
+#line 468 "basgram.y"
+{ yyval.expr = AssignExprNode::alloc(yyvsp[-2].s, NULL, yyvsp[0].expr); ;
+ break;}
+case 116:
+#line 470 "basgram.y"
+{ yyval.expr = AssignExprNode::alloc(yyvsp[-5].s, yyvsp[-3].expr, yyvsp[0].expr); ;
+ break;}
+case 117:
+#line 472 "basgram.y"
+{ yyval.expr = AssignOpExprNode::alloc(yyvsp[-1].s, NULL, yyvsp[0].asn.expr, yyvsp[0].asn.token); ;
+ break;}
+case 118:
+#line 474 "basgram.y"
+{ yyval.expr = AssignOpExprNode::alloc(yyvsp[-4].s, yyvsp[-2].expr, yyvsp[0].asn.expr, yyvsp[0].asn.token); ;
+ break;}
+case 119:
+#line 476 "basgram.y"
+{ yyval.expr = SlotAssignOpNode::alloc(yyvsp[-1].slot.object, yyvsp[-1].slot.slotName, yyvsp[-1].slot.array, yyvsp[0].asn.token, yyvsp[0].asn.expr); ;
+ break;}
+case 120:
+#line 478 "basgram.y"
+{ yyval.expr = SlotAssignNode::alloc(yyvsp[-2].slot.object, yyvsp[-2].slot.array, yyvsp[-2].slot.slotName, yyvsp[0].expr); ;
+ break;}
+case 121:
+#line 480 "basgram.y"
+{ yyval.expr = SlotAssignNode::alloc(yyvsp[-4].slot.object, yyvsp[-4].slot.array, yyvsp[-4].slot.slotName, yyvsp[-1].expr); ;
+ break;}
+case 122:
+#line 485 "basgram.y"
+{ yyval.expr = FuncCallExprNode::alloc(yyvsp[-3].s, NULL, yyvsp[-1].expr, false); ;
+ break;}
+case 123:
+#line 487 "basgram.y"
+{ yyval.expr = FuncCallExprNode::alloc(yyvsp[-3].s, yyvsp[-5].s, yyvsp[-1].expr, false); ;
+ break;}
+case 124:
+#line 489 "basgram.y"
+{ yyvsp[-5].expr->append(yyvsp[-1].expr); yyval.expr = FuncCallExprNode::alloc(yyvsp[-3].s, NULL, yyvsp[-5].expr, true); ;
+ break;}
+case 125:
+#line 494 "basgram.y"
+{ yyval.expr = NULL; ;
+ break;}
+case 126:
+#line 496 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 127:
+#line 501 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 128:
+#line 503 "basgram.y"
+{ (yyvsp[-2].expr)->append(yyvsp[0].expr); yyval.expr = yyvsp[-2].expr; ;
+ break;}
+case 129:
+#line 508 "basgram.y"
+{ yyval.slist = yyvsp[0].slist; ;
+ break;}
+case 130:
+#line 510 "basgram.y"
+{ yyvsp[-1].slist->append(yyvsp[0].slist); yyval.slist = yyvsp[-1].slist; ;
+ break;}
+case 131:
+#line 515 "basgram.y"
+{ yyval.slist = SlotAssignNode::alloc(NULL, NULL, yyvsp[-3].s, yyvsp[-1].expr); ;
+ break;}
+case 132:
+#line 517 "basgram.y"
+{ yyval.slist = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), yyvsp[-1].expr); ;
+ break;}
+case 133:
+#line 519 "basgram.y"
+{ yyval.slist = SlotAssignNode::alloc(NULL, yyvsp[-4].expr, yyvsp[-6].s, yyvsp[-1].expr); ;
+ break;}
+case 134:
+#line 524 "basgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 135:
+#line 526 "basgram.y"
+{ yyval.expr = CommaCatExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 487 "bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 532 "basgram.y"
+
+
diff --git a/engine/console/BASgram.y b/engine/console/BASgram.y
new file mode 100755
index 0000000..140ded9
--- /dev/null
+++ b/engine/console/BASgram.y
@@ -0,0 +1,533 @@
+%{
+// Make sure we don't get gram.h twice.
+#define _BASGRAM_H_
+#define _CMDGRAM_H_
+
+#include
+#include
+#include "console/console.h"
+#include "console/compiler.h"
+#include "console/consoleInternal.h"
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+#define YYSSIZE 350
+
+int outtext(char *fmt, ...);
+extern int serrors;
+
+#define nil 0
+#undef YY_ARGS
+#define YY_ARGS(x) x
+
+int BASlex();
+void BASerror(char *a, ...);
+
+#define alloca dMalloc
+
+%}
+%{
+ /* Reserved Word Definitions */
+
+/* IMPORTANT: SEE COMMENTS BELOW IF YOU ARE THINKING ABOUT MODIFYING TOKENS */
+
+/*
+ ALL TOKENS BETWEEN HERE AND ADDITIONAL TOKENS BELOW MUST BE IDENTICAL TO THOSE
+ IN CS_CMD.y OTHERWISE BAD STUFF HAPPENS
+*/
+%}
+%token rwDEFINE rwENDDEF rwDECLARE
+%token rwBREAK rwELSE rwCONTINUE rwGLOBAL
+%token rwIF rwNIL rwRETURN rwWHILE
+%token rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
+%token rwFOR rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
+%token rwCASEOR rwPACKAGE
+%token ILLEGAL_TOKEN
+%{
+ /* Constants and Identifier Definitions */
+%}
+%token CHRCONST
+%token INTCONST
+%token TTAG
+%token VAR
+%token IDENT
+%token STRATOM
+%token TAGATOM
+%token FLTCONST
+
+%{
+ /* Operator Definitions */
+%}
+%token '+' '-' '*' '/' '<' '>' '=' '.' '|' '&' '%'
+%token '(' ')' ',' ':' ';' '{' '}' '^' '~' '!' '@'
+%token opMINUSMINUS opPLUSPLUS
+%token STMT_SEP
+%token opSHL opSHR opPLASN opMIASN opMLASN opDVASN opMODASN opANDASN
+%token opXORASN opORASN opSLASN opSRASN opCAT
+%token opEQ opNE opGE opLE opAND opOR opSTREQ
+%token opCOLONCOLON
+
+%union {
+ char c;
+ int i;
+ const char *s;
+ char *str;
+ double f;
+ StmtNode *stmt;
+ ExprNode *expr;
+ SlotAssignNode *slist;
+ VarNode *var;
+ SlotDecl slot;
+ ObjectBlockDecl odcl;
+ ObjectDeclNode *od;
+ AssignDecl asn;
+ IfStmtNode *ifnode;
+}
+
+%type parent_block
+%type case_block
+%type switch_stmt
+%type decl
+%type decl_list
+%type package_decl
+%type fn_decl_stmt
+%type fn_decl_list
+%type statement_list
+%type stmt
+%type expr_list
+%type expr_list_decl
+%type aidx_expr
+%type funcall_expr
+%type object_name
+%type object_args
+%type stmt_expr
+%type case_expr
+%type class_name_expr
+%type if_stmt
+%type while_stmt
+%type for_stmt
+%type stmt_block
+%type datablock_decl
+%type object_decl
+%type object_decl_list
+%type object_declare_block
+%type expr
+%type slot_assign_list
+%type slot_assign
+%type slot_acc
+%type expression_stmt
+%type var_list
+%type var_list_decl
+%type assign_op_struct
+
+
+%left '['
+%right opMODASN opANDASN opXORASN opPLASN opMIASN opMLASN opDVASN opMDASN opNDASN opNTASN opORASN opSLASN opSRASN '='
+%left '?' ':'
+%left opOR
+%left opAND
+%left '|'
+%left '^'
+%left '&'
+%left opEQ opNE
+%left '<' opLE '>' opGE
+%left '@' opCAT opSTREQ opSTRNE
+%left opSHL opSHR
+%left '+' '-'
+%left '*' '/' '%'
+%right '!' '~' opPLUSPLUS opMINUSMINUS UNARY
+%left '.'
+
+%{
+/*
+ Additional tokens
+
+NOTE: These MUST be here, otherwise the definitions of the usual TorqueScript tokens changes
+which will break the compiler. Double check the standard TS tokens arent being redefined before
+testing a build.
+*/
+%}
+%token rwTHEN rwEND rwBEGIN rwCFOR rwTO rwSTEP
+
+%%
+
+start
+ : decl_list
+ { }
+ ;
+
+decl_list
+ :
+ { $$ = nil; }
+ | decl_list decl
+ { if(!statementList) { statementList = $2; } else { statementList->append($2); } }
+ ;
+
+decl
+ : stmt
+ { $$ = $1; }
+ | fn_decl_stmt
+ { $$ = $1; }
+ | package_decl
+ { $$ = $1; }
+ ;
+
+package_decl
+ : rwPACKAGE IDENT fn_decl_list rwEND
+ { $$ = $3; for(StmtNode *walk = ($3);walk;walk = walk->getNext() ) walk->setPackage($2); }
+ ;
+
+fn_decl_list
+ : fn_decl_stmt
+ { $$ = $1; }
+ | fn_decl_list fn_decl_stmt
+ { $$ = $1; ($1)->append($2); }
+ ;
+
+statement_list
+ :
+ { $$ = nil; }
+ | statement_list stmt
+ { if(!$1) { $$ = $2; } else { ($1)->append($2); $$ = $1; } }
+ ;
+
+stmt
+ : if_stmt
+ | while_stmt
+ | for_stmt
+ | datablock_decl
+ | switch_stmt
+ | rwBREAK semicolon
+ { $$ = BreakStmtNode::alloc(); }
+ | rwCONTINUE semicolon
+ { $$ = ContinueStmtNode::alloc(); }
+ | rwRETURN semicolon
+ { $$ = ReturnStmtNode::alloc(NULL); }
+ | rwRETURN expr semicolon
+ { $$ = ReturnStmtNode::alloc($2); }
+ | expression_stmt semicolon
+ { $$ = $1; }
+ | TTAG '=' expr semicolon
+ { $$ = TTagSetStmtNode::alloc($1, $3, NULL); }
+ | TTAG '=' expr ',' expr semicolon
+ { $$ = TTagSetStmtNode::alloc($1, $3, $5); }
+ ;
+
+fn_decl_stmt
+ : rwDEFINE IDENT '(' var_list_decl ')' statement_list rwEND
+ { $$ = FunctionDeclStmtNode::alloc($2, NULL, $4, $6); }
+ | rwDEFINE IDENT opCOLONCOLON IDENT '(' var_list_decl ')' statement_list rwEND
+ { $$ = FunctionDeclStmtNode::alloc($4, $2, $6, $8); }
+ ;
+
+var_list_decl
+ :
+ { $$ = NULL; }
+ | var_list
+ { $$ = $1; }
+ ;
+
+var_list
+ : VAR
+ { $$ = VarNode::alloc($1, NULL); }
+ | var_list ',' VAR
+ { $$ = $1; ((StmtNode*)($1))->append((StmtNode*)VarNode::alloc($3, NULL)); }
+ ;
+
+datablock_decl
+ : rwDATABLOCK IDENT '(' IDENT parent_block ')' slot_assign_list rwEND
+ { $$ = ObjectDeclNode::alloc(ConstantNode::alloc($2), ConstantNode::alloc($4), NULL, $5, $7, NULL, true); }
+ ;
+
+object_decl
+ : rwDECLARE class_name_expr '(' object_name parent_block object_args ')' object_declare_block rwEND
+ { $$ = ObjectDeclNode::alloc($2, $4, $6, $5, $8.slots, $8.decls, false); }
+ | rwDECLARE class_name_expr '(' object_name parent_block object_args ')' rwEND
+ { $$ = ObjectDeclNode::alloc($2, $4, $6, $5, NULL, NULL, false); }
+ ;
+
+parent_block
+ :
+ { $$ = NULL; }
+ | ':' IDENT
+ { $$ = $2; }
+ ;
+
+object_name
+ :
+ { $$ = StrConstNode::alloc("", false); }
+ | expr
+ { $$ = $1; }
+ ;
+
+object_args
+ :
+ { $$ = NULL; }
+ | ',' expr_list
+ { $$ = $2; }
+ ;
+
+object_declare_block
+ :
+ { $$.slots = NULL; $$.decls = NULL; }
+ | slot_assign_list
+ { $$.slots = $1; $$.decls = NULL; }
+ | object_decl_list
+ { $$.slots = NULL; $$.decls = $1; }
+ | slot_assign_list object_decl_list
+ { $$.slots = $1; $$.decls = $2; }
+ ;
+
+object_decl_list
+ : object_decl semicolon
+ { $$ = $1; }
+ | object_decl_list object_decl semicolon
+ { $1->append($2); $$ = $1; }
+ ;
+
+stmt_block
+ : rwBEGIN statement_list rwEND
+ { $$ = $2; }
+ | stmt
+ { $$ = $1; }
+ ;
+
+switch_stmt
+ : rwSWITCH expr case_block rwEND
+ { $$ = $3; $3->propagateSwitchExpr($2, false); }
+ | rwSWITCHSTR expr case_block rwEND
+ { $$ = $3; $3->propagateSwitchExpr($2, true); }
+ ;
+
+case_block
+ : rwCASE case_expr ':' statement_list
+ { $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
+ | rwCASE case_expr ':' statement_list rwDEFAULT ':' statement_list
+ { $$ = IfStmtNode::alloc($1, $2, $4, $7, false); }
+ | rwCASE case_expr ':' statement_list case_block
+ { $$ = IfStmtNode::alloc($1, $2, $4, $5, true); }
+ ;
+
+case_expr
+ : expr
+ { $$ = $1;}
+ | case_expr rwCASEOR expr
+ { ($1)->append($3); $$=$1; }
+ ;
+
+if_stmt
+ : rwIF expr rwTHEN statement_list rwEND
+ { $$ = IfStmtNode::alloc($1, $2, $4, NULL, false); }
+ | rwIF expr rwTHEN statement_list rwELSE statement_list rwEND
+ { $$ = IfStmtNode::alloc($1, $2, $4, $6, false); }
+ ;
+
+while_stmt
+ : rwWHILE expr rwBEGIN statement_list rwEND
+ { $$ = LoopStmtNode::alloc($1, nil, $2, nil, $4, false); }
+ ;
+
+for_stmt
+ : rwFOR expr ',' expr ',' expr rwBEGIN statement_list rwEND
+ { $$ = LoopStmtNode::alloc($1, $2, $4, $6, $8, false); }
+ ;
+
+expression_stmt
+ : stmt_expr
+ { $$ = $1; }
+ ;
+
+expr
+ : stmt_expr
+ { $$ = $1; }
+ | '(' expr ')'
+ { $$ = $2; }
+ | expr '^' expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr '%' expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr '&' expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr '|' expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr '+' expr
+ { $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
+ | expr '-' expr
+ { $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
+ | expr '*' expr
+ { $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
+ | expr '/' expr
+ { $$ = FloatBinaryExprNode::alloc($2, $1, $3); }
+ | '-' expr %prec UNARY
+ { $$ = FloatUnaryExprNode::alloc($1, $2); }
+ | '*' expr %prec UNARY
+ { $$ = TTagDerefNode::alloc($2); }
+ | TTAG
+ { $$ = TTagExprNode::alloc($1); }
+ | expr '?' expr ':' expr
+ { $$ = ConditionalExprNode::alloc($1, $3, $5); }
+ | expr '<' expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr '>' expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opGE expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opLE expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opEQ expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opNE expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opOR expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opSHL expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opSHR expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opAND expr
+ { $$ = IntBinaryExprNode::alloc($2, $1, $3); }
+ | expr opSTREQ expr
+ { $$ = StreqExprNode::alloc($1, $3, true); }
+ | expr opSTRNE expr
+ { $$ = StreqExprNode::alloc($1, $3, false); }
+ | expr '@' expr
+ { $$ = StrcatExprNode::alloc($1, $3, $2); }
+ | '!' expr
+ { $$ = IntUnaryExprNode::alloc($1, $2); }
+ | '~' expr
+ { $$ = IntUnaryExprNode::alloc($1, $2); }
+ | TAGATOM
+ { $$ = StrConstNode::alloc($1, true); }
+ | FLTCONST
+ { $$ = FloatNode::alloc($1); }
+ | INTCONST
+ { $$ = IntNode::alloc($1); }
+ | rwBREAK
+ { $$ = ConstantNode::alloc(StringTable->insert("break")); }
+ | slot_acc
+ { $$ = SlotAccessNode::alloc($1.object, $1.array, $1.slotName); }
+ | IDENT
+ { $$ = ConstantNode::alloc($1); }
+ | STRATOM
+ { $$ = StrConstNode::alloc($1, false); }
+ | VAR
+ { $$ = (ExprNode*)VarNode::alloc($1, NULL); }
+ | VAR '[' aidx_expr ']'
+ { $$ = (ExprNode*)VarNode::alloc($1, $3); }
+ ;
+
+slot_acc
+ : expr '.' IDENT
+ { $$.object = $1; $$.slotName = $3; $$.array = NULL; }
+ | expr '.' IDENT '[' aidx_expr ']'
+ { $$.object = $1; $$.slotName = $3; $$.array = $5; }
+ ;
+
+class_name_expr
+ : IDENT
+ { $$ = ConstantNode::alloc($1); }
+ | '(' expr ')'
+ { $$ = $2; }
+ ;
+
+assign_op_struct
+ : opPLUSPLUS
+ { $$.token = '+'; $$.expr = FloatNode::alloc(1); }
+ | opMINUSMINUS
+ { $$.token = '-'; $$.expr = FloatNode::alloc(1); }
+ | opPLASN expr
+ { $$.token = '+'; $$.expr = $2; }
+ | opMIASN expr
+ { $$.token = '-'; $$.expr = $2; }
+ | opMLASN expr
+ { $$.token = '*'; $$.expr = $2; }
+ | opDVASN expr
+ { $$.token = '/'; $$.expr = $2; }
+ | opMODASN expr
+ { $$.token = '%'; $$.expr = $2; }
+ | opANDASN expr
+ { $$.token = '&'; $$.expr = $2; }
+ | opXORASN expr
+ { $$.token = '^'; $$.expr = $2; }
+ | opORASN expr
+ { $$.token = '|'; $$.expr = $2; }
+ | opSLASN expr
+ { $$.token = opSHL; $$.expr = $2; }
+ | opSRASN expr
+ { $$.token = opSHR; $$.expr = $2; }
+ ;
+
+stmt_expr
+ : funcall_expr
+ { $$ = $1; }
+ | object_decl
+ { $$ = $1; }
+ | VAR '=' expr
+ { $$ = AssignExprNode::alloc($1, NULL, $3); }
+ | VAR '[' aidx_expr ']' '=' expr
+ { $$ = AssignExprNode::alloc($1, $3, $6); }
+ | VAR assign_op_struct
+ { $$ = AssignOpExprNode::alloc($1, NULL, $2.expr, $2.token); }
+ | VAR '[' aidx_expr ']' assign_op_struct
+ { $$ = AssignOpExprNode::alloc($1, $3, $5.expr, $5.token); }
+ | slot_acc assign_op_struct
+ { $$ = SlotAssignOpNode::alloc($1.object, $1.slotName, $1.array, $2.token, $2.expr); }
+ | slot_acc '=' expr
+ { $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $3); }
+ | slot_acc '=' '{' expr_list '}'
+ { $$ = SlotAssignNode::alloc($1.object, $1.array, $1.slotName, $4); }
+ ;
+
+funcall_expr
+ : IDENT '(' expr_list_decl ')'
+ { $$ = FuncCallExprNode::alloc($1, NULL, $3, false); }
+ | IDENT opCOLONCOLON IDENT '(' expr_list_decl ')'
+ { $$ = FuncCallExprNode::alloc($3, $1, $5, false); }
+ | expr '.' IDENT '(' expr_list_decl ')'
+ { $1->append($5); $$ = FuncCallExprNode::alloc($3, NULL, $1, true); }
+ ;
+
+expr_list_decl
+ :
+ { $$ = NULL; }
+ | expr_list
+ { $$ = $1; }
+ ;
+
+expr_list
+ : expr
+ { $$ = $1; }
+ | expr_list ',' expr
+ { ($1)->append($3); $$ = $1; }
+ ;
+
+slot_assign_list
+ : slot_assign
+ { $$ = $1; }
+ | slot_assign_list slot_assign
+ { $1->append($2); $$ = $1; }
+ ;
+
+slot_assign
+ : IDENT '=' expr semicolon
+ { $$ = SlotAssignNode::alloc(NULL, NULL, $1, $3); }
+ | rwDATABLOCK '=' expr semicolon
+ { $$ = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), $3); }
+ | IDENT '[' aidx_expr ']' '=' expr semicolon
+ { $$ = SlotAssignNode::alloc(NULL, $3, $1, $6); }
+ ;
+
+aidx_expr
+ : expr
+ { $$ = $1; }
+ | aidx_expr ',' expr
+ { $$ = CommaCatExprNode::alloc($1, $3); }
+ ;
+
+semicolon:
+ | semicolon ';'
+ ;
+%%
+
diff --git a/engine/console/BASscan.cc b/engine/console/BASscan.cc
new file mode 100755
index 0000000..72383cd
--- /dev/null
+++ b/engine/console/BASscan.cc
@@ -0,0 +1,2190 @@
+#define yy_create_buffer BAS_create_buffer
+#define yy_delete_buffer BAS_delete_buffer
+#define yy_scan_buffer BAS_scan_buffer
+#define yy_scan_string BAS_scan_string
+#define yy_scan_bytes BAS_scan_bytes
+#define yy_flex_debug BAS_flex_debug
+#define yy_init_buffer BAS_init_buffer
+#define yy_flush_buffer BAS_flush_buffer
+#define yy_load_buffer_state BAS_load_buffer_state
+#define yy_switch_to_buffer BAS_switch_to_buffer
+#define yyin BASin
+#define yyleng BASleng
+#define yylex BASlex
+#define yyout BASout
+#define yyrestart BASrestart
+#define yytext BAStext
+#define yywrap BASwrap
+
+#line 20 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\\\BASscan.cc"
+/* A lexical scanner generated by flex */
+
+/* Scanner skeleton version:
+ * $Header: /home/daffy/u0/vern/flex/RCS/flex.skl,v 2.85 95/04/24 10:48:47 vern Exp $
+ */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 5
+
+#include
+
+
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+
+
+#ifdef __cplusplus
+
+#include
+//nclude
+
+/* Use prototypes in function declarations. */
+#define YY_USE_PROTOS
+
+/* The "const" storage-class-modifier is valid. */
+#define YY_USE_CONST
+
+#else /* ! __cplusplus */
+
+#if __STDC__
+
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+
+#ifdef __TURBOC__
+ #pragma warn -rch
+ #pragma warn -use
+#include
+#include
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#endif
+
+#ifdef YY_USE_CONST
+#define yyconst const
+#else
+#define yyconst
+#endif
+
+
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#define YY_BUF_SIZE 16384
+
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern int yyleng;
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* The funky do-while in the following #define is used to turn the definition
+ * int a single C statement (which needs a semi-colon terminator). This
+ * avoids problems with code like:
+ *
+ * if ( condition_holds )
+ * yyless( 5 );
+ * else
+ * do_something_else();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the yyless() call.
+ */
+
+/* Return all but the first 'n' matched characters back to the input stream. */
+
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ *yy_cp = yy_hold_char; \
+ yy_c_buf_p = yy_cp = yy_bp + n - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yytext_ptr )
+
+/* The following is because we cannot portably get our hands on size_t
+ * (without autoconf's help, which isn't available because we want
+ * flex-generated scanners to compile on their own).
+ */
+typedef unsigned int yy_size_t;
+
+
+struct yy_buffer_state
+ {
+ FILE *yy_input_file;
+
+ char *yy_ch_buf; /* input buffer */
+ char *yy_buf_pos; /* current position in input buffer */
+
+ /* Size of input buffer in bytes, not including room for EOB
+ * characters.
+ */
+ yy_size_t yy_buf_size;
+
+ /* Number of characters read into yy_ch_buf, not including EOB
+ * characters.
+ */
+ int yy_n_chars;
+
+ /* Whether we "own" the buffer - i.e., we know we created it,
+ * and can realloc() it to grow it, and should free() it to
+ * delete it.
+ */
+ int yy_is_our_buffer;
+
+ /* Whether this is an "interactive" input source; if so, and
+ * if we're using stdio for input, then we want to use getc()
+ * instead of fread(), to make sure we stop fetching input after
+ * each newline.
+ */
+ int yy_is_interactive;
+
+ /* Whether we're considered to be at the beginning of a line.
+ * If so, '^' rules will be active on the next match, otherwise
+ * not.
+ */
+ int yy_at_bol;
+
+ /* Whether to try to fill the input buffer when we reach the
+ * end of it.
+ */
+ int yy_fill_buffer;
+
+ int yy_buffer_status;
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via yyrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+ };
+
+static YY_BUFFER_STATE yy_current_buffer = 0;
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ */
+#define YY_CURRENT_BUFFER yy_current_buffer
+
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = (char *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart YY_PROTO(( FILE *input_file ));
+
+void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+void yy_load_buffer_state YY_PROTO(( void ));
+YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size ));
+void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file ));
+void yy_flush_buffer YY_PROTO(( YY_BUFFER_STATE b ));
+#define YY_FLUSH_BUFFER yy_flush_buffer( yy_current_buffer )
+
+YY_BUFFER_STATE yy_scan_buffer YY_PROTO(( char *base, yy_size_t size ));
+YY_BUFFER_STATE yy_scan_string YY_PROTO(( yyconst char *str ));
+YY_BUFFER_STATE yy_scan_bytes YY_PROTO(( yyconst char *bytes, int len ));
+
+static void *yy_flex_alloc YY_PROTO(( yy_size_t ));
+static void *yy_flex_realloc YY_PROTO(( void *, yy_size_t ));
+static void yy_flex_free YY_PROTO(( void * ));
+
+#define yy_new_buffer yy_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! yy_current_buffer ) \
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); \
+ yy_current_buffer->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (yy_current_buffer->yy_at_bol)
+
+typedef unsigned char YY_CHAR;
+FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0;
+typedef int yy_state_type;
+extern char *yytext;
+#define yytext_ptr yytext
+
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yy_fatal_error YY_PROTO(( yyconst char msg[] ));
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yytext_ptr = yy_bp; \
+ yyleng = (int) (yy_cp - yy_bp); \
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 92
+#define YY_END_OF_BUFFER 93
+static yyconst short int yy_accept[205] =
+ { 0,
+ 0, 0, 93, 91, 1, 5, 4, 48, 91, 91,
+ 55, 54, 91, 38, 39, 42, 40, 53, 41, 47,
+ 43, 88, 88, 49, 50, 44, 58, 45, 35, 34,
+ 86, 86, 86, 86, 36, 37, 56, 86, 86, 86,
+ 86, 86, 86, 86, 86, 86, 86, 86, 86, 86,
+ 51, 46, 52, 57, 1, 0, 9, 0, 6, 0,
+ 0, 17, 85, 25, 12, 26, 0, 7, 0, 23,
+ 16, 21, 15, 22, 2, 24, 0, 88, 0, 0,
+ 14, 19, 11, 8, 10, 20, 86, 31, 86, 86,
+ 27, 86, 86, 86, 86, 86, 86, 86, 66, 86,
+
+ 86, 86, 86, 86, 64, 86, 59, 86, 86, 86,
+ 86, 86, 86, 71, 86, 86, 28, 13, 18, 90,
+ 85, 0, 2, 89, 0, 89, 87, 29, 30, 33,
+ 32, 86, 86, 86, 86, 86, 86, 86, 86, 68,
+ 86, 69, 86, 76, 86, 86, 86, 86, 75, 86,
+ 86, 86, 86, 90, 0, 86, 86, 78, 70, 86,
+ 86, 86, 62, 86, 86, 86, 3, 86, 72, 86,
+ 65, 83, 86, 67, 60, 86, 86, 86, 84, 86,
+ 86, 3, 3, 86, 86, 63, 86, 86, 86, 86,
+ 86, 61, 80, 86, 86, 81, 86, 82, 79, 73,
+
+ 86, 74, 77, 0
+ } ;
+
+static yyconst int yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 2, 2, 4, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 5, 6, 1, 7, 8, 9, 10, 11,
+ 12, 13, 14, 15, 16, 17, 18, 19, 20, 20,
+ 20, 20, 20, 20, 20, 20, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 32, 31,
+ 33, 33, 33, 33, 33, 34, 33, 35, 33, 36,
+ 33, 33, 37, 38, 33, 33, 33, 39, 33, 33,
+ 40, 41, 42, 43, 33, 1, 44, 45, 46, 47,
+
+ 48, 49, 50, 51, 52, 33, 53, 54, 55, 56,
+ 57, 58, 33, 59, 60, 61, 62, 33, 63, 39,
+ 33, 33, 64, 65, 66, 67, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst int yy_meta[68] =
+ { 0,
+ 1, 1, 2, 2, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 3, 3,
+ 4, 1, 1, 5, 1, 1, 1, 3, 3, 3,
+ 3, 3, 6, 6, 6, 6, 6, 6, 6, 1,
+ 1, 1, 1, 3, 3, 3, 3, 3, 3, 6,
+ 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+ 6, 6, 6, 1, 1, 1, 1
+ } ;
+
+static yyconst short int yy_base[215] =
+ { 0,
+ 0, 0, 281, 282, 278, 282, 282, 61, 63, 51,
+ 53, 65, 66, 282, 282, 255, 64, 282, 63, 282,
+ 62, 64, 73, 257, 282, 71, 253, 73, 282, 282,
+ 0, 242, 239, 246, 282, 282, 249, 43, 57, 65,
+ 54, 67, 223, 223, 211, 225, 220, 54, 68, 216,
+ 282, 58, 282, 282, 264, 241, 282, 93, 282, 260,
+ 111, 282, 242, 282, 282, 282, 108, 282, 258, 282,
+ 282, 282, 282, 282, 0, 282, 113, 118, 125, 0,
+ 282, 237, 282, 282, 282, 236, 0, 0, 229, 229,
+ 282, 207, 208, 195, 197, 197, 191, 202, 0, 190,
+
+ 202, 194, 188, 190, 0, 182, 0, 198, 65, 195,
+ 197, 189, 192, 0, 177, 186, 282, 282, 282, 216,
+ 215, 214, 0, 123, 127, 132, 0, 282, 282, 0,
+ 0, 182, 189, 184, 172, 169, 185, 184, 179, 0,
+ 166, 0, 179, 0, 171, 221, 160, 163, 0, 159,
+ 163, 170, 163, 195, 190, 152, 148, 0, 0, 146,
+ 149, 131, 0, 142, 128, 139, 177, 114, 0, 124,
+ 0, 0, 121, 0, 0, 112, 113, 111, 0, 112,
+ 113, 0, 160, 105, 109, 0, 97, 101, 96, 99,
+ 106, 0, 146, 100, 94, 0, 80, 0, 282, 0,
+
+ 75, 0, 0, 282, 171, 175, 181, 185, 189, 193,
+ 199, 97, 203, 209
+ } ;
+
+static yyconst short int yy_def[215] =
+ { 0,
+ 204, 1, 204, 204, 204, 204, 204, 204, 205, 206,
+ 206, 204, 207, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 208, 208, 208, 208, 204, 204, 204, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208, 208, 208,
+ 204, 204, 204, 204, 204, 204, 204, 205, 204, 205,
+ 209, 204, 210, 204, 204, 204, 207, 204, 207, 204,
+ 204, 204, 204, 204, 211, 204, 204, 204, 204, 212,
+ 204, 204, 204, 204, 204, 204, 208, 208, 208, 208,
+ 204, 208, 208, 208, 208, 208, 208, 208, 208, 208,
+
+ 208, 208, 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 204, 204, 204, 213,
+ 210, 210, 211, 204, 204, 204, 212, 204, 204, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 213, 213, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 214, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208, 208, 208,
+ 208, 214, 214, 208, 208, 208, 208, 208, 208, 208,
+ 208, 208, 208, 208, 208, 208, 208, 208, 204, 208,
+
+ 208, 208, 208, 0, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204
+ } ;
+
+static yyconst short int yy_nxt[350] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31, 31, 31,
+ 31, 31, 31, 31, 32, 31, 33, 34, 31, 35,
+ 4, 36, 37, 31, 38, 39, 40, 41, 42, 31,
+ 31, 43, 31, 31, 31, 44, 45, 46, 47, 48,
+ 49, 31, 50, 51, 52, 53, 54, 56, 59, 61,
+ 61, 61, 61, 65, 62, 68, 64, 71, 73, 75,
+ 77, 117, 78, 78, 57, 76, 74, 72, 66, 77,
+ 92, 78, 78, 82, 83, 79, 85, 86, 59, 127,
+
+ 94, 93, 80, 60, 79, 95, 69, 100, 97, 101,
+ 102, 79, 98, 96, 110, 111, 112, 68, 113, 146,
+ 79, 99, 118, 103, 114, 147, 115, 203, 104, 61,
+ 61, 124, 124, 60, 77, 202, 78, 78, 125, 201,
+ 125, 124, 124, 126, 126, 126, 126, 200, 69, 79,
+ 126, 126, 199, 198, 79, 197, 196, 195, 194, 193,
+ 192, 183, 191, 190, 189, 79, 188, 187, 186, 185,
+ 79, 58, 184, 58, 58, 58, 58, 63, 183, 63,
+ 63, 67, 181, 67, 67, 67, 67, 87, 180, 179,
+ 87, 120, 178, 177, 120, 121, 121, 176, 121, 123,
+
+ 175, 123, 123, 123, 123, 154, 154, 174, 154, 182,
+ 155, 182, 182, 182, 182, 155, 173, 172, 171, 170,
+ 169, 168, 167, 166, 165, 164, 163, 162, 161, 160,
+ 159, 158, 157, 156, 122, 122, 155, 153, 152, 151,
+ 150, 149, 148, 145, 144, 143, 142, 141, 140, 139,
+ 138, 137, 136, 135, 134, 133, 132, 131, 130, 129,
+ 128, 67, 122, 58, 119, 55, 116, 109, 108, 107,
+ 106, 105, 91, 90, 89, 88, 84, 81, 70, 55,
+ 204, 3, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204
+ } ;
+
+static yyconst short int yy_chk[350] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 8, 9, 10,
+ 10, 11, 11, 12, 10, 13, 11, 17, 19, 21,
+ 22, 52, 22, 22, 8, 21, 19, 17, 12, 23,
+ 38, 23, 23, 26, 26, 22, 28, 28, 58, 212,
+
+ 39, 38, 22, 9, 23, 39, 13, 41, 40, 41,
+ 42, 22, 40, 39, 48, 48, 48, 67, 49, 109,
+ 23, 40, 52, 42, 49, 109, 49, 201, 42, 61,
+ 61, 77, 77, 58, 78, 197, 78, 78, 79, 195,
+ 79, 124, 124, 79, 79, 125, 125, 194, 67, 78,
+ 126, 126, 193, 191, 124, 190, 189, 188, 187, 185,
+ 184, 183, 181, 180, 178, 78, 177, 176, 173, 170,
+ 124, 205, 168, 205, 205, 205, 205, 206, 167, 206,
+ 206, 207, 166, 207, 207, 207, 207, 208, 165, 164,
+ 208, 209, 162, 161, 209, 210, 210, 160, 210, 211,
+
+ 157, 211, 211, 211, 211, 213, 213, 156, 213, 214,
+ 155, 214, 214, 214, 214, 154, 153, 152, 151, 150,
+ 148, 147, 146, 145, 143, 141, 139, 138, 137, 136,
+ 135, 134, 133, 132, 122, 121, 120, 116, 115, 113,
+ 112, 111, 110, 108, 106, 104, 103, 102, 101, 100,
+ 98, 97, 96, 95, 94, 93, 92, 90, 89, 86,
+ 82, 69, 63, 60, 56, 55, 50, 47, 46, 45,
+ 44, 43, 37, 34, 33, 32, 27, 24, 16, 5,
+ 3, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204, 204,
+ 204, 204, 204, 204, 204, 204, 204, 204, 204
+ } ;
+
+static yy_state_type yy_last_accepting_state;
+static char *yy_last_accepting_cpos;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+char *yytext;
+#line 1 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+#define INITIAL 0
+#line 2 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+#define YYLMAX 4096
+
+#include
+#include "platform/platform.h"
+#include "core/stringTable.h"
+#include "console/console.h"
+#define _CMDGRAM_H_
+#include "console/compiler.h"
+#include "console/basgram.h"
+
+using namespace Compiler;
+
+#define YY_NEVER_INTERACTIVE 1
+
+// Some basic parsing primitives...
+static int Sc_ScanString(int ret);
+static int Sc_ScanNum();
+static int Sc_ScanVar();
+static int Sc_ScanHex();
+
+// Deal with debuggability of FLEX.
+#ifdef TORQUE_DEBUG
+#define FLEX_DEBUG 1
+#else
+#define FLEX_DEBUG 0
+#endif
+
+//#undef input
+//#undef unput
+#undef BASgetc
+int BASgetc();
+
+#define YY_INPUT(buf,result,max_size) \
+{ \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = BASgetc()) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ result = n; \
+}
+
+static int lineIndex;
+
+// Prototypes
+void BASSetScanBuffer(const char *sb, const char *fn);
+void BASerror(char * s, ...);
+
+// Error reporting
+void CMDerror(char * s, ...);
+
+// Reset the parser.
+void CMDrestart(FILE *in);
+
+#line 584 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\\\BASscan.cc"
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap YY_PROTO(( void ));
+#else
+extern int yywrap YY_PROTO(( void ));
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+static void yyunput YY_PROTO(( int c, char *buf_ptr ));
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy YY_PROTO(( char *, yyconst char *, int ));
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+#endif
+
+#if YY_STACK_USED
+static int yy_start_stack_ptr = 0;
+static int yy_start_stack_depth = 0;
+static int *yy_start_stack = 0;
+#ifndef YY_NO_PUSH_STATE
+static void yy_push_state YY_PROTO(( int new_state ));
+#endif
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state YY_PROTO(( void ));
+#endif
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state YY_PROTO(( void ));
+#endif
+
+#else
+#define YY_NO_PUSH_STATE 1
+#define YY_NO_POP_STATE 1
+#define YY_NO_TOP_STATE 1
+#endif
+
+#ifdef YY_MALLOC_DECL
+YY_MALLOC_DECL
+#else
+#if __STDC__
+#ifndef __cplusplus
+#include
+#endif
+#else
+/* Just try to get by without declaring the routines. This will fail
+ * miserably on non-ANSI systems for which sizeof(size_t) != sizeof(int)
+ * or sizeof(void*) != sizeof(int).
+ */
+#endif
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO (void) fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( yy_current_buffer->yy_is_interactive ) \
+ { \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else if ( ((result = fread( buf, 1, max_size, yyin )) == 0) \
+ && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" );
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL int yylex YY_PROTO(( void ))
+#endif
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+YY_DECL
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+
+#line 74 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+
+ ;
+#line 734 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\\\BASscan.cc"
+
+ if ( yy_init )
+ {
+ yy_init = 0;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! yy_current_buffer )
+ yy_current_buffer =
+ yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_load_buffer_state();
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 205 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 282 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+
+do_action: /* This label is used only to access EOF actions. */
+
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+ yy_cp = yy_last_accepting_cpos;
+ yy_current_state = yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 76 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ }
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 77 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+;
+ YY_BREAK
+case 3:
+YY_RULE_SETUP
+#line 78 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+;
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 79 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+;
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 80 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ lineIndex++; }
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 81 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ return(Sc_ScanString(STRATOM)); }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 82 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ return(Sc_ScanString(TAGATOM)); }
+ YY_BREAK
+case 8:
+YY_RULE_SETUP
+#line 83 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opEQ);
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 84 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opNE);
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 85 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opGE);
+ YY_BREAK
+case 11:
+YY_RULE_SETUP
+#line 86 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opLE);
+ YY_BREAK
+case 12:
+YY_RULE_SETUP
+#line 87 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opAND);
+ YY_BREAK
+case 13:
+YY_RULE_SETUP
+#line 88 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opOR);
+ YY_BREAK
+case 14:
+YY_RULE_SETUP
+#line 89 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opCOLONCOLON);
+ YY_BREAK
+case 15:
+YY_RULE_SETUP
+#line 90 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opMINUSMINUS);
+ YY_BREAK
+case 16:
+YY_RULE_SETUP
+#line 91 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opPLUSPLUS);
+ YY_BREAK
+case 17:
+YY_RULE_SETUP
+#line 92 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opSTREQ);
+ YY_BREAK
+case 18:
+YY_RULE_SETUP
+#line 93 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opSTRNE);
+ YY_BREAK
+case 19:
+YY_RULE_SETUP
+#line 94 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opSHL);
+ YY_BREAK
+case 20:
+YY_RULE_SETUP
+#line 95 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opSHR);
+ YY_BREAK
+case 21:
+YY_RULE_SETUP
+#line 96 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opPLASN);
+ YY_BREAK
+case 22:
+YY_RULE_SETUP
+#line 97 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opMIASN);
+ YY_BREAK
+case 23:
+YY_RULE_SETUP
+#line 98 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opMLASN);
+ YY_BREAK
+case 24:
+YY_RULE_SETUP
+#line 99 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opDVASN);
+ YY_BREAK
+case 25:
+YY_RULE_SETUP
+#line 100 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opMODASN);
+ YY_BREAK
+case 26:
+YY_RULE_SETUP
+#line 101 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opANDASN);
+ YY_BREAK
+case 27:
+YY_RULE_SETUP
+#line 102 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opXORASN);
+ YY_BREAK
+case 28:
+YY_RULE_SETUP
+#line 103 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opORASN);
+ YY_BREAK
+case 29:
+YY_RULE_SETUP
+#line 104 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opSLASN);
+ YY_BREAK
+case 30:
+YY_RULE_SETUP
+#line 105 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(BASlval.i = opSRASN);
+ YY_BREAK
+case 31:
+YY_RULE_SETUP
+#line 106 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{BASlval.i = '\n'; return '@'; }
+ YY_BREAK
+case 32:
+YY_RULE_SETUP
+#line 107 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{BASlval.i = '\t'; return '@'; }
+ YY_BREAK
+case 33:
+YY_RULE_SETUP
+#line 108 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{BASlval.i = ' '; return '@'; }
+ YY_BREAK
+case 34:
+YY_RULE_SETUP
+#line 109 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{BASlval.i = 0; return '@'; }
+ YY_BREAK
+case 35:
+#line 111 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 36:
+#line 112 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 37:
+#line 113 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 38:
+#line 114 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 39:
+#line 115 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 40:
+#line 116 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 41:
+#line 117 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 42:
+#line 118 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 43:
+#line 119 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 44:
+#line 120 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 45:
+#line 121 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 46:
+#line 122 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 47:
+#line 123 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 48:
+#line 124 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 49:
+#line 125 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 50:
+#line 126 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 51:
+#line 127 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 52:
+#line 128 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 53:
+#line 129 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 54:
+#line 130 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 55:
+#line 131 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 56:
+#line 132 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 57:
+#line 133 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+case 58:
+YY_RULE_SETUP
+#line 133 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ return(BASlval.i = BAStext[0]); }
+ YY_BREAK
+case 59:
+YY_RULE_SETUP
+#line 134 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwCASEOR); }
+ YY_BREAK
+case 60:
+YY_RULE_SETUP
+#line 135 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwBREAK); }
+ YY_BREAK
+case 61:
+YY_RULE_SETUP
+#line 136 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwRETURN); }
+ YY_BREAK
+case 62:
+YY_RULE_SETUP
+#line 137 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwELSE); }
+ YY_BREAK
+case 63:
+YY_RULE_SETUP
+#line 138 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwWHILE); }
+ YY_BREAK
+case 64:
+YY_RULE_SETUP
+#line 139 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwIF); }
+ YY_BREAK
+case 65:
+YY_RULE_SETUP
+#line 140 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwTHEN); }
+ YY_BREAK
+case 66:
+YY_RULE_SETUP
+#line 141 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwBEGIN); }
+ YY_BREAK
+case 67:
+YY_RULE_SETUP
+#line 142 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwBEGIN); }
+ YY_BREAK
+case 68:
+YY_RULE_SETUP
+#line 143 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwEND); }
+ YY_BREAK
+case 69:
+YY_RULE_SETUP
+#line 144 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwFOR); }
+ YY_BREAK
+case 70:
+YY_RULE_SETUP
+#line 145 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwCFOR); }
+ YY_BREAK
+case 71:
+YY_RULE_SETUP
+#line 146 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwTO); }
+ YY_BREAK
+case 72:
+YY_RULE_SETUP
+#line 147 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwSTEP); }
+ YY_BREAK
+case 73:
+YY_RULE_SETUP
+#line 148 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwCONTINUE); }
+ YY_BREAK
+case 74:
+YY_RULE_SETUP
+#line 149 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwDEFINE); }
+ YY_BREAK
+case 75:
+YY_RULE_SETUP
+#line 150 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwDEFINE); }
+ YY_BREAK
+case 76:
+YY_RULE_SETUP
+#line 151 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwDECLARE); }
+ YY_BREAK
+case 77:
+YY_RULE_SETUP
+#line 152 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwDATABLOCK); }
+ YY_BREAK
+case 78:
+YY_RULE_SETUP
+#line 153 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwCASE); }
+ YY_BREAK
+case 79:
+YY_RULE_SETUP
+#line 154 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwSWITCHSTR); }
+ YY_BREAK
+case 80:
+YY_RULE_SETUP
+#line 155 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwSWITCH); }
+ YY_BREAK
+case 81:
+YY_RULE_SETUP
+#line 156 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwDEFAULT); }
+ YY_BREAK
+case 82:
+YY_RULE_SETUP
+#line 157 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = lineIndex; return(rwPACKAGE); }
+ YY_BREAK
+case 83:
+YY_RULE_SETUP
+#line 158 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = 1; return INTCONST; }
+ YY_BREAK
+case 84:
+YY_RULE_SETUP
+#line 159 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BASlval.i = 0; return INTCONST; }
+ YY_BREAK
+case 85:
+YY_RULE_SETUP
+#line 160 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(Sc_ScanVar());
+ YY_BREAK
+case 86:
+YY_RULE_SETUP
+#line 161 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BAStext[BASleng] = 0; BASlval.s = StringTable->insert(BAStext); return(IDENT); }
+ YY_BREAK
+case 87:
+YY_RULE_SETUP
+#line 162 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(Sc_ScanHex());
+ YY_BREAK
+case 88:
+YY_RULE_SETUP
+#line 163 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+{ BAStext[BASleng] = 0; BASlval.i = atoi(BAStext); return INTCONST; }
+ YY_BREAK
+case 89:
+YY_RULE_SETUP
+#line 164 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return Sc_ScanNum();
+ YY_BREAK
+case 90:
+YY_RULE_SETUP
+#line 165 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(ILLEGAL_TOKEN);
+ YY_BREAK
+case 91:
+YY_RULE_SETUP
+#line 166 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+return(ILLEGAL_TOKEN);
+ YY_BREAK
+case 92:
+YY_RULE_SETUP
+#line 167 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+ECHO;
+ YY_BREAK
+#line 1208 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\\\BASscan.cc"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * yylex(). If so, then we have to assure
+ * consistency between yy_current_buffer and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yy_current_buffer->yy_input_file = yyin;
+ yy_current_buffer->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p =
+ yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p =
+ &yy_current_buffer->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+ } /* end of yylex */
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+
+static int yy_get_next_buffer()
+ {
+ register char *dest = yy_current_buffer->yy_ch_buf;
+ register char *source = yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( yy_current_buffer->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yy_c_buf_p - yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a singled characater, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yy_c_buf_p - yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( yy_current_buffer->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ yy_current_buffer->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+#ifdef YY_USES_REJECT
+ YY_FATAL_ERROR(
+"input buffer overflow, can't enlarge buffer because scanner uses REJECT" );
+#else
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = yy_current_buffer;
+
+ int yy_c_buf_p_offset =
+ (int) (yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ yy_flex_realloc( (void *) b->yy_ch_buf,
+ b->yy_buf_size + 2 );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = yy_current_buffer->yy_buf_size -
+ number_to_move - 1;
+#endif
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]),
+ yy_n_chars, num_to_read );
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ yyrestart( yyin );
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ yy_current_buffer->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yytext_ptr = &yy_current_buffer->yy_ch_buf[0];
+
+ return ret_val;
+ }
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+static yy_state_type yy_get_previous_state()
+ {
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+
+ yy_current_state = yy_start;
+
+ for ( yy_cp = yytext_ptr + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 205 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+ }
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+yy_state_type yy_current_state;
+#endif
+ {
+ register int yy_is_jam;
+ register char *yy_cp = yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yy_last_accepting_state = yy_current_state;
+ yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 205 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 204);
+
+ return yy_is_jam ? 0 : yy_current_state;
+ }
+
+
+#ifndef YY_NO_UNPUT
+#ifdef YY_USE_PROTOS
+static void yyunput( int c, register char *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+int c;
+register char *yy_bp;
+#endif
+ {
+ register char *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yy_n_chars + 2;
+ register char *dest = &yy_current_buffer->yy_ch_buf[
+ yy_current_buffer->yy_buf_size + 2];
+ register char *source =
+ &yy_current_buffer->yy_ch_buf[number_to_move];
+
+ while ( source > yy_current_buffer->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ yy_n_chars = yy_current_buffer->yy_buf_size;
+
+ if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+
+ yytext_ptr = yy_bp;
+ yy_hold_char = *yy_cp;
+ yy_c_buf_p = yy_cp;
+ }
+#endif /* ifndef YY_NO_UNPUT */
+
+
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+ {
+ int c;
+
+ *yy_c_buf_p = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] )
+ /* This was really a NUL. */
+ *yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ yytext_ptr = yy_c_buf_p;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ {
+ yy_c_buf_p =
+ yytext_ptr + YY_MORE_ADJ;
+ return EOF;
+ }
+
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput();
+#else
+ return input();
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yytext_ptr + YY_MORE_ADJ;
+ break;
+
+ case EOB_ACT_LAST_MATCH:
+#ifdef __cplusplus
+ YY_FATAL_ERROR(
+ "unexpected last match in yyinput()" );
+#else
+ YY_FATAL_ERROR(
+ "unexpected last match in input()" );
+#endif
+ }
+ }
+ }
+
+ c = *(unsigned char *) yy_c_buf_p; /* cast for 8-bit char's */
+ *yy_c_buf_p = '\0'; /* preserve yytext */
+ yy_hold_char = *++yy_c_buf_p;
+
+
+ return c;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yyrestart( FILE *input_file )
+#else
+void yyrestart( input_file )
+FILE *input_file;
+#endif
+ {
+ if ( ! yy_current_buffer )
+ yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE );
+
+ yy_init_buffer( yy_current_buffer, input_file );
+ yy_load_buffer_state();
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer )
+#else
+void yy_switch_to_buffer( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+ {
+ if ( yy_current_buffer == new_buffer )
+ return;
+
+ if ( yy_current_buffer )
+ {
+ /* Flush out information for old buffer. */
+ *yy_c_buf_p = yy_hold_char;
+ yy_current_buffer->yy_buf_pos = yy_c_buf_p;
+ yy_current_buffer->yy_n_chars = yy_n_chars;
+ }
+
+ yy_current_buffer = new_buffer;
+ yy_load_buffer_state();
+
+ /* We don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_load_buffer_state( void )
+#else
+void yy_load_buffer_state()
+#endif
+ {
+ yy_n_chars = yy_current_buffer->yy_n_chars;
+ yytext_ptr = yy_c_buf_p = yy_current_buffer->yy_buf_pos;
+ yyin = yy_current_buffer->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+ }
+
+
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_create_buffer( FILE *file, int size )
+#else
+YY_BUFFER_STATE yy_create_buffer( file, size )
+FILE *file;
+int size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) yy_flex_alloc( b->yy_buf_size + 2 );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ yy_init_buffer( b, file );
+
+ return b;
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_delete_buffer( YY_BUFFER_STATE b )
+#else
+void yy_delete_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+ {
+ if ( ! b )
+ return;
+
+ if ( b == yy_current_buffer )
+ yy_current_buffer = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ yy_flex_free( (void *) b->yy_ch_buf );
+
+ yy_flex_free( (void *) b );
+ }
+
+
+#ifndef YY_ALWAYS_INTERACTIVE
+#ifndef YY_NEVER_INTERACTIVE
+extern int isatty YY_PROTO(( int ));
+#endif
+#endif
+
+#ifdef YY_USE_PROTOS
+void yy_init_buffer( YY_BUFFER_STATE b, FILE *file )
+#else
+void yy_init_buffer( b, file )
+YY_BUFFER_STATE b;
+FILE *file;
+#endif
+
+
+ {
+ yy_flush_buffer( b );
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+#if YY_ALWAYS_INTERACTIVE
+ b->yy_is_interactive = 1;
+#else
+#if YY_NEVER_INTERACTIVE
+ b->yy_is_interactive = 0;
+#else
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+#endif
+#endif
+ }
+
+
+#ifdef YY_USE_PROTOS
+void yy_flush_buffer( YY_BUFFER_STATE b )
+#else
+void yy_flush_buffer( b )
+YY_BUFFER_STATE b;
+#endif
+
+ {
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == yy_current_buffer )
+ yy_load_buffer_state();
+ }
+
+
+#ifndef YY_NO_SCAN_BUFFER
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_buffer( char *base, yy_size_t size )
+#else
+YY_BUFFER_STATE yy_scan_buffer( base, size )
+char *base;
+yy_size_t size;
+#endif
+ {
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) yy_flex_alloc( sizeof( struct yy_buffer_state ) );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ yy_switch_to_buffer( b );
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_STRING
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_string( yyconst char *str )
+#else
+YY_BUFFER_STATE yy_scan_string( str )
+yyconst char *str;
+#endif
+ {
+ int len;
+ for ( len = 0; str[len]; ++len )
+ ;
+
+ return yy_scan_bytes( str, len );
+ }
+#endif
+
+
+#ifndef YY_NO_SCAN_BYTES
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE yy_scan_bytes( yyconst char *bytes, int len )
+#else
+YY_BUFFER_STATE yy_scan_bytes( bytes, len )
+yyconst char *bytes;
+int len;
+#endif
+ {
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = len + 2;
+ buf = (char *) yy_flex_alloc( n );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+ for ( i = 0; i < len; ++i )
+ buf[i] = bytes[i];
+
+ buf[len] = buf[len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = yy_scan_buffer( buf, n );
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+ }
+#endif
+
+
+#ifndef YY_NO_PUSH_STATE
+#ifdef YY_USE_PROTOS
+static void yy_push_state( int new_state )
+#else
+static void yy_push_state( new_state )
+int new_state;
+#endif
+ {
+ if ( yy_start_stack_ptr >= yy_start_stack_depth )
+ {
+ yy_size_t new_size;
+
+ yy_start_stack_depth += YY_START_STACK_INCR;
+ new_size = yy_start_stack_depth * sizeof( int );
+
+ if ( ! yy_start_stack )
+ yy_start_stack = (int *) yy_flex_alloc( new_size );
+
+ else
+ yy_start_stack = (int *) yy_flex_realloc(
+ (void *) yy_start_stack, new_size );
+
+ if ( ! yy_start_stack )
+ YY_FATAL_ERROR(
+ "out of memory expanding start-condition stack" );
+ }
+
+ yy_start_stack[yy_start_stack_ptr++] = YY_START;
+
+ BEGIN(new_state);
+ }
+#endif
+
+
+#ifndef YY_NO_POP_STATE
+static void yy_pop_state()
+ {
+ if ( --yy_start_stack_ptr < 0 )
+ YY_FATAL_ERROR( "start-condition stack underflow" );
+
+ BEGIN(yy_start_stack[yy_start_stack_ptr]);
+ }
+#endif
+
+
+#ifndef YY_NO_TOP_STATE
+static int yy_top_state()
+ {
+ return yy_start_stack[yy_start_stack_ptr - 1];
+ }
+#endif
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+#ifdef YY_USE_PROTOS
+static void yy_fatal_error( yyconst char msg[] )
+#else
+static void yy_fatal_error( msg )
+char msg[];
+#endif
+ {
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+ }
+
+
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ yytext[yyleng] = yy_hold_char; \
+ yy_c_buf_p = yytext + n - YY_MORE_ADJ; \
+ yy_hold_char = *yy_c_buf_p; \
+ *yy_c_buf_p = '\0'; \
+ yyleng = n; \
+ } \
+ while ( 0 )
+
+
+/* Internal utility routines. */
+
+#ifndef yytext_ptr
+#ifdef YY_USE_PROTOS
+static void yy_flex_strncpy( char *s1, yyconst char *s2, int n )
+#else
+static void yy_flex_strncpy( s1, s2, n )
+char *s1;
+yyconst char *s2;
+int n;
+#endif
+ {
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+ }
+#endif
+
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_alloc( yy_size_t size )
+#else
+static void *yy_flex_alloc( size )
+yy_size_t size;
+#endif
+ {
+ return (void *) malloc( size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void *yy_flex_realloc( void *ptr, yy_size_t size )
+#else
+static void *yy_flex_realloc( ptr, size )
+void *ptr;
+yy_size_t size;
+#endif
+ {
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+ }
+
+#ifdef YY_USE_PROTOS
+static void yy_flex_free( void *ptr )
+#else
+static void yy_flex_free( ptr )
+void *ptr;
+#endif
+ {
+ free( ptr );
+ }
+
+#if YY_MAIN
+int main()
+ {
+ yylex();
+ return 0;
+ }
+#endif
+#line 167 "c:\\dev\\GameEngine\\torque_SVN\\trunk\\engine\\console\\BASscan.l"
+
+
+/*
+ * Scan character constant.
+ */
+
+/*
+ * Scan identifier.
+ */
+
+static const char *scanBuffer;
+static const char *fileName;
+static int scanIndex;
+
+const char * BASGetCurrentFile()
+{
+ return fileName;
+}
+
+int BASGetCurrentLine()
+{
+ return lineIndex;
+}
+
+void BASerror(char *, ...)
+{
+ gSyntaxError = true;
+ if(fileName)
+ Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - Syntax error.",
+ fileName, lineIndex);
+ else
+ Con::errorf(ConsoleLogEntry::Script, "Syntax error in input.");
+}
+
+void BASSetScanBuffer(const char *sb, const char *fn)
+{
+ scanBuffer = sb;
+ fileName = fn;
+ scanIndex = 0;
+ lineIndex = 1;
+}
+
+int BASgetc()
+{
+ int ret = scanBuffer[scanIndex];
+ if(ret)
+ scanIndex++;
+ else
+ ret = -1;
+ return ret;
+}
+
+int BASwrap()
+{
+ return 1;
+}
+
+static int Sc_ScanVar()
+{
+ BAStext[BASleng] = 0;
+ BASlval.s = StringTable->insert(BAStext);
+ return(VAR);
+}
+/*
+ * Scan string constant.
+ */
+
+// Defined in CS_CMD.l
+extern void expandEscape(char *dest, const char *src);
+extern bool collapseEscape(char *buf);
+
+static int charConv(int in)
+{
+ switch(in)
+ {
+ case 'r':
+ return '\r';
+ case 'n':
+ return '\n';
+ case 't':
+ return '\t';
+ default:
+ return in;
+ }
+}
+
+static int getHexDigit(char c)
+{
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ if(c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ if(c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+static int Sc_ScanString(int ret)
+{
+ BAStext[BASleng - 1] = 0;
+ if(!collapseEscape(BAStext+1))
+ return -1;
+ BASlval.str = (char *) consoleAlloc(dStrlen(BAStext));
+ dStrcpy(BASlval.str, BAStext + 1);
+ return(ret);
+}
+
+
+static int Sc_ScanNum()
+{
+ BAStext[BASleng] = 0;
+ BASlval.f = atof(BAStext);
+ return(FLTCONST);
+}
+
+static int Sc_ScanHex()
+{
+ int val = 0;
+ dSscanf(BAStext, "%x", &val);
+ BASlval.i = val;
+ return INTCONST;
+}
+
diff --git a/engine/console/BASscan.l b/engine/console/BASscan.l
new file mode 100755
index 0000000..1c27c23
--- /dev/null
+++ b/engine/console/BASscan.l
@@ -0,0 +1,289 @@
+%{
+#define YYLMAX 4096
+
+#include
+#include "platform/platform.h"
+#include "core/stringTable.h"
+#include "console/console.h"
+#define _CMDGRAM_H_
+#include "console/compiler.h"
+#include "console/basgram.h"
+
+using namespace Compiler;
+
+#define YY_NEVER_INTERACTIVE 1
+
+// Some basic parsing primitives...
+static int Sc_ScanString(int ret);
+static int Sc_ScanNum();
+static int Sc_ScanVar();
+static int Sc_ScanHex();
+
+// Deal with debuggability of FLEX.
+#ifdef TORQUE_DEBUG
+#define FLEX_DEBUG 1
+#else
+#define FLEX_DEBUG 0
+#endif
+
+//#undef input
+//#undef unput
+#undef BASgetc
+int BASgetc();
+
+#define YY_INPUT(buf,result,max_size) \
+{ \
+ int c = '*', n; \
+ for ( n = 0; n < max_size && \
+ (c = BASgetc()) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ result = n; \
+}
+
+static int lineIndex;
+
+// Prototypes
+void BASSetScanBuffer(const char *sb, const char *fn);
+void BASerror(char * s, ...);
+
+// Error reporting
+void CMDerror(char * s, ...);
+
+// Reset the parser.
+void CMDrestart(FILE *in);
+
+%}
+
+DIGIT [0-9]
+INTEGER {DIGIT}+
+FLOAT ({INTEGER}\.{INTEGER})|({INTEGER}(\.{INTEGER})?[eE][+-]?{INTEGER})
+LETTER [A-Za-z_]
+FILECHAR [A-Za-z_\.]
+VARMID [:A-Za-z0-9_]
+IDTAIL [A-Za-z0-9_]
+VARTAIL {VARMID}*{IDTAIL}
+VAR [$%]{LETTER}{VARTAIL}*
+ID {LETTER}{IDTAIL}*
+ILID [$%]{DIGIT}+{LETTER}{VARTAIL}*
+FILENAME {FILECHAR}+
+SPACE [ \t\v\f]
+HEXDIGIT [a-fA-F0-9]
+
+%%
+ ;
+{SPACE}+ { }
+"//"[^\n\r]* ;
+"rem"{SPACE}+[^\n\r]* ;
+[\r] ;
+[\n] { lineIndex++; }
+\"(\\.|[^\\"\n\r])*\" { return(Sc_ScanString(STRATOM)); }
+\'(\\.|[^\\'\n\r])*\' { return(Sc_ScanString(TAGATOM)); }
+"==" return(BASlval.i = opEQ);
+"!=" return(BASlval.i = opNE);
+">=" return(BASlval.i = opGE);
+"<=" return(BASlval.i = opLE);
+"&&" return(BASlval.i = opAND);
+"||" return(BASlval.i = opOR);
+"::" return(BASlval.i = opCOLONCOLON);
+"--" return(BASlval.i = opMINUSMINUS);
+"++" return(BASlval.i = opPLUSPLUS);
+"$=" return(BASlval.i = opSTREQ);
+"!$=" return(BASlval.i = opSTRNE);
+"<<" return(BASlval.i = opSHL);
+">>" return(BASlval.i = opSHR);
+"+=" return(BASlval.i = opPLASN);
+"-=" return(BASlval.i = opMIASN);
+"*=" return(BASlval.i = opMLASN);
+"/=" return(BASlval.i = opDVASN);
+"%=" return(BASlval.i = opMODASN);
+"&=" return(BASlval.i = opANDASN);
+"^=" return(BASlval.i = opXORASN);
+"|=" return(BASlval.i = opORASN);
+"<<=" return(BASlval.i = opSLASN);
+">>=" return(BASlval.i = opSRASN);
+"NL" {BASlval.i = '\n'; return '@'; }
+"TAB" {BASlval.i = '\t'; return '@'; }
+"SPC" {BASlval.i = ' '; return '@'; }
+"@" {BASlval.i = 0; return '@'; }
+"?" |
+"[" |
+"]" |
+"(" |
+")" |
+"+" |
+"-" |
+"*" |
+"/" |
+"<" |
+">" |
+"|" |
+"." |
+"!" |
+":" |
+";" |
+"{" |
+"}" |
+"," |
+"&" |
+"%" |
+"^" |
+"~" |
+"=" { return(BASlval.i = BAStext[0]); }
+"or" { BASlval.i = lineIndex; return(rwCASEOR); }
+"break" { BASlval.i = lineIndex; return(rwBREAK); }
+"return" { BASlval.i = lineIndex; return(rwRETURN); }
+"else" { BASlval.i = lineIndex; return(rwELSE); }
+"while" { BASlval.i = lineIndex; return(rwWHILE); }
+"if" { BASlval.i = lineIndex; return(rwIF); }
+"then" { BASlval.i = lineIndex; return(rwTHEN); }
+"do" { BASlval.i = lineIndex; return(rwBEGIN); }
+"begin" { BASlval.i = lineIndex; return(rwBEGIN); }
+"end" { BASlval.i = lineIndex; return(rwEND); }
+"for" { BASlval.i = lineIndex; return(rwFOR); }
+"cfor" { BASlval.i = lineIndex; return(rwCFOR); }
+"to" { BASlval.i = lineIndex; return(rwTO); }
+"step" { BASlval.i = lineIndex; return(rwSTEP); }
+"continue" { BASlval.i = lineIndex; return(rwCONTINUE); }
+"function" { BASlval.i = lineIndex; return(rwDEFINE); }
+"sub" { BASlval.i = lineIndex; return(rwDEFINE); }
+"new" { BASlval.i = lineIndex; return(rwDECLARE); }
+"datablock" { BASlval.i = lineIndex; return(rwDATABLOCK); }
+"case" { BASlval.i = lineIndex; return(rwCASE); }
+"switch$" { BASlval.i = lineIndex; return(rwSWITCHSTR); }
+"switch" { BASlval.i = lineIndex; return(rwSWITCH); }
+"default" { BASlval.i = lineIndex; return(rwDEFAULT); }
+"package" { BASlval.i = lineIndex; return(rwPACKAGE); }
+"true" { BASlval.i = 1; return INTCONST; }
+"false" { BASlval.i = 0; return INTCONST; }
+{VAR} return(Sc_ScanVar());
+{ID} { BAStext[BASleng] = 0; BASlval.s = StringTable->insert(BAStext); return(IDENT); }
+0[xX]{HEXDIGIT}+ return(Sc_ScanHex());
+{INTEGER} { BAStext[BASleng] = 0; BASlval.i = atoi(BAStext); return INTCONST; }
+{FLOAT} return Sc_ScanNum();
+{ILID} return(ILLEGAL_TOKEN);
+. return(ILLEGAL_TOKEN);
+%%
+
+/*
+ * Scan character constant.
+ */
+
+/*
+ * Scan identifier.
+ */
+
+static const char *scanBuffer;
+static const char *fileName;
+static int scanIndex;
+
+const char * BASGetCurrentFile()
+{
+ return fileName;
+}
+
+int BASGetCurrentLine()
+{
+ return lineIndex;
+}
+
+void BASerror(char *, ...)
+{
+ gSyntaxError = true;
+ if(fileName)
+ Con::errorf(ConsoleLogEntry::Script, "%s Line: %d - Syntax error.",
+ fileName, lineIndex);
+ else
+ Con::errorf(ConsoleLogEntry::Script, "Syntax error in input.");
+}
+
+void BASSetScanBuffer(const char *sb, const char *fn)
+{
+ scanBuffer = sb;
+ fileName = fn;
+ scanIndex = 0;
+ lineIndex = 1;
+}
+
+int BASgetc()
+{
+ int ret = scanBuffer[scanIndex];
+ if(ret)
+ scanIndex++;
+ else
+ ret = -1;
+ return ret;
+}
+
+int BASwrap()
+{
+ return 1;
+}
+
+static int Sc_ScanVar()
+{
+ BAStext[BASleng] = 0;
+ BASlval.s = StringTable->insert(BAStext);
+ return(VAR);
+}
+/*
+ * Scan string constant.
+ */
+
+// Defined in CS_CMD.l
+extern void expandEscape(char *dest, const char *src);
+extern bool collapseEscape(char *buf);
+
+static int charConv(int in)
+{
+ switch(in)
+ {
+ case 'r':
+ return '\r';
+ case 'n':
+ return '\n';
+ case 't':
+ return '\t';
+ default:
+ return in;
+ }
+}
+
+static int getHexDigit(char c)
+{
+ if(c >= '0' && c <= '9')
+ return c - '0';
+ if(c >= 'A' && c <= 'F')
+ return c - 'A' + 10;
+ if(c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return -1;
+}
+
+static int Sc_ScanString(int ret)
+{
+ BAStext[BASleng - 1] = 0;
+ if(!collapseEscape(BAStext+1))
+ return -1;
+ BASlval.str = (char *) consoleAlloc(dStrlen(BAStext));
+ dStrcpy(BASlval.str, BAStext + 1);
+ return(ret);
+}
+
+
+static int Sc_ScanNum()
+{
+ BAStext[BASleng] = 0;
+ BASlval.f = atof(BAStext);
+ return(FLTCONST);
+}
+
+static int Sc_ScanHex()
+{
+ int val = 0;
+ dSscanf(BAStext, "%x", &val);
+ BASlval.i = val;
+ return INTCONST;
+}
+
diff --git a/engine/console/CMDgram.cc b/engine/console/CMDgram.cc
new file mode 100755
index 0000000..b75d199
--- /dev/null
+++ b/engine/console/CMDgram.cc
@@ -0,0 +1,2140 @@
+
+/* A Bison parser, made from cmdgram.y with Bison version GNU Bison version 1.24
+ */
+
+#define YYBISON 1 /* Identify Bison output. */
+
+#define yyparse CMDparse
+#define yylex CMDlex
+#define yyerror CMDerror
+#define yylval CMDlval
+#define yychar CMDchar
+#define yydebug CMDdebug
+#define yynerrs CMDnerrs
+#define rwDEFINE 258
+#define rwENDDEF 259
+#define rwDECLARE 260
+#define rwBREAK 261
+#define rwELSE 262
+#define rwCONTINUE 263
+#define rwGLOBAL 264
+#define rwIF 265
+#define rwNIL 266
+#define rwRETURN 267
+#define rwWHILE 268
+#define rwDO 269
+#define rwENDIF 270
+#define rwENDWHILE 271
+#define rwENDFOR 272
+#define rwDEFAULT 273
+#define rwFOR 274
+#define rwDATABLOCK 275
+#define rwSWITCH 276
+#define rwCASE 277
+#define rwSWITCHSTR 278
+#define rwCASEOR 279
+#define rwPACKAGE 280
+#define rwNAMESPACE 281
+#define rwCLASS 282
+#define ILLEGAL_TOKEN 283
+#define CHRCONST 284
+#define INTCONST 285
+#define TTAG 286
+#define VAR 287
+#define IDENT 288
+#define STRATOM 289
+#define TAGATOM 290
+#define FLTCONST 291
+#define opMINUSMINUS 292
+#define opPLUSPLUS 293
+#define STMT_SEP 294
+#define opSHL 295
+#define opSHR 296
+#define opPLASN 297
+#define opMIASN 298
+#define opMLASN 299
+#define opDVASN 300
+#define opMODASN 301
+#define opANDASN 302
+#define opXORASN 303
+#define opORASN 304
+#define opSLASN 305
+#define opSRASN 306
+#define opCAT 307
+#define opEQ 308
+#define opNE 309
+#define opGE 310
+#define opLE 311
+#define opAND 312
+#define opOR 313
+#define opSTREQ 314
+#define opCOLONCOLON 315
+#define opMDASN 316
+#define opNDASN 317
+#define opNTASN 318
+#define opSTRNE 319
+#define UNARY 320
+
+#line 1 "cmdgram.y"
+
+
+// Make sure we don't get gram.h twice.
+#define _CMDGRAM_H_
+
+#include
+#include
+#include "console/console.h"
+#include "console/compiler.h"
+#include "console/consoleInternal.h"
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+#define YYSSIZE 350
+
+int outtext(char *fmt, ...);
+extern int serrors;
+
+#define nil 0
+#undef YY_ARGS
+#define YY_ARGS(x) x
+
+int CMDlex();
+void CMDerror(char *, ...);
+
+#define alloca dMalloc
+
+#line 31 "cmdgram.y"
+
+ /* Reserved Word Definitions */
+#line 41 "cmdgram.y"
+
+ /* Constants and Identifier Definitions */
+#line 53 "cmdgram.y"
+
+ /* Operator Definitions */
+
+#line 65 "cmdgram.y"
+typedef union {
+ char c;
+ int i;
+ const char * s;
+ char * str;
+ double f;
+ StmtNode * stmt;
+ ExprNode * expr;
+ SlotAssignNode * slist;
+ VarNode * var;
+ SlotDecl slot;
+ ObjectBlockDecl odcl;
+ ObjectDeclNode * od;
+ AssignDecl asn;
+ IfStmtNode * ifnode;
+} YYSTYPE;
+
+#ifndef YYLTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YYLTYPE yyltype
+#endif
+
+#include
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define YYFINAL 320
+#define YYFLAG -32768
+#define YYNTBASE 91
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 320 ? yytranslate[x] : 127)
+
+static const char yytranslate[] = { 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 57, 2, 2, 2, 47, 46, 2, 48,
+ 49, 39, 37, 50, 38, 44, 40, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 51, 52, 41,
+ 43, 42, 87, 58, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 83, 2, 90, 55, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 53, 45, 54, 56, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 1, 2, 3, 4, 5,
+ 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
+ 36, 59, 60, 61, 62, 63, 64, 65, 66, 67,
+ 68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
+ 78, 79, 80, 81, 82, 84, 85, 86, 88, 89
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = { 0,
+ 0, 2, 3, 6, 8, 10, 12, 19, 21, 24,
+ 25, 28, 30, 32, 34, 36, 38, 41, 44, 47,
+ 51, 54, 59, 66, 75, 86, 87, 89, 91, 95,
+ 106, 117, 125, 126, 129, 130, 132, 133, 136, 137,
+ 139, 141, 144, 147, 151, 155, 157, 165, 173, 178,
+ 186, 192, 194, 198, 204, 212, 218, 225, 235, 244,
+ 253, 261, 270, 278, 286, 293, 295, 297, 301, 305,
+ 309, 313, 317, 321, 325, 329, 333, 336, 339, 341,
+ 347, 351, 355, 359, 363, 367, 371, 375, 379, 383,
+ 387, 391, 395, 399, 402, 405, 407, 409, 411, 413,
+ 415, 417, 419, 421, 426, 430, 437, 439, 443, 445,
+ 447, 450, 453, 456, 459, 462, 465, 468, 471, 474,
+ 477, 479, 481, 485, 492, 495, 501, 504, 508, 514,
+ 519, 526, 533, 534, 536, 538, 542, 544, 547, 552,
+ 557, 565, 567
+};
+
+static const short yyrhs[] = { 92,
+ 0, 0, 92, 93, 0, 97, 0, 98, 0, 94,
+ 0, 25, 33, 53, 95, 54, 52, 0, 98, 0,
+ 95, 98, 0, 0, 96, 97, 0, 112, 0, 113,
+ 0, 114, 0, 101, 0, 109, 0, 6, 52, 0,
+ 8, 52, 0, 12, 52, 0, 12, 116, 52, 0,
+ 115, 52, 0, 31, 43, 116, 52, 0, 31, 43,
+ 116, 50, 116, 52, 0, 3, 33, 48, 99, 49,
+ 53, 96, 54, 0, 3, 33, 82, 33, 48, 99,
+ 49, 53, 96, 54, 0, 0, 100, 0, 32, 0,
+ 100, 50, 32, 0, 20, 33, 48, 33, 103, 49,
+ 53, 124, 54, 52, 0, 5, 118, 48, 104, 103,
+ 105, 49, 53, 106, 54, 0, 5, 118, 48, 104,
+ 103, 105, 49, 0, 0, 51, 33, 0, 0, 116,
+ 0, 0, 50, 123, 0, 0, 124, 0, 107, 0,
+ 124, 107, 0, 102, 52, 0, 107, 102, 52, 0,
+ 53, 96, 54, 0, 97, 0, 21, 48, 116, 49,
+ 53, 110, 54, 0, 23, 48, 116, 49, 53, 110,
+ 54, 0, 22, 111, 51, 96, 0, 22, 111, 51,
+ 96, 18, 51, 96, 0, 22, 111, 51, 96, 110,
+ 0, 116, 0, 111, 24, 116, 0, 10, 48, 116,
+ 49, 108, 0, 10, 48, 116, 49, 108, 7, 108,
+ 0, 13, 48, 116, 49, 108, 0, 14, 108, 13,
+ 48, 116, 49, 0, 19, 48, 116, 52, 116, 52,
+ 116, 49, 108, 0, 19, 48, 116, 52, 116, 52,
+ 49, 108, 0, 19, 48, 116, 52, 52, 116, 49,
+ 108, 0, 19, 48, 116, 52, 52, 49, 108, 0,
+ 19, 48, 52, 116, 52, 116, 49, 108, 0, 19,
+ 48, 52, 116, 52, 49, 108, 0, 19, 48, 52,
+ 52, 116, 49, 108, 0, 19, 48, 52, 52, 49,
+ 108, 0, 120, 0, 120, 0, 48, 116, 49, 0,
+ 116, 55, 116, 0, 116, 47, 116, 0, 116, 46,
+ 116, 0, 116, 45, 116, 0, 116, 37, 116, 0,
+ 116, 38, 116, 0, 116, 39, 116, 0, 116, 40,
+ 116, 0, 38, 116, 0, 39, 116, 0, 31, 0,
+ 116, 87, 116, 51, 116, 0, 116, 41, 116, 0,
+ 116, 42, 116, 0, 116, 77, 116, 0, 116, 78,
+ 116, 0, 116, 75, 116, 0, 116, 76, 116, 0,
+ 116, 80, 116, 0, 116, 62, 116, 0, 116, 63,
+ 116, 0, 116, 79, 116, 0, 116, 81, 116, 0,
+ 116, 88, 116, 0, 116, 58, 116, 0, 57, 116,
+ 0, 56, 116, 0, 35, 0, 36, 0, 30, 0,
+ 6, 0, 117, 0, 33, 0, 34, 0, 32, 0,
+ 32, 83, 126, 90, 0, 116, 44, 33, 0, 116,
+ 44, 33, 83, 126, 90, 0, 33, 0, 48, 116,
+ 49, 0, 60, 0, 59, 0, 64, 116, 0, 65,
+ 116, 0, 66, 116, 0, 67, 116, 0, 68, 116,
+ 0, 69, 116, 0, 70, 116, 0, 71, 116, 0,
+ 72, 116, 0, 73, 116, 0, 121, 0, 102, 0,
+ 32, 43, 116, 0, 32, 83, 126, 90, 43, 116,
+ 0, 32, 119, 0, 32, 83, 126, 90, 119, 0,
+ 117, 119, 0, 117, 43, 116, 0, 117, 43, 53,
+ 123, 54, 0, 33, 48, 122, 49, 0, 33, 82,
+ 33, 48, 122, 49, 0, 116, 44, 33, 48, 122,
+ 49, 0, 0, 123, 0, 116, 0, 123, 50, 116,
+ 0, 125, 0, 124, 125, 0, 33, 43, 116, 52,
+ 0, 20, 43, 116, 52, 0, 33, 83, 126, 90,
+ 43, 116, 52, 0, 116, 0, 126, 50, 116, 0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+ 138, 143, 145, 150, 152, 154, 159, 164, 166, 171,
+ 173, 178, 179, 180, 181, 182, 183, 185, 187, 189,
+ 191, 193, 195, 200, 202, 207, 209, 214, 216, 221,
+ 226, 228, 233, 235, 240, 242, 247, 249, 254, 256,
+ 258, 260, 265, 267, 272, 274, 279, 281, 286, 288,
+ 290, 295, 297, 302, 304, 309, 311, 316, 318, 320,
+ 322, 324, 326, 328, 330, 335, 340, 342, 344, 346,
+ 348, 350, 352, 354, 356, 358, 360, 362, 364, 366,
+ 368, 370, 372, 374, 376, 378, 380, 382, 384, 386,
+ 388, 390, 392, 394, 396, 398, 400, 402, 404, 406,
+ 408, 410, 412, 414, 419, 421, 426, 428, 433, 435,
+ 437, 439, 441, 443, 445, 447, 449, 451, 453, 455,
+ 460, 462, 464, 466, 468, 470, 472, 474, 476, 481,
+ 483, 485, 490, 492, 497, 499, 504, 506, 511, 513,
+ 515, 520, 522
+};
+
+static const char * const yytname[] = { "$","error","$undefined.","rwDEFINE",
+"rwENDDEF","rwDECLARE","rwBREAK","rwELSE","rwCONTINUE","rwGLOBAL","rwIF","rwNIL",
+"rwRETURN","rwWHILE","rwDO","rwENDIF","rwENDWHILE","rwENDFOR","rwDEFAULT","rwFOR",
+"rwDATABLOCK","rwSWITCH","rwCASE","rwSWITCHSTR","rwCASEOR","rwPACKAGE","rwNAMESPACE",
+"rwCLASS","ILLEGAL_TOKEN","CHRCONST","INTCONST","TTAG","VAR","IDENT","STRATOM",
+"TAGATOM","FLTCONST","'+'","'-'","'*'","'/'","'<'","'>'","'='","'.'","'|'","'&'",
+"'%'","'('","')'","','","':'","';'","'{'","'}'","'^'","'~'","'!'","'@'","opMINUSMINUS",
+"opPLUSPLUS","STMT_SEP","opSHL","opSHR","opPLASN","opMIASN","opMLASN","opDVASN",
+"opMODASN","opANDASN","opXORASN","opORASN","opSLASN","opSRASN","opCAT","opEQ",
+"opNE","opGE","opLE","opAND","opOR","opSTREQ","opCOLONCOLON","'['","opMDASN",
+"opNDASN","opNTASN","'?'","opSTRNE","UNARY","']'","start","decl_list","decl",
+"package_decl","fn_decl_list","statement_list","stmt","fn_decl_stmt","var_list_decl",
+"var_list","datablock_decl","object_decl","parent_block","object_name","object_args",
+"object_declare_block","object_decl_list","stmt_block","switch_stmt","case_block",
+"case_expr","if_stmt","while_stmt","for_stmt","expression_stmt","expr","slot_acc",
+"class_name_expr","assign_op_struct","stmt_expr","funcall_expr","expr_list_decl",
+"expr_list","slot_assign_list","slot_assign","aidx_expr",""
+};
+#endif
+
+static const short yyr1[] = { 0,
+ 91, 92, 92, 93, 93, 93, 94, 95, 95, 96,
+ 96, 97, 97, 97, 97, 97, 97, 97, 97, 97,
+ 97, 97, 97, 98, 98, 99, 99, 100, 100, 101,
+ 102, 102, 103, 103, 104, 104, 105, 105, 106, 106,
+ 106, 106, 107, 107, 108, 108, 109, 109, 110, 110,
+ 110, 111, 111, 112, 112, 113, 113, 114, 114, 114,
+ 114, 114, 114, 114, 114, 115, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
+ 116, 116, 116, 116, 116, 116, 116, 116, 116, 116,
+ 116, 116, 116, 116, 117, 117, 118, 118, 119, 119,
+ 119, 119, 119, 119, 119, 119, 119, 119, 119, 119,
+ 120, 120, 120, 120, 120, 120, 120, 120, 120, 121,
+ 121, 121, 122, 122, 123, 123, 124, 124, 125, 125,
+ 125, 126, 126
+};
+
+static const short yyr2[] = { 0,
+ 1, 0, 2, 1, 1, 1, 6, 1, 2, 0,
+ 2, 1, 1, 1, 1, 1, 2, 2, 2, 3,
+ 2, 4, 6, 8, 10, 0, 1, 1, 3, 10,
+ 10, 7, 0, 2, 0, 1, 0, 2, 0, 1,
+ 1, 2, 2, 3, 3, 1, 7, 7, 4, 7,
+ 5, 1, 3, 5, 7, 5, 6, 9, 8, 8,
+ 7, 8, 7, 7, 6, 1, 1, 3, 3, 3,
+ 3, 3, 3, 3, 3, 3, 2, 2, 1, 5,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 3, 3, 3, 2, 2, 1, 1, 1, 1, 1,
+ 1, 1, 1, 4, 3, 6, 1, 3, 1, 1,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 3, 6, 2, 5, 2, 3, 5, 4,
+ 6, 6, 0, 1, 1, 3, 1, 2, 4, 4,
+ 7, 1, 3
+};
+
+static const short yydefact[] = { 2,
+ 1, 0, 0, 99, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 98, 79, 103, 101, 102, 96,
+ 97, 0, 0, 0, 0, 0, 3, 6, 4, 5,
+ 15, 122, 16, 12, 13, 14, 0, 0, 100, 67,
+ 121, 0, 107, 0, 0, 17, 18, 0, 99, 79,
+ 19, 0, 67, 0, 10, 46, 0, 0, 0, 0,
+ 0, 0, 0, 0, 110, 109, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 125, 133, 0,
+ 77, 78, 0, 95, 94, 21, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 127, 26, 0, 0, 35, 0, 20, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 123, 111, 112,
+ 113, 114, 115, 116, 117, 118, 119, 120, 142, 0,
+ 135, 0, 134, 0, 68, 73, 74, 75, 76, 81,
+ 82, 105, 72, 71, 70, 69, 93, 88, 89, 85,
+ 86, 83, 84, 90, 87, 91, 0, 92, 0, 128,
+ 28, 0, 27, 0, 108, 33, 36, 0, 0, 45,
+ 11, 0, 0, 0, 0, 33, 0, 0, 0, 8,
+ 0, 22, 0, 104, 130, 0, 133, 133, 0, 0,
+ 0, 0, 0, 26, 0, 37, 54, 56, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 9, 0,
+ 143, 0, 126, 136, 0, 0, 0, 80, 129, 10,
+ 29, 0, 34, 0, 0, 0, 57, 65, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 7, 23,
+ 124, 131, 132, 106, 0, 0, 38, 32, 55, 64,
+ 63, 0, 61, 0, 0, 0, 0, 0, 52, 47,
+ 48, 24, 10, 39, 62, 60, 59, 0, 0, 0,
+ 0, 137, 0, 10, 0, 0, 0, 41, 40, 58,
+ 0, 0, 0, 0, 138, 53, 49, 25, 43, 31,
+ 0, 42, 0, 0, 0, 30, 0, 51, 44, 140,
+ 139, 0, 10, 0, 50, 0, 141, 0, 0, 0
+};
+
+static const short yydefgoto[] = { 318,
+ 1, 27, 28, 189, 119, 56, 30, 172, 173, 31,
+ 32, 206, 176, 235, 287, 288, 57, 33, 247, 268,
+ 34, 35, 36, 37, 38, 39, 45, 78, 53, 41,
+ 142, 143, 281, 282, 140
+};
+
+static const short yypact[] = {-32768,
+ 333, -17, -16, -34, -27, -10, 154, 23, 413, 27,
+ -12, 31, 33, 51,-32768, 42, 2137, -9,-32768,-32768,
+-32768, 854, 854, 854, 854, 854,-32768,-32768,-32768,-32768,
+-32768,-32768,-32768,-32768,-32768,-32768, 35, 1942, 102, 37,
+-32768, -8,-32768, 854, 38,-32768,-32768, 854,-32768,-32768,
+-32768, 927,-32768, 854,-32768,-32768, 78, 618, 48, 854,
+ 854, 47, 854, 854,-32768,-32768, 854, 854, 854, 854,
+ 854, 854, 854, 854, 854, 854, 854,-32768, 854, 79,
+ 71, 71, 979, 71, 71,-32768, 854, 854, 854, 854,
+ 854, 854, 83, 854, 854, 854, 854, 854, 854, 854,
+ 854, 854, 854, 854, 854, 854, 854, 854, 854, 629,
+-32768, 85, 87, 1031, 854, 1083,-32768, 1135, 466, 100,
+ 666, 1187, 94, 1239, 1291, 147, 875, 1942, 1942, 1942,
+ 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, 1942, -44,
+ 1942, 103, 106, 105,-32768, 107, 107, 71, 71, 2131,
+ 2131, -38, 2023, 2077, 71, 2050, 160, 213, 213, 2104,
+ 2104, 2131, 2131, 1996, -11, 160, 1343, 160, 854, 1942,
+-32768, 109, 113, 116,-32768, 114, 1942, 413, 413,-32768,
+-32768, 854, 701, 1395, 736, 114, 123, 126, 8,-32768,
+ 854,-32768, 854, 222,-32768, 854, 854, 854, 854, 854,
+ -31, 128, 150, 85, 158, 133, 189,-32768, 1447, 413,
+ 1499, 747, 784, 1551, 152, 183, 183, 161,-32768, 1603,
+ 1942, 854,-32768, 1942, 163, 165, -36, 1969,-32768,-32768,
+-32768, 166,-32768, 854, 169, 413,-32768,-32768, 413, 413,
+ 1655, 413, 1707, 819, 167, 854, 170, 172,-32768,-32768,
+ 1942,-32768,-32768,-32768, 505, 168, 106, 175,-32768,-32768,
+-32768, 413,-32768, 413, 413, 1759, 4, -2, 1942,-32768,
+-32768,-32768,-32768, 43,-32768,-32768,-32768, 413, 186, -30,
+ 60,-32768, 854,-32768, 544, 184, 188, 232, 43,-32768,
+ 854, 854, 854, 191,-32768, 1942, 374,-32768,-32768,-32768,
+ 196, 232, 1811, 1863, -35,-32768, 198,-32768,-32768,-32768,
+-32768, 197,-32768, 854, 583, 1915,-32768, 258, 259,-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,-32768,-32768,-32768, -223, 0, -117, 58,-32768,-32768,
+ -94, 80,-32768,-32768,-32768, -22, 66,-32768, -214,-32768,
+-32768,-32768,-32768,-32768, 34,-32768,-32768, -37, -1,-32768,
+ -155, -165, -5, -269, -194
+};
+
+
+#define YYLAST 2220
+
+
+static const short yytable[] = { 40,
+ 29, 111, 248, 201, 227, 193, 255, 40, 190, 198,
+ 2, 295, 292, 193, 193, 42, 43, 46, 196, 295,
+ 59, 283, 229, 279, 47, 87, 88, 89, 90, 91,
+ 92, 44, 93, 94, 95, 96, 280, 48, 79, 112,
+ 52, 225, 226, 97, 199, 194, 98, 3, 284, 285,
+ 99, 100, 293, 254, 312, 81, 82, 83, 84, 85,
+ 297, 218, 279, 101, 102, 103, 104, 105, 257, 107,
+ 54, 219, 80, 113, 58, 280, 109, 114, 60, 279,
+ 61, 116, 308, 62, 63, 115, 86, 118, -66, 315,
+ 120, 122, 280, 124, 125, 123, 127, 128, 305, 126,
+ 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+ 139, 144, 141, 294, 93, 152, 171, 40, 181, 174,
+ 146, 147, 148, 149, 150, 151, 186, 153, 154, 155,
+ 156, 157, 158, 159, 160, 161, 162, 163, 164, 165,
+ 166, 167, 168, 170, 110, 89, 90, 182, 177, 2,
+ 93, 195, 197, 96, 184, 196, 223, 202, 3, 49,
+ 65, 66, 203, 204, 205, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 216, 40, 40, 217, 286,
+ 230, 231, 234, 15, 50, 17, 18, 19, 20, 21,
+ 233, 22, 23, 301, 286, 236, 87, 88, 89, 90,
+ 245, 24, 141, 93, 246, 51, 96, 301, 40, 25,
+ 26, 252, 249, 253, 256, 209, 211, 258, 214, 267,
+ 273, 99, 100, 270, 220, 271, 221, 274, 291, 224,
+ 141, 141, 139, 228, 40, 299, 3, 40, 40, 314,
+ 40, 300, 306, 207, 208, 241, 243, 309, 313, 87,
+ 88, 89, 90, 40, 181, 251, 93, 319, 320, 96,
+ 40, 232, 40, 40, 222, 215, 302, 141, 289, 0,
+ 0, 0, 0, 0, 0, 238, 40, 266, 0, 269,
+ 65, 66, 0, 40, 181, 67, 68, 69, 70, 71,
+ 72, 73, 74, 75, 76, 40, 181, 0, 0, 0,
+ 0, 259, 0, 0, 260, 261, 0, 263, 0, 0,
+ 0, 0, 0, 40, 181, 0, 296, 0, 0, 0,
+ 0, 0, 0, 0, 303, 304, 139, 275, 0, 276,
+ 277, 0, 0, 0, 0, 2, 0, 3, 4, 0,
+ 5, 0, 6, 290, 7, 8, 9, 316, 0, 0,
+ 0, 10, 11, 12, 0, 13, 0, 14, 0, 0,
+ 0, 0, 15, 16, 17, 18, 19, 20, 21, 0,
+ 22, 23, 0, 0, 0, 0, 0, 0, 3, 4,
+ 24, 5, 0, 6, 0, 7, 8, 9, 25, 26,
+ 0, 307, 10, 11, 12, 246, 13, 0, 0, 0,
+ 0, 0, 0, 15, 16, 17, 18, 19, 20, 21,
+ 0, 22, 23, 0, 0, 0, 0, 3, 4, 0,
+ 5, 24, 6, 0, 7, 8, 9, 0, 0, 25,
+ 26, 10, 11, 12, 0, 13, 0, 0, 0, 0,
+ 0, 0, 15, 16, 17, 18, 19, 20, 21, 0,
+ 22, 23, 0, 0, 0, 0, 0, 0, 0, 0,
+ 24, 0, 0, 0, 0, 55, 0, 0, 25, 26,
+ 3, 4, 0, 5, 0, 6, 0, 7, 8, 9,
+ 0, 0, 0, 0, 10, 11, 12, 0, 13, 0,
+ 0, 0, 0, 0, 0, 15, 16, 17, 18, 19,
+ 20, 21, 0, 22, 23, 0, 0, 0, 0, 3,
+ 4, 0, 5, 24, 6, 0, 7, 8, 9, 180,
+ 0, 25, 26, 10, 11, 12, 0, 13, 0, 0,
+ 0, 0, 0, 0, 15, 16, 17, 18, 19, 20,
+ 21, 0, 22, 23, 0, 0, 0, 0, 3, 4,
+ 0, 5, 24, 6, 0, 7, 8, 9, 272, 0,
+ 25, 26, 10, 11, 12, 0, 13, 0, 0, 0,
+ 0, 0, 0, 15, 16, 17, 18, 19, 20, 21,
+ 0, 22, 23, 0, 0, 0, 0, 3, 4, 0,
+ 5, 24, 6, 0, 7, 8, 9, 298, 0, 25,
+ 26, 10, 11, 12, 0, 13, 0, 0, 0, 0,
+ 0, 0, 15, 16, 17, 18, 19, 20, 21, 0,
+ 22, 23, 3, 49, 0, 0, 0, 0, 0, 0,
+ 24, 0, 0, 3, 49, 0, 0, 0, 25, 26,
+ 0, 0, 0, 0, 0, 0, 0, 15, 50, 17,
+ 18, 19, 20, 21, 0, 22, 23, 0, 15, 50,
+ 17, 18, 19, 20, 21, 24, 22, 23, 0, 121,
+ 3, 49, 0, 25, 26, 0, 24, 0, 0, 0,
+ 0, 169, 0, 0, 25, 26, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 15, 50, 17, 18, 19,
+ 20, 21, 0, 22, 23, 3, 49, 0, 0, 0,
+ 0, 0, 0, 24, 0, 0, 0, 183, 0, 0,
+ 0, 25, 26, 0, 0, 0, 0, 0, 0, 0,
+ 15, 50, 17, 18, 19, 20, 21, 0, 22, 23,
+ 3, 49, 0, 0, 0, 0, 0, 0, 24, 210,
+ 0, 3, 49, 0, 0, 0, 25, 26, 0, 0,
+ 0, 0, 0, 0, 0, 15, 50, 17, 18, 19,
+ 20, 21, 0, 22, 23, 0, 15, 50, 17, 18,
+ 19, 20, 21, 24, 22, 23, 0, 213, 3, 49,
+ 0, 25, 26, 0, 24, 240, 0, 0, 0, 0,
+ 0, 0, 25, 26, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 15, 50, 17, 18, 19, 20, 21,
+ 0, 22, 23, 3, 49, 0, 0, 0, 0, 0,
+ 0, 24, 242, 0, 0, 0, 0, 0, 0, 25,
+ 26, 0, 0, 0, 0, 0, 0, 0, 15, 50,
+ 17, 18, 19, 20, 21, 0, 22, 23, 3, 49,
+ 0, 0, 0, 0, 0, 0, 24, 265, 0, 0,
+ 0, 0, 0, 0, 25, 26, 0, 0, 0, 0,
+ 0, 0, 0, 15, 50, 17, 18, 19, 20, 21,
+ 0, 22, 23, 0, 0, 0, 0, 0, 0, 0,
+ 0, 24, 0, 0, 0, 0, 0, 0, 0, 25,
+ 26, 87, 88, 89, 90, 91, 92, 0, 93, 94,
+ 95, 96, 0, 0, 191, 0, 192, 0, 0, 97,
+ 0, 0, 98, 0, 0, 0, 99, 100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 101,
+ 102, 103, 104, 105, 106, 107, 0, 0, 0, 0,
+ 0, 108, 109, 87, 88, 89, 90, 91, 92, 0,
+ 93, 94, 95, 96, 0, 0, 0, 0, 117, 0,
+ 0, 97, 0, 0, 98, 0, 0, 0, 99, 100,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 101, 102, 103, 104, 105, 106, 107, 0, 0,
+ 0, 0, 0, 108, 109, 87, 88, 89, 90, 91,
+ 92, 0, 93, 94, 95, 96, 0, 145, 0, 0,
+ 0, 0, 0, 97, 0, 0, 98, 0, 0, 0,
+ 99, 100, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 101, 102, 103, 104, 105, 106, 107,
+ 0, 0, 0, 0, 0, 108, 109, 87, 88, 89,
+ 90, 91, 92, 0, 93, 94, 95, 96, 0, 175,
+ 0, 0, 0, 0, 0, 97, 0, 0, 98, 0,
+ 0, 0, 99, 100, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 101, 102, 103, 104, 105,
+ 106, 107, 0, 0, 0, 0, 0, 108, 109, 87,
+ 88, 89, 90, 91, 92, 0, 93, 94, 95, 96,
+ 0, 178, 0, 0, 0, 0, 0, 97, 0, 0,
+ 98, 0, 0, 0, 99, 100, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 101, 102, 103,
+ 104, 105, 106, 107, 0, 0, 0, 0, 0, 108,
+ 109, 87, 88, 89, 90, 91, 92, 0, 93, 94,
+ 95, 96, 0, 179, 0, 0, 0, 0, 0, 97,
+ 0, 0, 98, 0, 0, 0, 99, 100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 101,
+ 102, 103, 104, 105, 106, 107, 0, 0, 0, 0,
+ 0, 108, 109, 87, 88, 89, 90, 91, 92, 0,
+ 93, 94, 95, 96, 0, 0, 0, 0, 185, 0,
+ 0, 97, 0, 0, 98, 0, 0, 0, 99, 100,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 101, 102, 103, 104, 105, 106, 107, 0, 0,
+ 0, 0, 0, 108, 109, 87, 88, 89, 90, 91,
+ 92, 0, 93, 94, 95, 96, 0, 187, 0, 0,
+ 0, 0, 0, 97, 0, 0, 98, 0, 0, 0,
+ 99, 100, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 101, 102, 103, 104, 105, 106, 107,
+ 0, 0, 0, 0, 0, 108, 109, 87, 88, 89,
+ 90, 91, 92, 0, 93, 94, 95, 96, 0, 188,
+ 0, 0, 0, 0, 0, 97, 0, 0, 98, 0,
+ 0, 0, 99, 100, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 101, 102, 103, 104, 105,
+ 106, 107, 0, 0, 0, 0, 0, 108, 109, 87,
+ 88, 89, 90, 91, 92, 0, 93, 94, 95, 96,
+ 0, 0, 0, 200, 0, 0, 0, 97, 0, 0,
+ 98, 0, 0, 0, 99, 100, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 101, 102, 103,
+ 104, 105, 106, 107, 0, 0, 0, 0, 0, 108,
+ 109, 87, 88, 89, 90, 91, 92, 0, 93, 94,
+ 95, 96, 0, 0, 0, 0, 212, 0, 0, 97,
+ 0, 0, 98, 0, 0, 0, 99, 100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 101,
+ 102, 103, 104, 105, 106, 107, 0, 0, 0, 0,
+ 0, 108, 109, 87, 88, 89, 90, 91, 92, 0,
+ 93, 94, 95, 96, 0, 237, 0, 0, 0, 0,
+ 0, 97, 0, 0, 98, 0, 0, 0, 99, 100,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 101, 102, 103, 104, 105, 106, 107, 0, 0,
+ 0, 0, 0, 108, 109, 87, 88, 89, 90, 91,
+ 92, 0, 93, 94, 95, 96, 0, 239, 0, 0,
+ 0, 0, 0, 97, 0, 0, 98, 0, 0, 0,
+ 99, 100, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 101, 102, 103, 104, 105, 106, 107,
+ 0, 0, 0, 0, 0, 108, 109, 87, 88, 89,
+ 90, 91, 92, 0, 93, 94, 95, 96, 0, 0,
+ 0, 0, 244, 0, 0, 97, 0, 0, 98, 0,
+ 0, 0, 99, 100, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 101, 102, 103, 104, 105,
+ 106, 107, 0, 0, 0, 0, 0, 108, 109, 87,
+ 88, 89, 90, 91, 92, 0, 93, 94, 95, 96,
+ 0, 0, 0, 0, 250, 0, 0, 97, 0, 0,
+ 98, 0, 0, 0, 99, 100, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 101, 102, 103,
+ 104, 105, 106, 107, 0, 0, 0, 0, 0, 108,
+ 109, 87, 88, 89, 90, 91, 92, 0, 93, 94,
+ 95, 96, 0, 262, 0, 0, 0, 0, 0, 97,
+ 0, 0, 98, 0, 0, 0, 99, 100, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 101,
+ 102, 103, 104, 105, 106, 107, 0, 0, 0, 0,
+ 0, 108, 109, 87, 88, 89, 90, 91, 92, 0,
+ 93, 94, 95, 96, 0, 264, 0, 0, 0, 0,
+ 0, 97, 0, 0, 98, 0, 0, 0, 99, 100,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 101, 102, 103, 104, 105, 106, 107, 0, 0,
+ 0, 0, 0, 108, 109, 87, 88, 89, 90, 91,
+ 92, 0, 93, 94, 95, 96, 0, 278, 0, 0,
+ 0, 0, 0, 97, 0, 0, 98, 0, 0, 0,
+ 99, 100, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 101, 102, 103, 104, 105, 106, 107,
+ 0, 0, 0, 0, 0, 108, 109, 87, 88, 89,
+ 90, 91, 92, 0, 93, 94, 95, 96, 0, 0,
+ 0, 0, 310, 0, 0, 97, 0, 0, 98, 0,
+ 0, 0, 99, 100, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 101, 102, 103, 104, 105,
+ 106, 107, 0, 0, 0, 0, 0, 108, 109, 87,
+ 88, 89, 90, 91, 92, 0, 93, 94, 95, 96,
+ 0, 0, 0, 0, 311, 0, 0, 97, 0, 0,
+ 98, 0, 0, 0, 99, 100, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 101, 102, 103,
+ 104, 105, 106, 107, 0, 0, 0, 0, 0, 108,
+ 109, 87, 88, 89, 90, 91, 92, 0, 93, 94,
+ 95, 96, 0, 0, 0, 0, 317, 0, 0, 97,
+ 0, 0, 98, 0, 0, 0, 99, 100, 87, 88,
+ 89, 90, 91, 92, 0, 93, 94, 95, 96, 101,
+ 102, 103, 104, 105, 106, 107, 97, 0, 0, 98,
+ 0, 108, 109, 99, 100, 87, 88, 89, 90, 91,
+ 92, 0, 93, 94, 95, 96, 101, 102, 103, 104,
+ 105, 106, 107, 97, 0, 0, 98, 0, 108, 109,
+ 99, 100, 87, 88, 89, 90, 91, 92, 0, 93,
+ 94, 95, 96, 101, 102, 103, 104, 105, 106, 107,
+ 97, 0, 0, 98, 0, 0, 109, 99, 100, 87,
+ 88, 89, 90, 91, 92, 0, 93, 0, 95, 96,
+ 101, 102, 103, 104, 0, 0, 107, 97, 0, 0,
+ 98, 0, 0, 109, 99, 100, 87, 88, 89, 90,
+ 91, 92, 0, 93, 0, 95, 96, 101, 102, 103,
+ 104, 0, 0, 107, 0, 0, 0, 98, 0, 0,
+ 109, 99, 100, 87, 88, 89, 90, 91, 92, 0,
+ 93, 0, 0, 96, 101, 102, 103, 104, 0, 0,
+ 107, 0, 0, 0, 98, 0, 0, 109, 99, 100,
+ 87, 88, 89, 90, 91, 92, 0, 93, 0, 0,
+ 96, 101, 102, 103, 104, 0, 0, 107, 0, 0,
+ 0, 98, 0, 0, 109, 99, 100, 87, 88, 89,
+ 90, 0, 0, 0, 93, 0, 0, 96, 0, 64,
+ 103, 104, 0, 0, 107, 0, 0, 0, 98, 0,
+ 0, 109, 99, 100, 0, 65, 66, 0, 0, 0,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 0, 107, 0, 0, 0, 0, 0, 0, 109, 77
+};
+
+static const short yycheck[] = { 1,
+ 1, 39, 217, 169, 199, 50, 230, 9, 126, 48,
+ 3, 281, 43, 50, 50, 33, 33, 52, 50, 289,
+ 33, 24, 54, 20, 52, 37, 38, 39, 40, 41,
+ 42, 48, 44, 45, 46, 47, 33, 48, 48, 48,
+ 7, 197, 198, 55, 83, 90, 58, 5, 51, 273,
+ 62, 63, 83, 90, 90, 22, 23, 24, 25, 26,
+ 284, 54, 20, 75, 76, 77, 78, 79, 234, 81,
+ 48, 189, 82, 82, 48, 33, 88, 44, 48, 20,
+ 48, 48, 297, 33, 43, 48, 52, 54, 52, 313,
+ 13, 58, 33, 60, 61, 48, 63, 64, 293, 53,
+ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76,
+ 77, 33, 79, 54, 44, 33, 32, 119, 119, 33,
+ 87, 88, 89, 90, 91, 92, 33, 94, 95, 96,
+ 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
+ 107, 108, 109, 110, 43, 39, 40, 48, 115, 3,
+ 44, 49, 48, 47, 121, 50, 194, 49, 5, 6,
+ 59, 60, 50, 48, 51, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 53, 178, 179, 53, 274,
+ 53, 32, 50, 30, 31, 32, 33, 34, 35, 36,
+ 33, 38, 39, 288, 289, 7, 37, 38, 39, 40,
+ 49, 48, 169, 44, 22, 52, 47, 302, 210, 56,
+ 57, 49, 52, 49, 49, 182, 183, 49, 185, 53,
+ 53, 62, 63, 54, 191, 54, 193, 53, 43, 196,
+ 197, 198, 199, 200, 236, 52, 5, 239, 240, 43,
+ 242, 54, 52, 178, 179, 212, 213, 52, 51, 37,
+ 38, 39, 40, 255, 255, 222, 44, 0, 0, 47,
+ 262, 204, 264, 265, 43, 186, 289, 234, 274, -1,
+ -1, -1, -1, -1, -1, 210, 278, 244, -1, 246,
+ 59, 60, -1, 285, 285, 64, 65, 66, 67, 68,
+ 69, 70, 71, 72, 73, 297, 297, -1, -1, -1,
+ -1, 236, -1, -1, 239, 240, -1, 242, -1, -1,
+ -1, -1, -1, 315, 315, -1, 283, -1, -1, -1,
+ -1, -1, -1, -1, 291, 292, 293, 262, -1, 264,
+ 265, -1, -1, -1, -1, 3, -1, 5, 6, -1,
+ 8, -1, 10, 278, 12, 13, 14, 314, -1, -1,
+ -1, 19, 20, 21, -1, 23, -1, 25, -1, -1,
+ -1, -1, 30, 31, 32, 33, 34, 35, 36, -1,
+ 38, 39, -1, -1, -1, -1, -1, -1, 5, 6,
+ 48, 8, -1, 10, -1, 12, 13, 14, 56, 57,
+ -1, 18, 19, 20, 21, 22, 23, -1, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, 38, 39, -1, -1, -1, -1, 5, 6, -1,
+ 8, 48, 10, -1, 12, 13, 14, -1, -1, 56,
+ 57, 19, 20, 21, -1, 23, -1, -1, -1, -1,
+ -1, -1, 30, 31, 32, 33, 34, 35, 36, -1,
+ 38, 39, -1, -1, -1, -1, -1, -1, -1, -1,
+ 48, -1, -1, -1, -1, 53, -1, -1, 56, 57,
+ 5, 6, -1, 8, -1, 10, -1, 12, 13, 14,
+ -1, -1, -1, -1, 19, 20, 21, -1, 23, -1,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, 38, 39, -1, -1, -1, -1, 5,
+ 6, -1, 8, 48, 10, -1, 12, 13, 14, 54,
+ -1, 56, 57, 19, 20, 21, -1, 23, -1, -1,
+ -1, -1, -1, -1, 30, 31, 32, 33, 34, 35,
+ 36, -1, 38, 39, -1, -1, -1, -1, 5, 6,
+ -1, 8, 48, 10, -1, 12, 13, 14, 54, -1,
+ 56, 57, 19, 20, 21, -1, 23, -1, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, 38, 39, -1, -1, -1, -1, 5, 6, -1,
+ 8, 48, 10, -1, 12, 13, 14, 54, -1, 56,
+ 57, 19, 20, 21, -1, 23, -1, -1, -1, -1,
+ -1, -1, 30, 31, 32, 33, 34, 35, 36, -1,
+ 38, 39, 5, 6, -1, -1, -1, -1, -1, -1,
+ 48, -1, -1, 5, 6, -1, -1, -1, 56, 57,
+ -1, -1, -1, -1, -1, -1, -1, 30, 31, 32,
+ 33, 34, 35, 36, -1, 38, 39, -1, 30, 31,
+ 32, 33, 34, 35, 36, 48, 38, 39, -1, 52,
+ 5, 6, -1, 56, 57, -1, 48, -1, -1, -1,
+ -1, 53, -1, -1, 56, 57, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, 38, 39, 5, 6, -1, -1, -1,
+ -1, -1, -1, 48, -1, -1, -1, 52, -1, -1,
+ -1, 56, 57, -1, -1, -1, -1, -1, -1, -1,
+ 30, 31, 32, 33, 34, 35, 36, -1, 38, 39,
+ 5, 6, -1, -1, -1, -1, -1, -1, 48, 49,
+ -1, 5, 6, -1, -1, -1, 56, 57, -1, -1,
+ -1, -1, -1, -1, -1, 30, 31, 32, 33, 34,
+ 35, 36, -1, 38, 39, -1, 30, 31, 32, 33,
+ 34, 35, 36, 48, 38, 39, -1, 52, 5, 6,
+ -1, 56, 57, -1, 48, 49, -1, -1, -1, -1,
+ -1, -1, 56, 57, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, 38, 39, 5, 6, -1, -1, -1, -1, -1,
+ -1, 48, 49, -1, -1, -1, -1, -1, -1, 56,
+ 57, -1, -1, -1, -1, -1, -1, -1, 30, 31,
+ 32, 33, 34, 35, 36, -1, 38, 39, 5, 6,
+ -1, -1, -1, -1, -1, -1, 48, 49, -1, -1,
+ -1, -1, -1, -1, 56, 57, -1, -1, -1, -1,
+ -1, -1, -1, 30, 31, 32, 33, 34, 35, 36,
+ -1, 38, 39, -1, -1, -1, -1, -1, -1, -1,
+ -1, 48, -1, -1, -1, -1, -1, -1, -1, 56,
+ 57, 37, 38, 39, 40, 41, 42, -1, 44, 45,
+ 46, 47, -1, -1, 50, -1, 52, -1, -1, 55,
+ -1, -1, 58, -1, -1, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 75,
+ 76, 77, 78, 79, 80, 81, -1, -1, -1, -1,
+ -1, 87, 88, 37, 38, 39, 40, 41, 42, -1,
+ 44, 45, 46, 47, -1, -1, -1, -1, 52, -1,
+ -1, 55, -1, -1, 58, -1, -1, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 75, 76, 77, 78, 79, 80, 81, -1, -1,
+ -1, -1, -1, 87, 88, 37, 38, 39, 40, 41,
+ 42, -1, 44, 45, 46, 47, -1, 49, -1, -1,
+ -1, -1, -1, 55, -1, -1, 58, -1, -1, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 75, 76, 77, 78, 79, 80, 81,
+ -1, -1, -1, -1, -1, 87, 88, 37, 38, 39,
+ 40, 41, 42, -1, 44, 45, 46, 47, -1, 49,
+ -1, -1, -1, -1, -1, 55, -1, -1, 58, -1,
+ -1, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 75, 76, 77, 78, 79,
+ 80, 81, -1, -1, -1, -1, -1, 87, 88, 37,
+ 38, 39, 40, 41, 42, -1, 44, 45, 46, 47,
+ -1, 49, -1, -1, -1, -1, -1, 55, -1, -1,
+ 58, -1, -1, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 75, 76, 77,
+ 78, 79, 80, 81, -1, -1, -1, -1, -1, 87,
+ 88, 37, 38, 39, 40, 41, 42, -1, 44, 45,
+ 46, 47, -1, 49, -1, -1, -1, -1, -1, 55,
+ -1, -1, 58, -1, -1, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 75,
+ 76, 77, 78, 79, 80, 81, -1, -1, -1, -1,
+ -1, 87, 88, 37, 38, 39, 40, 41, 42, -1,
+ 44, 45, 46, 47, -1, -1, -1, -1, 52, -1,
+ -1, 55, -1, -1, 58, -1, -1, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 75, 76, 77, 78, 79, 80, 81, -1, -1,
+ -1, -1, -1, 87, 88, 37, 38, 39, 40, 41,
+ 42, -1, 44, 45, 46, 47, -1, 49, -1, -1,
+ -1, -1, -1, 55, -1, -1, 58, -1, -1, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 75, 76, 77, 78, 79, 80, 81,
+ -1, -1, -1, -1, -1, 87, 88, 37, 38, 39,
+ 40, 41, 42, -1, 44, 45, 46, 47, -1, 49,
+ -1, -1, -1, -1, -1, 55, -1, -1, 58, -1,
+ -1, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 75, 76, 77, 78, 79,
+ 80, 81, -1, -1, -1, -1, -1, 87, 88, 37,
+ 38, 39, 40, 41, 42, -1, 44, 45, 46, 47,
+ -1, -1, -1, 51, -1, -1, -1, 55, -1, -1,
+ 58, -1, -1, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 75, 76, 77,
+ 78, 79, 80, 81, -1, -1, -1, -1, -1, 87,
+ 88, 37, 38, 39, 40, 41, 42, -1, 44, 45,
+ 46, 47, -1, -1, -1, -1, 52, -1, -1, 55,
+ -1, -1, 58, -1, -1, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 75,
+ 76, 77, 78, 79, 80, 81, -1, -1, -1, -1,
+ -1, 87, 88, 37, 38, 39, 40, 41, 42, -1,
+ 44, 45, 46, 47, -1, 49, -1, -1, -1, -1,
+ -1, 55, -1, -1, 58, -1, -1, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 75, 76, 77, 78, 79, 80, 81, -1, -1,
+ -1, -1, -1, 87, 88, 37, 38, 39, 40, 41,
+ 42, -1, 44, 45, 46, 47, -1, 49, -1, -1,
+ -1, -1, -1, 55, -1, -1, 58, -1, -1, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 75, 76, 77, 78, 79, 80, 81,
+ -1, -1, -1, -1, -1, 87, 88, 37, 38, 39,
+ 40, 41, 42, -1, 44, 45, 46, 47, -1, -1,
+ -1, -1, 52, -1, -1, 55, -1, -1, 58, -1,
+ -1, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 75, 76, 77, 78, 79,
+ 80, 81, -1, -1, -1, -1, -1, 87, 88, 37,
+ 38, 39, 40, 41, 42, -1, 44, 45, 46, 47,
+ -1, -1, -1, -1, 52, -1, -1, 55, -1, -1,
+ 58, -1, -1, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 75, 76, 77,
+ 78, 79, 80, 81, -1, -1, -1, -1, -1, 87,
+ 88, 37, 38, 39, 40, 41, 42, -1, 44, 45,
+ 46, 47, -1, 49, -1, -1, -1, -1, -1, 55,
+ -1, -1, 58, -1, -1, -1, 62, 63, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, 75,
+ 76, 77, 78, 79, 80, 81, -1, -1, -1, -1,
+ -1, 87, 88, 37, 38, 39, 40, 41, 42, -1,
+ 44, 45, 46, 47, -1, 49, -1, -1, -1, -1,
+ -1, 55, -1, -1, 58, -1, -1, -1, 62, 63,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 75, 76, 77, 78, 79, 80, 81, -1, -1,
+ -1, -1, -1, 87, 88, 37, 38, 39, 40, 41,
+ 42, -1, 44, 45, 46, 47, -1, 49, -1, -1,
+ -1, -1, -1, 55, -1, -1, 58, -1, -1, -1,
+ 62, 63, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 75, 76, 77, 78, 79, 80, 81,
+ -1, -1, -1, -1, -1, 87, 88, 37, 38, 39,
+ 40, 41, 42, -1, 44, 45, 46, 47, -1, -1,
+ -1, -1, 52, -1, -1, 55, -1, -1, 58, -1,
+ -1, -1, 62, 63, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, 75, 76, 77, 78, 79,
+ 80, 81, -1, -1, -1, -1, -1, 87, 88, 37,
+ 38, 39, 40, 41, 42, -1, 44, 45, 46, 47,
+ -1, -1, -1, -1, 52, -1, -1, 55, -1, -1,
+ 58, -1, -1, -1, 62, 63, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, 75, 76, 77,
+ 78, 79, 80, 81, -1, -1, -1, -1, -1, 87,
+ 88, 37, 38, 39, 40, 41, 42, -1, 44, 45,
+ 46, 47, -1, -1, -1, -1, 52, -1, -1, 55,
+ -1, -1, 58, -1, -1, -1, 62, 63, 37, 38,
+ 39, 40, 41, 42, -1, 44, 45, 46, 47, 75,
+ 76, 77, 78, 79, 80, 81, 55, -1, -1, 58,
+ -1, 87, 88, 62, 63, 37, 38, 39, 40, 41,
+ 42, -1, 44, 45, 46, 47, 75, 76, 77, 78,
+ 79, 80, 81, 55, -1, -1, 58, -1, 87, 88,
+ 62, 63, 37, 38, 39, 40, 41, 42, -1, 44,
+ 45, 46, 47, 75, 76, 77, 78, 79, 80, 81,
+ 55, -1, -1, 58, -1, -1, 88, 62, 63, 37,
+ 38, 39, 40, 41, 42, -1, 44, -1, 46, 47,
+ 75, 76, 77, 78, -1, -1, 81, 55, -1, -1,
+ 58, -1, -1, 88, 62, 63, 37, 38, 39, 40,
+ 41, 42, -1, 44, -1, 46, 47, 75, 76, 77,
+ 78, -1, -1, 81, -1, -1, -1, 58, -1, -1,
+ 88, 62, 63, 37, 38, 39, 40, 41, 42, -1,
+ 44, -1, -1, 47, 75, 76, 77, 78, -1, -1,
+ 81, -1, -1, -1, 58, -1, -1, 88, 62, 63,
+ 37, 38, 39, 40, 41, 42, -1, 44, -1, -1,
+ 47, 75, 76, 77, 78, -1, -1, 81, -1, -1,
+ -1, 58, -1, -1, 88, 62, 63, 37, 38, 39,
+ 40, -1, -1, -1, 44, -1, -1, 47, -1, 43,
+ 77, 78, -1, -1, 81, -1, -1, -1, 58, -1,
+ -1, 88, 62, 63, -1, 59, 60, -1, -1, -1,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73,
+ -1, 81, -1, -1, -1, -1, -1, -1, 88, 83
+};
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+#line 3 "bison.simple"
+
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+ 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, 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. */
+
+/* As a special exception, when this file is copied by Bison into a
+ Bison output file, you may use that output file without restriction.
+ This special exception was added by the Free Software Foundation
+ in version 1.24 of Bison. */
+
+#ifndef alloca
+#ifdef __GNUC__
+#define alloca __builtin_alloca
+#else /* not GNU C. */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include
+#else /* not sparc */
+#if defined (MSDOS) && !defined (__TURBOC__)
+#include
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+#include
+ #pragma alloca
+#else /* not MSDOS, __TURBOC__, or _AIX */
+#ifdef __hpux
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+#endif /* __hpux */
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc. */
+#endif /* not GNU C. */
+#endif /* alloca not defined. */
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: there must be only one dollar sign in this file.
+ It is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (yychar = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT return(0)
+#define YYABORT return(1)
+#define YYERROR goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL goto yyerrlab
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do \
+ if (yychar == YYEMPTY && yylen == 1) \
+ { yychar = (token), yylval = (value); \
+ yychar1 = YYTRANSLATE (yychar); \
+ YYPOPSTACK; \
+ goto yybackup; \
+ } \
+ else \
+ { yyerror ("syntax error: cannot back up"); YYERROR; } \
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YYPURE
+#define YYLEX yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int yychar; /* the lookahead symbol */
+YYSTYPE yylval; /* the semantic value of the */
+ /* lookahead symbol */
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc; /* location data for the lookahead */
+ /* symbol */
+#endif
+
+int yynerrs; /* number of parse errors so far */
+#endif /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+/* Prevent warning if -Wstrict-prototypes. */
+#ifdef __GNUC__
+int yyparse (void);
+#endif
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_memcpy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+static void
+__yy_memcpy (char *from, char *to, int count)
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+
+#endif
+#endif
+
+#line 192 "bison.simple"
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+ into yyparse. The argument should have type void *.
+ It should actually point to an object.
+ Grammar actions can access the variable by casting it
+ to the proper pointer type. */
+
+#ifdef YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#else
+#define YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#endif
+
+int
+yyparse(YYPARSE_PARAM)
+ YYPARSE_PARAM_DECL
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YYSTYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1 = 0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YYSTYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YYSTYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YYLTYPE *yyls = yylsa;
+ YYLTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YYPURE
+ int yychar;
+ YYSTYPE yylval;
+ int yynerrs;
+#ifdef YYLSP_NEEDED
+ YYLTYPE yylloc;
+#endif
+#endif
+
+ YYSTYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Starting parse\n");
+#endif
+
+ yystate = 0;
+ yyerrstatus = 0;
+ yynerrs = 0;
+ yychar = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+yynewstate:
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YYSTYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+ YYLTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YYLSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ yyerror("parser stack overflow");
+ return 2;
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) alloca (yystacksize * sizeof (*yyssp));
+ __yy_memcpy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ yyvs = (YYSTYPE *) alloca (yystacksize * sizeof (*yyvsp));
+ __yy_memcpy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+ yyls = (YYLTYPE *) alloca (yystacksize * sizeof (*yylsp));
+ __yy_memcpy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* yyresume: */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yydefault;
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (yychar == YYEMPTY)
+ {
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ yychar = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (yychar <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ yychar = YYEOF; /* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, yychar, yylval);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ goto yydefault;
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrlab;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrlab;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (yychar != YYEOF)
+ yychar = YYEMPTY;
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ goto yynewstate;
+
+/* Do the default action for the current state. */
+yydefault:
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ goto yyerrlab;
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+yyreduce:
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+
+ switch (yyn) {
+
+case 1:
+#line 139 "cmdgram.y"
+{ ;
+ break;}
+case 2:
+#line 144 "cmdgram.y"
+{ yyval.stmt = nil; ;
+ break;}
+case 3:
+#line 146 "cmdgram.y"
+{ if(!statementList) { statementList = yyvsp[0].stmt; } else { statementList->append(yyvsp[0].stmt); } ;
+ break;}
+case 4:
+#line 151 "cmdgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 5:
+#line 153 "cmdgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 6:
+#line 155 "cmdgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 7:
+#line 160 "cmdgram.y"
+{ yyval.stmt = yyvsp[-2].stmt; for(StmtNode *walk = (yyvsp[-2].stmt);walk;walk = walk->getNext() ) walk->setPackage(yyvsp[-4].s); ;
+ break;}
+case 8:
+#line 165 "cmdgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 9:
+#line 167 "cmdgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; (yyvsp[-1].stmt)->append(yyvsp[0].stmt); ;
+ break;}
+case 10:
+#line 172 "cmdgram.y"
+{ yyval.stmt = nil; ;
+ break;}
+case 11:
+#line 174 "cmdgram.y"
+{ if(!yyvsp[-1].stmt) { yyval.stmt = yyvsp[0].stmt; } else { (yyvsp[-1].stmt)->append(yyvsp[0].stmt); yyval.stmt = yyvsp[-1].stmt; } ;
+ break;}
+case 17:
+#line 184 "cmdgram.y"
+{ yyval.stmt = BreakStmtNode::alloc(); ;
+ break;}
+case 18:
+#line 186 "cmdgram.y"
+{ yyval.stmt = ContinueStmtNode::alloc(); ;
+ break;}
+case 19:
+#line 188 "cmdgram.y"
+{ yyval.stmt = ReturnStmtNode::alloc(NULL); ;
+ break;}
+case 20:
+#line 190 "cmdgram.y"
+{ yyval.stmt = ReturnStmtNode::alloc(yyvsp[-1].expr); ;
+ break;}
+case 21:
+#line 192 "cmdgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; ;
+ break;}
+case 22:
+#line 194 "cmdgram.y"
+{ yyval.stmt = TTagSetStmtNode::alloc(yyvsp[-3].s, yyvsp[-1].expr, NULL); ;
+ break;}
+case 23:
+#line 196 "cmdgram.y"
+{ yyval.stmt = TTagSetStmtNode::alloc(yyvsp[-5].s, yyvsp[-3].expr, yyvsp[-1].expr); ;
+ break;}
+case 24:
+#line 201 "cmdgram.y"
+{ yyval.stmt = FunctionDeclStmtNode::alloc(yyvsp[-6].s, NULL, yyvsp[-4].var, yyvsp[-1].stmt); ;
+ break;}
+case 25:
+#line 203 "cmdgram.y"
+{ yyval.stmt = FunctionDeclStmtNode::alloc(yyvsp[-6].s, yyvsp[-8].s, yyvsp[-4].var, yyvsp[-1].stmt); ;
+ break;}
+case 26:
+#line 208 "cmdgram.y"
+{ yyval.var = NULL; ;
+ break;}
+case 27:
+#line 210 "cmdgram.y"
+{ yyval.var = yyvsp[0].var; ;
+ break;}
+case 28:
+#line 215 "cmdgram.y"
+{ yyval.var = VarNode::alloc(yyvsp[0].s, NULL); ;
+ break;}
+case 29:
+#line 217 "cmdgram.y"
+{ yyval.var = yyvsp[-2].var; ((StmtNode*)(yyvsp[-2].var))->append((StmtNode*)VarNode::alloc(yyvsp[0].s, NULL)); ;
+ break;}
+case 30:
+#line 222 "cmdgram.y"
+{ yyval.stmt = ObjectDeclNode::alloc(ConstantNode::alloc(yyvsp[-8].s), ConstantNode::alloc(yyvsp[-6].s), NULL, yyvsp[-5].s, yyvsp[-2].slist, NULL, true); ;
+ break;}
+case 31:
+#line 227 "cmdgram.y"
+{ yyval.od = ObjectDeclNode::alloc(yyvsp[-8].expr, yyvsp[-6].expr, yyvsp[-4].expr, yyvsp[-5].s, yyvsp[-1].odcl.slots, yyvsp[-1].odcl.decls, false); ;
+ break;}
+case 32:
+#line 229 "cmdgram.y"
+{ yyval.od = ObjectDeclNode::alloc(yyvsp[-5].expr, yyvsp[-3].expr, yyvsp[-1].expr, yyvsp[-2].s, NULL, NULL, false); ;
+ break;}
+case 33:
+#line 234 "cmdgram.y"
+{ yyval.s = NULL; ;
+ break;}
+case 34:
+#line 236 "cmdgram.y"
+{ yyval.s = yyvsp[0].s; ;
+ break;}
+case 35:
+#line 241 "cmdgram.y"
+{ yyval.expr = StrConstNode::alloc("", false); ;
+ break;}
+case 36:
+#line 243 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 37:
+#line 248 "cmdgram.y"
+{ yyval.expr = NULL; ;
+ break;}
+case 38:
+#line 250 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 39:
+#line 255 "cmdgram.y"
+{ yyval.odcl.slots = NULL; yyval.odcl.decls = NULL; ;
+ break;}
+case 40:
+#line 257 "cmdgram.y"
+{ yyval.odcl.slots = yyvsp[0].slist; yyval.odcl.decls = NULL; ;
+ break;}
+case 41:
+#line 259 "cmdgram.y"
+{ yyval.odcl.slots = NULL; yyval.odcl.decls = yyvsp[0].od; ;
+ break;}
+case 42:
+#line 261 "cmdgram.y"
+{ yyval.odcl.slots = yyvsp[-1].slist; yyval.odcl.decls = yyvsp[0].od; ;
+ break;}
+case 43:
+#line 266 "cmdgram.y"
+{ yyval.od = yyvsp[-1].od; ;
+ break;}
+case 44:
+#line 268 "cmdgram.y"
+{ yyvsp[-2].od->append(yyvsp[-1].od); yyval.od = yyvsp[-2].od; ;
+ break;}
+case 45:
+#line 273 "cmdgram.y"
+{ yyval.stmt = yyvsp[-1].stmt; ;
+ break;}
+case 46:
+#line 275 "cmdgram.y"
+{ yyval.stmt = yyvsp[0].stmt; ;
+ break;}
+case 47:
+#line 280 "cmdgram.y"
+{ yyval.stmt = yyvsp[-1].ifnode; yyvsp[-1].ifnode->propagateSwitchExpr(yyvsp[-4].expr, false); ;
+ break;}
+case 48:
+#line 282 "cmdgram.y"
+{ yyval.stmt = yyvsp[-1].ifnode; yyvsp[-1].ifnode->propagateSwitchExpr(yyvsp[-4].expr, true); ;
+ break;}
+case 49:
+#line 287 "cmdgram.y"
+{ yyval.ifnode = IfStmtNode::alloc(yyvsp[-3].i, yyvsp[-2].expr, yyvsp[0].stmt, NULL, false); ;
+ break;}
+case 50:
+#line 289 "cmdgram.y"
+{ yyval.ifnode = IfStmtNode::alloc(yyvsp[-6].i, yyvsp[-5].expr, yyvsp[-3].stmt, yyvsp[0].stmt, false); ;
+ break;}
+case 51:
+#line 291 "cmdgram.y"
+{ yyval.ifnode = IfStmtNode::alloc(yyvsp[-4].i, yyvsp[-3].expr, yyvsp[-1].stmt, yyvsp[0].ifnode, true); ;
+ break;}
+case 52:
+#line 296 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr;;
+ break;}
+case 53:
+#line 298 "cmdgram.y"
+{ (yyvsp[-2].expr)->append(yyvsp[0].expr); yyval.expr=yyvsp[-2].expr; ;
+ break;}
+case 54:
+#line 303 "cmdgram.y"
+{ yyval.stmt = IfStmtNode::alloc(yyvsp[-4].i, yyvsp[-2].expr, yyvsp[0].stmt, NULL, false); ;
+ break;}
+case 55:
+#line 305 "cmdgram.y"
+{ yyval.stmt = IfStmtNode::alloc(yyvsp[-6].i, yyvsp[-4].expr, yyvsp[-2].stmt, yyvsp[0].stmt, false); ;
+ break;}
+case 56:
+#line 310 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-4].i, nil, yyvsp[-2].expr, nil, yyvsp[0].stmt, false); ;
+ break;}
+case 57:
+#line 312 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-3].i, nil, yyvsp[-1].expr, nil, yyvsp[-4].stmt, true); ;
+ break;}
+case 58:
+#line 317 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-8].i, yyvsp[-6].expr, yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].stmt, false); ;
+ break;}
+case 59:
+#line 319 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-7].i, yyvsp[-5].expr, yyvsp[-3].expr, NULL, yyvsp[0].stmt, false); ;
+ break;}
+case 60:
+#line 321 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-7].i, yyvsp[-5].expr, NULL, yyvsp[-2].expr, yyvsp[0].stmt, false); ;
+ break;}
+case 61:
+#line 323 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-6].i, yyvsp[-4].expr, NULL, NULL, yyvsp[0].stmt, false); ;
+ break;}
+case 62:
+#line 325 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-7].i, NULL, yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].stmt, false); ;
+ break;}
+case 63:
+#line 327 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-6].i, NULL, yyvsp[-3].expr, NULL, yyvsp[0].stmt, false); ;
+ break;}
+case 64:
+#line 329 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-6].i, NULL, NULL, yyvsp[-2].expr, yyvsp[0].stmt, false); ;
+ break;}
+case 65:
+#line 331 "cmdgram.y"
+{ yyval.stmt = LoopStmtNode::alloc(yyvsp[-5].i, NULL, NULL, NULL, yyvsp[0].stmt, false); ;
+ break;}
+case 66:
+#line 336 "cmdgram.y"
+{ yyval.stmt = yyvsp[0].expr; ;
+ break;}
+case 67:
+#line 341 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 68:
+#line 343 "cmdgram.y"
+{ yyval.expr = yyvsp[-1].expr; ;
+ break;}
+case 69:
+#line 345 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 70:
+#line 347 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 71:
+#line 349 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 72:
+#line 351 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 73:
+#line 353 "cmdgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 74:
+#line 355 "cmdgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 75:
+#line 357 "cmdgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 76:
+#line 359 "cmdgram.y"
+{ yyval.expr = FloatBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 77:
+#line 361 "cmdgram.y"
+{ yyval.expr = FloatUnaryExprNode::alloc(yyvsp[-1].i, yyvsp[0].expr); ;
+ break;}
+case 78:
+#line 363 "cmdgram.y"
+{ yyval.expr = TTagDerefNode::alloc(yyvsp[0].expr); ;
+ break;}
+case 79:
+#line 365 "cmdgram.y"
+{ yyval.expr = TTagExprNode::alloc(yyvsp[0].s); ;
+ break;}
+case 80:
+#line 367 "cmdgram.y"
+{ yyval.expr = ConditionalExprNode::alloc(yyvsp[-4].expr, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 81:
+#line 369 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 82:
+#line 371 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 83:
+#line 373 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 84:
+#line 375 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 85:
+#line 377 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 86:
+#line 379 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 87:
+#line 381 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 88:
+#line 383 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 89:
+#line 385 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 90:
+#line 387 "cmdgram.y"
+{ yyval.expr = IntBinaryExprNode::alloc(yyvsp[-1].i, yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+case 91:
+#line 389 "cmdgram.y"
+{ yyval.expr = StreqExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr, true); ;
+ break;}
+case 92:
+#line 391 "cmdgram.y"
+{ yyval.expr = StreqExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr, false); ;
+ break;}
+case 93:
+#line 393 "cmdgram.y"
+{ yyval.expr = StrcatExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr, yyvsp[-1].i); ;
+ break;}
+case 94:
+#line 395 "cmdgram.y"
+{ yyval.expr = IntUnaryExprNode::alloc(yyvsp[-1].i, yyvsp[0].expr); ;
+ break;}
+case 95:
+#line 397 "cmdgram.y"
+{ yyval.expr = IntUnaryExprNode::alloc(yyvsp[-1].i, yyvsp[0].expr); ;
+ break;}
+case 96:
+#line 399 "cmdgram.y"
+{ yyval.expr = StrConstNode::alloc(yyvsp[0].str, true); ;
+ break;}
+case 97:
+#line 401 "cmdgram.y"
+{ yyval.expr = FloatNode::alloc(yyvsp[0].f); ;
+ break;}
+case 98:
+#line 403 "cmdgram.y"
+{ yyval.expr = IntNode::alloc(yyvsp[0].i); ;
+ break;}
+case 99:
+#line 405 "cmdgram.y"
+{ yyval.expr = ConstantNode::alloc(StringTable->insert("break")); ;
+ break;}
+case 100:
+#line 407 "cmdgram.y"
+{ yyval.expr = SlotAccessNode::alloc(yyvsp[0].slot.object, yyvsp[0].slot.array, yyvsp[0].slot.slotName); ;
+ break;}
+case 101:
+#line 409 "cmdgram.y"
+{ yyval.expr = ConstantNode::alloc(yyvsp[0].s); ;
+ break;}
+case 102:
+#line 411 "cmdgram.y"
+{ yyval.expr = StrConstNode::alloc(yyvsp[0].str, false); ;
+ break;}
+case 103:
+#line 413 "cmdgram.y"
+{ yyval.expr = (ExprNode*)VarNode::alloc(yyvsp[0].s, NULL); ;
+ break;}
+case 104:
+#line 415 "cmdgram.y"
+{ yyval.expr = (ExprNode*)VarNode::alloc(yyvsp[-3].s, yyvsp[-1].expr); ;
+ break;}
+case 105:
+#line 420 "cmdgram.y"
+{ yyval.slot.object = yyvsp[-2].expr; yyval.slot.slotName = yyvsp[0].s; yyval.slot.array = NULL; ;
+ break;}
+case 106:
+#line 422 "cmdgram.y"
+{ yyval.slot.object = yyvsp[-5].expr; yyval.slot.slotName = yyvsp[-3].s; yyval.slot.array = yyvsp[-1].expr; ;
+ break;}
+case 107:
+#line 427 "cmdgram.y"
+{ yyval.expr = ConstantNode::alloc(yyvsp[0].s); ;
+ break;}
+case 108:
+#line 429 "cmdgram.y"
+{ yyval.expr = yyvsp[-1].expr; ;
+ break;}
+case 109:
+#line 434 "cmdgram.y"
+{ yyval.asn.token = '+'; yyval.asn.expr = FloatNode::alloc(1); ;
+ break;}
+case 110:
+#line 436 "cmdgram.y"
+{ yyval.asn.token = '-'; yyval.asn.expr = FloatNode::alloc(1); ;
+ break;}
+case 111:
+#line 438 "cmdgram.y"
+{ yyval.asn.token = '+'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 112:
+#line 440 "cmdgram.y"
+{ yyval.asn.token = '-'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 113:
+#line 442 "cmdgram.y"
+{ yyval.asn.token = '*'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 114:
+#line 444 "cmdgram.y"
+{ yyval.asn.token = '/'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 115:
+#line 446 "cmdgram.y"
+{ yyval.asn.token = '%'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 116:
+#line 448 "cmdgram.y"
+{ yyval.asn.token = '&'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 117:
+#line 450 "cmdgram.y"
+{ yyval.asn.token = '^'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 118:
+#line 452 "cmdgram.y"
+{ yyval.asn.token = '|'; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 119:
+#line 454 "cmdgram.y"
+{ yyval.asn.token = opSHL; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 120:
+#line 456 "cmdgram.y"
+{ yyval.asn.token = opSHR; yyval.asn.expr = yyvsp[0].expr; ;
+ break;}
+case 121:
+#line 461 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 122:
+#line 463 "cmdgram.y"
+{ yyval.expr = yyvsp[0].od; ;
+ break;}
+case 123:
+#line 465 "cmdgram.y"
+{ yyval.expr = AssignExprNode::alloc(yyvsp[-2].s, NULL, yyvsp[0].expr); ;
+ break;}
+case 124:
+#line 467 "cmdgram.y"
+{ yyval.expr = AssignExprNode::alloc(yyvsp[-5].s, yyvsp[-3].expr, yyvsp[0].expr); ;
+ break;}
+case 125:
+#line 469 "cmdgram.y"
+{ yyval.expr = AssignOpExprNode::alloc(yyvsp[-1].s, NULL, yyvsp[0].asn.expr, yyvsp[0].asn.token); ;
+ break;}
+case 126:
+#line 471 "cmdgram.y"
+{ yyval.expr = AssignOpExprNode::alloc(yyvsp[-4].s, yyvsp[-2].expr, yyvsp[0].asn.expr, yyvsp[0].asn.token); ;
+ break;}
+case 127:
+#line 473 "cmdgram.y"
+{ yyval.expr = SlotAssignOpNode::alloc(yyvsp[-1].slot.object, yyvsp[-1].slot.slotName, yyvsp[-1].slot.array, yyvsp[0].asn.token, yyvsp[0].asn.expr); ;
+ break;}
+case 128:
+#line 475 "cmdgram.y"
+{ yyval.expr = SlotAssignNode::alloc(yyvsp[-2].slot.object, yyvsp[-2].slot.array, yyvsp[-2].slot.slotName, yyvsp[0].expr); ;
+ break;}
+case 129:
+#line 477 "cmdgram.y"
+{ yyval.expr = SlotAssignNode::alloc(yyvsp[-4].slot.object, yyvsp[-4].slot.array, yyvsp[-4].slot.slotName, yyvsp[-1].expr); ;
+ break;}
+case 130:
+#line 482 "cmdgram.y"
+{ yyval.expr = FuncCallExprNode::alloc(yyvsp[-3].s, NULL, yyvsp[-1].expr, false); ;
+ break;}
+case 131:
+#line 484 "cmdgram.y"
+{ yyval.expr = FuncCallExprNode::alloc(yyvsp[-3].s, yyvsp[-5].s, yyvsp[-1].expr, false); ;
+ break;}
+case 132:
+#line 486 "cmdgram.y"
+{ yyvsp[-5].expr->append(yyvsp[-1].expr); yyval.expr = FuncCallExprNode::alloc(yyvsp[-3].s, NULL, yyvsp[-5].expr, true); ;
+ break;}
+case 133:
+#line 491 "cmdgram.y"
+{ yyval.expr = NULL; ;
+ break;}
+case 134:
+#line 493 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 135:
+#line 498 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 136:
+#line 500 "cmdgram.y"
+{ (yyvsp[-2].expr)->append(yyvsp[0].expr); yyval.expr = yyvsp[-2].expr; ;
+ break;}
+case 137:
+#line 505 "cmdgram.y"
+{ yyval.slist = yyvsp[0].slist; ;
+ break;}
+case 138:
+#line 507 "cmdgram.y"
+{ yyvsp[-1].slist->append(yyvsp[0].slist); yyval.slist = yyvsp[-1].slist; ;
+ break;}
+case 139:
+#line 512 "cmdgram.y"
+{ yyval.slist = SlotAssignNode::alloc(NULL, NULL, yyvsp[-3].s, yyvsp[-1].expr); ;
+ break;}
+case 140:
+#line 514 "cmdgram.y"
+{ yyval.slist = SlotAssignNode::alloc(NULL, NULL, StringTable->insert("datablock"), yyvsp[-1].expr); ;
+ break;}
+case 141:
+#line 516 "cmdgram.y"
+{ yyval.slist = SlotAssignNode::alloc(NULL, yyvsp[-4].expr, yyvsp[-6].s, yyvsp[-1].expr); ;
+ break;}
+case 142:
+#line 521 "cmdgram.y"
+{ yyval.expr = yyvsp[0].expr; ;
+ break;}
+case 143:
+#line 523 "cmdgram.y"
+{ yyval.expr = CommaCatExprNode::alloc(yyvsp[-2].expr, yyvsp[0].expr); ;
+ break;}
+}
+ /* the action file gets copied in in place of this dollarsign */
+#line 487 "bison.simple"
+
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ yylsp->first_line = yylloc.first_line;
+ yylsp->first_column = yylloc.first_column;
+ yylsp->last_line = (yylsp-1)->last_line;
+ yylsp->last_column = (yylsp-1)->last_column;
+ yylsp->text = 0;
+ }
+ else
+ {
+ yylsp->last_line = (yylsp+yylen-1)->last_line;
+ yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ goto yynewstate;
+
+yyerrlab: /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ yyerror(msg);
+ free(msg);
+ }
+ else
+ yyerror ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YYERROR_VERBOSE */
+ yyerror("parse error");
+ }
+
+ goto yyerrlab1;
+yyerrlab1: /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (yychar == YYEOF)
+ YYABORT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+ yychar = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ goto yyerrhandle;
+
+yyerrdefault: /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) goto yydefault;
+#endif
+
+yyerrpop: /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+ yylsp--;
+#endif
+
+#if YYDEBUG != 0
+ if (yydebug)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+yyerrhandle:
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ goto yyerrdefault;
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ goto yyerrdefault;
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ goto yyerrpop;
+ yyn = -yyn;
+ goto yyreduce;
+ }
+ else if (yyn == 0)
+ goto yyerrpop;
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YYDEBUG != 0
+ if (yydebug)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+ *++yylsp = yylloc;
+#endif
+
+ yystate = yyn;
+ goto yynewstate;
+}
+#line 525 "cmdgram.y"
+
+
diff --git a/engine/console/CMDgram.y b/engine/console/CMDgram.y
new file mode 100755
index 0000000..9e85b18
--- /dev/null
+++ b/engine/console/CMDgram.y
@@ -0,0 +1,526 @@
+%{
+
+// Make sure we don't get gram.h twice.
+#define _CMDGRAM_H_
+
+#include
+#include
+#include "console/console.h"
+#include "console/compiler.h"
+#include "console/consoleInternal.h"
+
+#ifndef YYDEBUG
+#define YYDEBUG 0
+#endif
+
+#define YYSSIZE 350
+
+int outtext(char *fmt, ...);
+extern int serrors;
+
+#define nil 0
+#undef YY_ARGS
+#define YY_ARGS(x) x
+
+int CMDlex();
+void CMDerror(char *, ...);
+
+#define alloca dMalloc
+
+%}
+%{
+ /* Reserved Word Definitions */
+%}
+%token rwDEFINE rwENDDEF rwDECLARE
+%token rwBREAK rwELSE rwCONTINUE rwGLOBAL
+%token rwIF rwNIL rwRETURN rwWHILE rwDO
+%token rwENDIF rwENDWHILE rwENDFOR rwDEFAULT
+%token rwFOR rwDATABLOCK rwSWITCH rwCASE rwSWITCHSTR
+%token rwCASEOR rwPACKAGE rwNAMESPACE rwCLASS
+%token ILLEGAL_TOKEN
+%{
+ /* Constants and Identifier Definitions */
+%}
+%token CHRCONST
+%token INTCONST
+%token TTAG
+%token VAR
+%token IDENT
+%token STRATOM
+%token TAGATOM
+%token