jp9000 12 vuotta sitten
sitoutus
f255ae1922
100 muutettua tiedostoa jossa 24778 lisäystä ja 0 poistoa
  1. 7 0
      .gitattributes
  2. 36 0
      .gitignore
  3. 674 0
      COPYING
  4. 166 0
      README
  5. 0 0
      build/.gitignore
  6. 12 0
      config.mak
  7. 200 0
      libobs-d3d11/GS_D3D11Exports.h
  8. 54 0
      libobs-d3d11/GS_D3D11IndexBuffer.cpp
  9. 66 0
      libobs-d3d11/GS_D3D11SamplerState.cpp
  10. 338 0
      libobs-d3d11/GS_D3D11Shader.cpp
  11. 236 0
      libobs-d3d11/GS_D3D11ShaderProcessor.cpp
  12. 40 0
      libobs-d3d11/GS_D3D11ShaderProcessor.hpp
  13. 44 0
      libobs-d3d11/GS_D3D11StageSurf.cpp
  14. 1574 0
      libobs-d3d11/GS_D3D11SubSystem.cpp
  15. 642 0
      libobs-d3d11/GS_D3D11SubSystem.hpp
  16. 154 0
      libobs-d3d11/GS_D3D11Texture2D.cpp
  17. 143 0
      libobs-d3d11/GS_D3D11VertexBuffer.cpp
  18. 60 0
      libobs-d3d11/GS_D3D11ZStencilBuffer.cpp
  19. 51 0
      libobs-d3d11/makefile
  20. 2713 0
      libobs-d3d11/mingw/dxgi.h
  21. 0 0
      libobs-d3d9/.gitignore
  22. 0 0
      libobs-opengl/.gitignore
  23. 38 0
      libobs/graphics/axisang.c
  24. 67 0
      libobs/graphics/axisang.h
  25. 3 0
      libobs/graphics/basemath.hpp
  26. 263 0
      libobs/graphics/bounds.c
  27. 135 0
      libobs/graphics/bounds.h
  28. 1459 0
      libobs/graphics/effect-parser.c
  29. 285 0
      libobs/graphics/effect-parser.h
  30. 313 0
      libobs/graphics/effect.c
  31. 181 0
      libobs/graphics/effect.h
  32. 162 0
      libobs/graphics/graphics-imports.c
  33. 229 0
      libobs/graphics/graphics-internal.h
  34. 1397 0
      libobs/graphics/graphics.c
  35. 664 0
      libobs/graphics/graphics.h
  36. 155 0
      libobs/graphics/input.h
  37. 48 0
      libobs/graphics/math-defs.h
  38. 132 0
      libobs/graphics/math-extra.c
  39. 66 0
      libobs/graphics/math-extra.h
  40. 145 0
      libobs/graphics/matrix3.c
  41. 87 0
      libobs/graphics/matrix3.h
  42. 209 0
      libobs/graphics/matrix4.c
  43. 76 0
      libobs/graphics/matrix4.h
  44. 148 0
      libobs/graphics/plane.c
  45. 101 0
      libobs/graphics/plane.h
  46. 216 0
      libobs/graphics/quat.c
  47. 185 0
      libobs/graphics/quat.h
  48. 677 0
      libobs/graphics/shader-parser.c
  49. 246 0
      libobs/graphics/shader-parser.h
  50. 139 0
      libobs/graphics/texture-render.c
  51. 52 0
      libobs/graphics/vec2.c
  52. 171 0
      libobs/graphics/vec2.h
  53. 73 0
      libobs/graphics/vec3.c
  54. 236 0
      libobs/graphics/vec3.h
  55. 32 0
      libobs/graphics/vec4.c
  56. 253 0
      libobs/graphics/vec4.h
  57. 67 0
      libobs/makefile
  58. 134 0
      libobs/media-io/audio-io.c
  59. 91 0
      libobs/media-io/audio-io.h
  60. 0 0
      libobs/media-io/audio-mixer.h
  61. 158 0
      libobs/media-io/media-io.c
  62. 77 0
      libobs/media-io/media-io.h
  63. 194 0
      libobs/media-io/video-io.c
  64. 69 0
      libobs/media-io/video-io.h
  65. 83 0
      libobs/obs-data.h
  66. 30 0
      libobs/obs-defs.h
  67. 53 0
      libobs/obs-display.c
  68. 127 0
      libobs/obs-module.c
  69. 32 0
      libobs/obs-module.h
  70. 121 0
      libobs/obs-output.c
  71. 119 0
      libobs/obs-output.h
  72. 222 0
      libobs/obs-scene.c
  73. 42 0
      libobs/obs-scene.h
  74. 33 0
      libobs/obs-service.h
  75. 313 0
      libobs/obs-source.c
  76. 214 0
      libobs/obs-source.h
  77. 126 0
      libobs/obs-video.c
  78. 242 0
      libobs/obs.c
  79. 304 0
      libobs/obs.h
  80. 104 0
      libobs/util/base.c
  81. 58 0
      libobs/util/base.h
  82. 131 0
      libobs/util/bmem.c
  83. 102 0
      libobs/util/bmem.h
  84. 75 0
      libobs/util/c99defs.h
  85. 1311 0
      libobs/util/cf-lexer.c
  86. 216 0
      libobs/util/cf-lexer.h
  87. 72 0
      libobs/util/cf-parser.c
  88. 284 0
      libobs/util/cf-parser.h
  89. 528 0
      libobs/util/config-file.c
  90. 87 0
      libobs/util/config-file.h
  91. 545 0
      libobs/util/darray.h
  92. 611 0
      libobs/util/dstr.c
  93. 310 0
      libobs/util/dstr.h
  94. 321 0
      libobs/util/lexer.c
  95. 294 0
      libobs/util/lexer.h
  96. 139 0
      libobs/util/platform-windows.c
  97. 305 0
      libobs/util/platform.c
  98. 82 0
      libobs/util/platform.h
  99. 128 0
      libobs/util/serializer.h
  100. 371 0
      libobs/util/text-lookup.c

+ 7 - 0
.gitattributes

@@ -0,0 +1,7 @@
+* text=auto
+
+*.sln             text eol=crlf
+*.vcproj          text eol=crlf
+*.vcxproj         text eol=crlf
+*.vcxproj         text eol=crlf
+*.vcxproj.filters text eol=crlf

+ 36 - 0
.gitignore

@@ -0,0 +1,36 @@
+*.exe
+*.dll
+
+/other/
+Release_MD/
+Release/
+Debug/
+x64/
+ipch/
+
+tags
+*.swp
+*.dat
+*.clbin
+*.log
+*.tlog
+*.sdf
+*.opensdf
+*.xml
+*.ipch
+*.css
+*.xslt
+*.aps
+*.suo
+*.ncb
+*.user
+*.o
+*.obj
+*.pdb
+*.res
+*.manifest
+*.dep
+*.zip
+*.lnk
+*.chm
+*~

+ 674 - 0
COPYING

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state 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) <year>  <name 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 3 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, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.

+ 166 - 0
README

@@ -0,0 +1,166 @@
+
+What is OBS?
+
+  This project is a rewrite of what was formerly known as "Open Broadcaster
+  Software", software originally designed for recording and streaming live
+  video content, efficiently.
+
+
+What's the goal of rewriting OBS?
+
+ - Make it multiplatform.  Use multiplatform libraries/functions/classes where
+   possible to allow this.  Multi-platform support was one of the primary
+   reasons for the rewrite.  This also means using a UI toolkit will be
+   necessary for user interface.  It also means allowing the use of OpenGL as
+   well as Direct3D.
+
+ - Separate the application from the core, allowing custom user interfaces and
+   custom appliction of the core if desired, and easier extending of the user
+   interface.
+
+ - Simplify complex systems to not only make it easier to use, but easier to
+   maintain.
+
+ - Write a better core API, and design the entire system to be modular.
+
+ - Now that we have much more experience, improve the overall design of all
+   the subsystems/API, and minimize/eliminate design flaws.  Make it so we can
+   do all the things we've had trouble with before, such as custom outputs,
+   multiple outputs at once, better handling of inputs, custom services.
+
+ - Make a better/cleaner code base, use better coding standards, use standard
+   libraries where possible (not just STL and C standard library, but also
+   things like ffmpeg as well), and improve maintainability of the project as a
+   whole.
+
+ - Implement a new API-independent shader/effect system allowing better and
+   easier shaders usage and customization without having to duplicate shader
+   code.
+
+ - Better device support.  Again, I didn't know what I was getting into when
+   I originally started writing code for devices.  It evolved into a totally
+   convoluted mess.  I would have improved the existing device plugin code, but
+   it was just all so fundamentally bad and flawed that it would have been
+   detrimental to progression to continue working on it rather than rewrite it.
+
+
+What was wrong with the original OBS?
+
+  The original OBS was rewritten not because it was bad, at least in terms of
+  optimization.  Optimization and graphics are things I love.  However, there
+  were some serious problems with the code and design that were deep and
+  fundamental, which prevented myself and other developers from being able to
+  improve/extend the application or add new features very easily.
+
+  First, the design flaws:
+
+    - The original OBS was completely and hopelessly hard-coded for windows,
+      and only windows.  It was just totally impossible to use it on other
+      systems.
+
+    - All the sub-systems were written before I really knew what I was getting
+      into.  When I started the project, I didn't really fully comprehend the
+      scope of what I would need or how to properly design the project.  My
+      design and plans for the application were just to write something that
+      would "stream games and a webcam, with things like overlays and such."
+      This turned out fine for most casual gamers and streamers (and very
+      successful), but left anyone wanting to do anything more advanced left
+      massively wanting.
+
+    - Subsystems and core functionalities intermingled in such a way that it
+      was a nightmare to get proper custom functionality out of it.  Things
+      like QSV had to be meshed in with the main encoding loop, and it just
+      made things a nightmare to deal with.  Custom outputs were nigh
+      impossible.
+
+    - The API was poorly designed because most of it came after I originally
+      wrote the application, it was more of an afterthought, and plugin API
+      would routinely break for plugin developers due to changing C++
+      interfaces (one of the reasons the core is now C).
+
+    - API was intermeshed with the main executable.  The OBSApi DLL was
+      nothing more than basically this mutant growth upon OBS.exe that allowed
+      plugin developers to barely write plugins, but all the important API
+      code was actually stored in the executable.  Navigation was a total mess.
+
+    - The graphics subsystem, while not bad, was incomplete, and though far
+      easier to use than bare D3D, wasn't ideal, and was hard-coded for D3D
+      specifically.
+
+    - The devices and audio code was poor, I had no idea what I was getting into
+      when I started writing them in.  I did not realize beforehand all the
+      device-specific quirks that each device/system could have.  Some devices
+      had bad timing and quirks that I never aniticipated while writing them.
+      I struggled with devices, and my original design for the audio subsystem
+      for example morphed over and over into an abomination that, though works,
+      is basically this giant duct-taped zombie monster.
+
+    - Shaders were difficult to customize because they had to be duplicated if
+      you wanted slightly different functionality that required more than just
+      changing shader constants.
+
+    - Oriantation of sources was fixed, and required special code for each
+      source to do any custom modification of rotation/position/scale/etc.
+      This is one of those fundamental flaws that I look back on and regret, as
+      it was a stupid idea from the beginning.  I originally thought I could
+      get more accurate source position/sizes, but it just turned out to be
+      totally bad.  Should have been matrices from the beginning just like with
+      a regular 3D engine.
+
+  Second, the coding flaws:
+
+    - The coding style was inconsistent.
+
+    - C++98, C-Style C++, there was no exception usage, no STL.  C++ used
+      poorly.
+
+    - Not Invented Here Syndrome everywhere.  Custom string functions/classes,
+      custom templates, custom everything everywhere.  To be fair, it was all
+      hand-me-down code from the early 2000s that I had become used to, but
+      that was no excuse -- C-standard libraries and the STL should have been
+      used from the beginning over anything else.  That doesn't mean to say
+      that using custom stuff is always bad, but doing it to the extent I did
+      definitely was.  Made it horrible to maintain as well, required extra
+      knowledge for plugin developers and anyone messing with the code.
+
+    - Giant monolithic classes everywhere, the main OBS class was paricularly
+      bad in this regard.  This meant navigation was a nightmare, and no one
+      really knew where to go or where to add/change things.
+
+    - Giant monolithic functions everywhere.  This was particularly bad
+      because it meant that functions became harder to debug and harder to
+      keep track of what was going on in any particular function at any given
+      time.  These large functions, though not inefficient, were delicate and
+      easily breakable.  (See OBS::MainCaptureLoop for a nightmarish example,
+      or the listbox subclass window procedure in WindowStuff.cpp)
+
+    - Very large file sizes with everything clumped up into single files (for
+      another particularly nightmarish example, see WindowStuff.cpp)
+
+    - Bad formatting.  Code could go beyond 200 columns in some cases, making
+      it very unpleasant to read with many editors.  Spaces instead of tabs,
+      K&R mixed with allman (which was admittedly my fault).
+
+
+New (actual) coding guidelines
+
+ - For the C code (especially in the core), guidelines are pretty strict K&R,
+   kernel style.  See the linux kernel "CodingStyle" document for more
+   information.  That particular coding style guideline is for more than just
+   style, it actually helps produce a better overall code base.
+
+ - For C++ code, I still use CamelCase instead of all_lowercase just because
+   I prefer it that way, it feels right with C++ for some reason.  It also
+   helps make it distinguishable from C code.
+
+ - I've started using 8-column tabs for almost everything -- I really
+   personally like it over 4-column tabs.  I feel that 8-column tabs are very
+   helpful in preventing large amounts of indentation.  A self-imposed
+   limitation, if you will.  I also use actual tabs now, instead of spaces.
+   Also, I feel that the K&R style looks much better/cleaner when viewed with
+   8-column tabs.
+
+ - Preferred maximum columns: 80.  I've also been doing this because in
+   combination with 8-column tabs, it further prevents large/bad functions
+   with high indentation.  Another self-imposed limitation.  Also, it makes
+   for much cleaner viewing in certain editors that wrap (like vim).

+ 0 - 0
build/.gitignore


+ 12 - 0
config.mak

@@ -0,0 +1,12 @@
+SRCPATH=.
+CC=gcc
+CCDEP=gcc
+CFLAGS=-std=gnu99 -I$(SRCPATH)
+CXXFLAGS=-std=c++11 -I$(SRCPATH)
+#CPPFLAGS=-Wall -msse2 -O3 -ffast-math -fomit-frame-pointer
+CPPFLAGS=-Wall -msse2 -g
+LD=gcc -o 
+LDFLAGS=-shared
+OBJ=o
+SOEXT=dll
+EXT=.exe

+ 200 - 0
libobs-d3d11/GS_D3D11Exports.h

@@ -0,0 +1,200 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef GS_D3D11EXPORTS_H
+#define GS_D3D11EXPORTS_H
+
+#include "util/c99defs.h"
+
+extern "C" {
+
+EXPORT device_t device_create(struct gs_init_data *data);
+EXPORT void device_destroy(device_t device);
+EXPORT swapchain_t device_create_swapchain(device_t device,
+		struct gs_init_data *data);
+EXPORT void device_resize(device_t device, uint32_t x, uint32_t y);
+EXPORT void device_getsize(device_t device, uint32_t *x, uint32_t *y);
+EXPORT uint32_t device_getwidth(device_t device);
+EXPORT uint32_t device_getheight(device_t device);
+EXPORT texture_t device_create_texture(device_t device, uint32_t width,
+		uint32_t height, enum gs_color_format color_format, void *data,
+		uint32_t flags);
+EXPORT texture_t device_create_cubetexture(device_t device, uint32_t size,
+		enum gs_color_format color_format, void *data[6],
+		uint32_t flags);
+EXPORT texture_t device_create_volumetexture(device_t device, uint32_t width,
+		uint32_t height, uint32_t depth,
+		enum gs_color_format color_format, void *data, uint32_t flags);
+EXPORT zstencil_t device_create_zstencil(device_t device, uint32_t width,
+		uint32_t height, enum gs_zstencil_format format);
+EXPORT stagesurf_t device_create_stagesurface(device_t device, uint32_t width,
+		uint32_t height, enum gs_color_format color_format);
+EXPORT samplerstate_t device_create_samplerstate(device_t device,
+		struct gs_sampler_info *info);
+EXPORT shader_t device_create_vertexshader(device_t device,
+		const char *shader, const char *file,
+		char **error_string);
+EXPORT shader_t device_create_pixelshader(device_t device,
+		const char *shader, const char *file,
+		char **error_string);
+EXPORT vertbuffer_t device_create_vertexbuffer(device_t device,
+		struct vb_data *data, uint32_t flags);
+EXPORT indexbuffer_t device_create_indexbuffer(device_t device,
+		enum gs_index_type type, void *indices, size_t num,
+		uint32_t flags);
+EXPORT enum gs_texture_type device_gettexturetype(device_t device,
+		texture_t texture);
+EXPORT void device_load_vertexbuffer(device_t device, vertbuffer_t vertbuffer);
+EXPORT void device_load_indexbuffer(device_t device, indexbuffer_t indexbuffer);
+EXPORT void device_load_texture(device_t device, texture_t tex, int unit);
+EXPORT void device_load_cubetexture(device_t device, texture_t cubetex,
+		int unit);
+EXPORT void device_load_volumetexture(device_t device, texture_t voltex,
+		int unit);
+EXPORT void device_load_samplerstate(device_t device,
+		samplerstate_t samplerstate, int unit);
+EXPORT void device_load_vertexshader(device_t device, shader_t vertshader);
+EXPORT void device_load_pixelshader(device_t device, shader_t pixelshader);
+EXPORT void device_load_defaultsamplerstate(device_t device, bool b_3d,
+		int unit);
+EXPORT shader_t device_getvertexshader(device_t device);
+EXPORT shader_t device_getpixelshader(device_t device);
+EXPORT texture_t device_getrendertarget(device_t device);
+EXPORT zstencil_t device_getzstenciltarget(device_t device);
+EXPORT void device_setrendertarget(device_t device, texture_t tex,
+		zstencil_t zstencil);
+EXPORT void device_setcuberendertarget(device_t device, texture_t cubetex,
+		int side, zstencil_t zstencil);
+EXPORT void device_copy_texture(device_t device, texture_t dst, texture_t src);
+EXPORT void device_stage_texture(device_t device, stagesurf_t dst,
+		texture_t src);
+EXPORT void device_beginscene(device_t device);
+EXPORT void device_draw(device_t device, enum gs_draw_mode draw_mode,
+		uint32_t start_vert, uint32_t num_verts);
+EXPORT void device_endscene(device_t device);
+EXPORT void device_load_swapchain(device_t device, swapchain_t swapchain);
+EXPORT void device_clear(device_t device, uint32_t clear_flags,
+		struct vec4 *color, float depth, uint8_t stencil);
+EXPORT void device_present(device_t device);
+EXPORT void device_setcullmode(device_t device, enum gs_cull_mode mode);
+EXPORT enum gs_cull_mode device_getcullmode(device_t device);
+EXPORT void device_enable_blending(device_t device, bool enable);
+EXPORT void device_enable_depthtest(device_t device, bool enable);
+EXPORT void device_enable_stenciltest(device_t device, bool enable);
+EXPORT void device_enable_stencilwrite(device_t device, bool enable);
+EXPORT void device_enable_color(device_t device, bool red, bool blue,
+		bool green, bool alpha);
+EXPORT void device_blendfunction(device_t device, enum gs_blend_type src,
+		enum gs_blend_type dest);
+EXPORT void device_depthfunction(device_t device, enum gs_depth_test test);
+EXPORT void device_stencilfunction(device_t device, enum gs_stencil_side side,
+		enum gs_depth_test test);
+EXPORT void device_stencilop(device_t device, enum gs_stencil_side side,
+		enum gs_stencil_op fail, enum gs_stencil_op zfail,
+		enum gs_stencil_op zpass);
+EXPORT void device_enable_fullscreen(device_t device, bool enable);
+EXPORT int device_fullscreen_enabled(device_t device);
+EXPORT void device_setdisplaymode(device_t device,
+		const struct gs_display_mode *mode);
+EXPORT void device_getdisplaymode(device_t device,
+		struct gs_display_mode *mode);
+EXPORT void device_setcolorramp(device_t device, float gamma, float brightness,
+		float contrast);
+EXPORT void device_setviewport(device_t device, int x, int y, int width,
+		int height);
+EXPORT void device_getviewport(device_t device, struct gs_rect *rect);
+EXPORT void device_setscissorrect(device_t device, struct gs_rect *rect);
+EXPORT void device_ortho(device_t device, float left, float right,
+		float top, float bottom, float znear, float zfar);
+EXPORT void device_frustum(device_t device, float left, float right,
+		float top, float bottom, float znear, float zfar);
+EXPORT void device_perspective(device_t device, float fovy, float aspect,
+		float znear, float zfar);
+EXPORT void device_set_view_matrix(device_t device, struct matrix3 *mat);
+EXPORT void device_projection_push(device_t device);
+EXPORT void device_projection_pop(device_t device);
+
+EXPORT void     swapchain_destroy(swapchain_t swapchain);
+
+EXPORT void     texture_destroy(texture_t tex);
+EXPORT uint32_t texture_getwidth(texture_t tex);
+EXPORT uint32_t texture_getheight(texture_t tex);
+EXPORT enum gs_color_format texture_getcolorformat(texture_t tex);
+EXPORT bool     texture_map(texture_t tex, void **ptr, uint32_t *byte_width);
+EXPORT void     texture_unmap(texture_t tex);
+
+EXPORT void     cubetexture_destroy(texture_t cubetex);
+EXPORT uint32_t cubetexture_getsize(texture_t cubetex);
+EXPORT enum gs_color_format cubetexture_getcolorformat(texture_t cubetex);
+
+EXPORT void     volumetexture_destroy(texture_t voltex);
+EXPORT uint32_t volumetexture_getwidth(texture_t voltex);
+EXPORT uint32_t volumetexture_getheight(texture_t voltex);
+EXPORT uint32_t volumetexture_getdepth(texture_t voltex);
+EXPORT enum gs_color_format volumetexture_getcolorformat(texture_t voltex);
+
+EXPORT void     stagesurface_destroy(stagesurf_t stagesurf);
+EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
+EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
+EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
+EXPORT bool     stagesurface_map(stagesurf_t stagesurf, const void **data,
+		uint32_t *byte_width);
+EXPORT void     stagesurface_unmap(stagesurf_t stagesurf);
+
+EXPORT void zstencil_destroy(zstencil_t zstencil);
+
+EXPORT void samplerstate_destroy(samplerstate_t samplerstate);
+
+EXPORT void vertexbuffer_destroy(vertbuffer_t vertbuffer);
+EXPORT void vertexbuffer_flush(vertbuffer_t vertbuffer, bool rebuild);
+EXPORT struct vb_data *vertexbuffer_getdata(vertbuffer_t vertbuffer);
+
+EXPORT void   indexbuffer_destroy(indexbuffer_t indexbuffer);
+EXPORT void   indexbuffer_flush(indexbuffer_t indexbuffer);
+EXPORT void  *indexbuffer_getdata(indexbuffer_t indexbuffer);
+EXPORT size_t indexbuffer_numindices(indexbuffer_t indexbuffer);
+EXPORT enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer);
+
+EXPORT void shader_destroy(shader_t shader);
+EXPORT int shader_numparams(shader_t shader);
+EXPORT sparam_t shader_getparambyidx(shader_t shader, int param);
+EXPORT sparam_t shader_getparambyname(shader_t shader, const char *name);
+EXPORT void shader_getparaminfo(shader_t shader, sparam_t param,
+		struct shader_param_info *info);
+EXPORT sparam_t shader_getviewprojmatrix(shader_t shader);
+EXPORT sparam_t shader_getworldmatrix(shader_t shader);
+EXPORT void shader_setbool(shader_t shader, sparam_t param, bool val);
+EXPORT void shader_setfloat(shader_t shader, sparam_t param, float val);
+EXPORT void shader_setint(shader_t shader, sparam_t param, int val);
+EXPORT void shader_setmatrix3(shader_t shader, sparam_t param,
+		const struct matrix3 *val);
+EXPORT void shader_setmatrix4(shader_t shader, sparam_t param,
+		const struct matrix4 *val);
+EXPORT void shader_setvec2(shader_t shader, sparam_t param,
+		const struct vec2 *val);
+EXPORT void shader_setvec3(shader_t shader, sparam_t param,
+		const struct vec3 *val);
+EXPORT void shader_setvec4(shader_t shader, sparam_t param,
+		const struct vec4 *val);
+EXPORT void shader_settexture(shader_t shader, sparam_t param, texture_t val);
+EXPORT void shader_setval(shader_t shader, sparam_t param, const void *val,
+		size_t size);
+EXPORT void shader_setdefault(shader_t shader, sparam_t param);
+
+}
+
+#endif

+ 54 - 0
libobs-d3d11/GS_D3D11IndexBuffer.cpp

@@ -0,0 +1,54 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "GS_D3D11SubSystem.hpp"
+
+void gs_index_buffer::InitBuffer()
+{
+	D3D11_BUFFER_DESC bd;
+	D3D11_SUBRESOURCE_DATA srd;
+	HRESULT hr;
+
+	memset(&bd,  0, sizeof(bd));
+	memset(&srd, 0, sizeof(srd));
+
+	bd.Usage          = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+	bd.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+	bd.BindFlags      = D3D11_BIND_INDEX_BUFFER;
+	bd.ByteWidth      = UINT(indexSize * num);
+	srd.pSysMem       = indices.data;
+
+	hr = device->device->CreateBuffer(&bd, &srd, indexBuffer.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create buffer", hr);
+}
+
+gs_index_buffer::gs_index_buffer(device_t device, enum gs_index_type type,
+		void *indices, size_t num, uint32_t flags)
+	: device  (device),
+	  type    (type),
+	  indices (indices),
+	  num     (num),
+	  dynamic ((flags & GS_DYNAMIC) != 0)
+{
+	switch (type) {
+	case GS_UNSIGNED_SHORT: indexSize = 2; break;
+	case GS_UNSIGNED_LONG:  indexSize = 4; break;
+	}
+
+	InitBuffer();
+}

+ 66 - 0
libobs-d3d11/GS_D3D11SamplerState.cpp

@@ -0,0 +1,66 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "GS_D3D11SubSystem.hpp"
+#include "graphics/vec4.h"
+
+const D3D11_TEXTURE_ADDRESS_MODE convertAddressMode[] =
+{
+	D3D11_TEXTURE_ADDRESS_CLAMP,
+	D3D11_TEXTURE_ADDRESS_WRAP,
+	D3D11_TEXTURE_ADDRESS_MIRROR,
+	D3D11_TEXTURE_ADDRESS_BORDER,
+	D3D11_TEXTURE_ADDRESS_MIRROR_ONCE
+};
+	
+const D3D11_FILTER convertFilter[] =
+{
+	D3D11_FILTER_MIN_MAG_MIP_LINEAR,
+	D3D11_FILTER_MIN_MAG_MIP_POINT,
+	D3D11_FILTER_ANISOTROPIC,
+	D3D11_FILTER_MIN_MAG_POINT_MIP_LINEAR,
+	D3D11_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
+	D3D11_FILTER_MIN_POINT_MAG_MIP_LINEAR,
+	D3D11_FILTER_MIN_LINEAR_MAG_MIP_POINT,
+	D3D11_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
+	D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT
+};
+
+gs_sampler_state::gs_sampler_state(device_t device, gs_sampler_info *info)
+	: device (device),
+	  info   (*info)
+{
+	D3D11_SAMPLER_DESC sd;
+	HRESULT hr;
+	vec4 v4;
+
+	memset(&sd, 0, sizeof(sd));
+	sd.AddressU       = convertAddressMode[(uint32_t)info->address_u];
+	sd.AddressV       = convertAddressMode[(uint32_t)info->address_v];
+	sd.AddressW       = convertAddressMode[(uint32_t)info->address_w];
+	sd.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
+	sd.Filter         = convertFilter[(uint32_t)info->filter];
+	sd.MaxAnisotropy  = info->max_anisotropy;
+	sd.MaxLOD         = FLT_MAX;
+
+	vec4_from_rgba(&v4, info->border_color);
+	memcpy(sd.BorderColor, v4.ptr, sizeof(v4));
+
+	hr = device->device->CreateSamplerState(&sd, state.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create sampler state", hr);
+}

+ 338 - 0
libobs-d3d11/GS_D3D11Shader.cpp

@@ -0,0 +1,338 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "GS_D3D11SubSystem.hpp"
+#include "GS_D3D11ShaderProcessor.hpp"
+#include "graphics/vec2.h"
+#include "graphics/vec3.h"
+#include "graphics/matrix3.h"
+#include "graphics/matrix4.h"
+
+void gs_vertex_shader::GetBuffersExpected(
+		const vector<D3D11_INPUT_ELEMENT_DESC> &inputs)
+{
+	for (size_t i = 0; i < inputs.size(); i++) {
+		const D3D11_INPUT_ELEMENT_DESC &input = inputs[i];
+		if (strcmp(input.SemanticName, "NORMAL") == 0)
+			hasNormals = true;
+		else if (strcmp(input.SemanticName, "TANGENT") == 0)
+			hasTangents = true;
+		else if (strcmp(input.SemanticName, "COLOR") == 0)
+			hasColors = true;
+		else if (strcmp(input.SemanticName, "TEXCOORD") == 0)
+			nTexUnits++;
+	}
+}
+
+gs_vertex_shader::gs_vertex_shader(device_t device, const char *file,
+		const char *shaderString)
+	: gs_shader   (device, SHADER_VERTEX),
+	  hasNormals  (false),
+	  hasColors   (false),
+	  hasTangents (false),
+	  nTexUnits   (0)
+{
+	vector<D3D11_INPUT_ELEMENT_DESC> inputs;
+	ShaderProcessor    processor(device);
+	ComPtr<ID3D10Blob> shaderBlob;
+	string             outputString;
+	HRESULT            hr;
+
+	processor.Process(shaderString, file);
+	processor.BuildString(outputString);
+	processor.BuildParams(params);
+	processor.BuildInputLayout(inputs);
+	GetBuffersExpected(inputs);
+	BuildConstantBuffer();
+
+	Compile(outputString.c_str(), file, "vs_4_0", shaderBlob.Assign());
+
+	hr = device->device->CreateVertexShader(shaderBlob->GetBufferPointer(),
+			shaderBlob->GetBufferSize(), NULL, shader.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create vertex shader", hr);
+
+	hr = device->device->CreateInputLayout(inputs.data(),
+			(UINT)inputs.size(), shaderBlob->GetBufferPointer(),
+			shaderBlob->GetBufferSize(), layout.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create input layout", hr);
+
+	viewProj = shader_getparambyname(this, "ViewProj");
+	world    = shader_getparambyname(this, "World");
+}
+
+gs_pixel_shader::gs_pixel_shader(device_t device, const char *file,
+		const char *shaderString)
+	: gs_shader(device, SHADER_PIXEL)
+{
+	ShaderProcessor    processor(device);
+	ComPtr<ID3D10Blob> shaderBlob;
+	string             outputString;
+	HRESULT            hr;
+
+	processor.Process(shaderString, file);
+	processor.BuildString(outputString);
+	processor.BuildParams(params);
+	processor.BuildSamplers(samplers);
+	BuildConstantBuffer();
+
+	Compile(outputString.c_str(), file, "ps_4_0", shaderBlob.Assign());
+
+	hr = device->device->CreatePixelShader(shaderBlob->GetBufferPointer(),
+			shaderBlob->GetBufferSize(), NULL, shader.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create vertex shader", hr);
+}
+
+void gs_shader::BuildConstantBuffer()
+{
+	for (size_t i = 0; i < params.size(); i++) {
+		shader_param &param = params[i];
+		switch (param.type) {
+		case SHADER_PARAM_BOOL:
+		case SHADER_PARAM_INT:
+		case SHADER_PARAM_FLOAT: constantSize += sizeof(float); break;
+		case SHADER_PARAM_VEC2:  constantSize += sizeof(vec2); break;
+		case SHADER_PARAM_VEC3:  constantSize += sizeof(float)*3; break;
+		case SHADER_PARAM_VEC4:  constantSize += sizeof(vec4); break;
+		case SHADER_PARAM_MATRIX3X3:
+			constantSize += sizeof(float)*3*3;
+			break;
+		case SHADER_PARAM_MATRIX4X4:
+			constantSize += sizeof(float)*4*4;
+		}
+	}
+
+	if (constantSize) {
+		D3D11_BUFFER_DESC bd;
+		HRESULT hr;
+
+		memset(&bd, 0, sizeof(bd));
+		bd.ByteWidth      = (constantSize+15)&0xFFFFFFF0; /* align */
+		bd.Usage          = D3D11_USAGE_DYNAMIC;
+		bd.BindFlags      = D3D11_BIND_CONSTANT_BUFFER;
+		bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+		hr = device->device->CreateBuffer(&bd, NULL,
+				constants.Assign());
+		if (FAILED(hr))
+			throw HRError("Failed to create constant buffer", hr);
+	}
+
+	for (size_t i = 0; i < params.size(); i++)
+		shader_setdefault(this, &params[i]);
+}
+
+void gs_shader::Compile(const char *shaderString, const char *file,
+		const char *target, ID3D10Blob **shader)
+{
+	ComPtr<ID3D10Blob> errorsBlob;
+	HRESULT hr;
+
+	if (!shaderString)
+		throw "No shader string specified";
+
+	hr = D3DCompile(shaderString, strlen(shaderString), file, NULL, NULL,
+			"main", target,
+			D3D10_SHADER_OPTIMIZATION_LEVEL1, 0,
+			shader, errorsBlob.Assign());
+	if (FAILED(hr)) {
+		if (errorsBlob != NULL && errorsBlob->GetBufferSize())
+			throw ShaderError(errorsBlob, hr);
+		else
+			throw HRError("Failed to compile shader", hr);
+	}
+}
+
+inline void gs_shader::UpdateParam(vector<uint8_t> &constData,
+		shader_param &param, bool &upload)
+{
+	if (param.type != SHADER_PARAM_TEXTURE) {
+		if (!param.curValue.size())
+			throw "Not all shader parameters were set";
+
+		constData.insert(constData.end(),
+				param.curValue.begin(),
+				param.curValue.end());
+
+		if (param.changed) {
+			upload = true;
+			param.changed = false;
+		}
+	} else if (param.curValue.size() == sizeof(texture_t)) {
+		texture_t tex;
+		memcpy(&tex, param.curValue.data(), sizeof(texture_t));
+		device_load_texture(device, tex, param.textureID);
+	}
+}
+
+void gs_shader::UploadParams()
+{
+	vector<uint8_t> constData;
+	bool            upload = false;
+
+	constData.reserve(constantSize);
+
+	for (size_t i = 0; i < params.size(); i++)
+		UpdateParam(constData, params[i], upload);
+
+	if (constData.size() != constantSize)
+		throw "Invalid constant data size given to shader";
+
+	if (upload) {
+		D3D11_MAPPED_SUBRESOURCE map;
+		HRESULT hr;
+
+		hr = device->context->Map(constants, 0, D3D11_MAP_WRITE_DISCARD,
+				0, &map);
+		if (FAILED(hr))
+			throw HRError("Could not lock constant buffer", hr);
+
+		memcpy(map.pData, constData.data(), constData.size());
+		device->context->Unmap(constants, 0);
+	}
+}
+
+void shader_destroy(shader_t shader)
+{
+	delete shader;
+}
+
+int shader_numparams(shader_t shader)
+{
+	return (int)shader->params.size();
+}
+
+sparam_t shader_getparambyidx(shader_t shader, int param)
+{
+	return &shader->params[param];
+}
+
+sparam_t shader_getparambyname(shader_t shader, const char *name)
+{
+	for (size_t i = 0; i < shader->params.size(); i++) {
+		shader_param &param = shader->params[i];
+		if (strcmp(param.name.c_str(), name) == 0)
+			return &param;
+	}
+
+	return NULL;
+}
+
+void shader_getparaminfo(shader_t shader, sparam_t param,
+		struct shader_param_info *info)
+{
+	if (!param || !shader)
+		return;
+
+	info->name = param->name.c_str();
+	info->type = param->type;
+}
+
+sparam_t shader_getviewprojmatrix(shader_t shader)
+{
+	if (shader->type != SHADER_VERTEX)
+		return NULL;
+
+	return static_cast<gs_vertex_shader*>(shader)->viewProj;
+}
+
+sparam_t shader_getworldmatrix(shader_t shader)
+{
+	if (shader->type != SHADER_VERTEX)
+		return NULL;
+
+	return static_cast<gs_vertex_shader*>(shader)->world;
+}
+
+static inline void shader_setval_inline(gs_shader *shader, shader_param *param,
+		const void *data, size_t size)
+{
+	assert(param);
+	if (!param)
+		return;
+
+	bool size_changed = param->curValue.size() != size;
+	if (size_changed)
+		param->curValue.resize(size);
+
+	if (size_changed || memcmp(param->curValue.data(), data, size) != 0) {
+		memcpy(param->curValue.data(), data, size);
+		param->changed = true;
+	}
+}
+
+void shader_setbool(shader_t shader, sparam_t param, bool val)
+{
+	shader_setval_inline(shader, param, &val, sizeof(bool));
+}
+
+void shader_setfloat(shader_t shader, sparam_t param, float val)
+{
+	shader_setval_inline(shader, param, &val, sizeof(float));
+}
+
+void shader_setint(shader_t shader, sparam_t param, int val)
+{
+	shader_setval_inline(shader, param, &val, sizeof(int));
+}
+
+void shader_setmatrix3(shader_t shader, sparam_t param,
+		const struct matrix3 *val)
+{
+	shader_setval_inline(shader, param, val, sizeof(matrix3));
+}
+
+void shader_setmatrix4(shader_t shader, sparam_t param,
+		const struct matrix4 *val)
+{
+	shader_setval_inline(shader, param, val, sizeof(matrix4));
+}
+
+void shader_setvec2(shader_t shader, sparam_t param, const struct vec2 *val)
+{
+	shader_setval_inline(shader, param, val, sizeof(vec2));
+}
+
+void shader_setvec3(shader_t shader, sparam_t param, const struct vec3 *val)
+{
+	shader_setval_inline(shader, param, val, sizeof(float) * 3);
+}
+
+void shader_setvec4(shader_t shader, sparam_t param, const struct vec4 *val)
+{
+	shader_setval_inline(shader, param, val, sizeof(vec4));
+}
+
+void shader_settexture(shader_t shader, sparam_t param, texture_t val)
+{
+	shader_setval_inline(shader, param, &val, sizeof(texture_t));
+}
+
+void shader_setval(shader_t shader, sparam_t param, const void *val,
+		size_t size)
+{
+	shader_setval_inline(shader, param, val, size);
+}
+
+void shader_setdefault(shader_t shader, sparam_t param)
+{
+	if (param->defaultValue.size())
+		shader_setval_inline(shader, param, param->defaultValue.data(),
+				param->defaultValue.size());
+}

+ 236 - 0
libobs-d3d11/GS_D3D11ShaderProcessor.cpp

@@ -0,0 +1,236 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "GS_D3D11SubSystem.hpp"
+#include "GS_D3D11ShaderProcessor.hpp"
+
+#include <sstream>
+using namespace std;
+
+static const char *semanticInputNames[] =
+	{"POSITION", "NORMAL", "COLOR", "TANGENT", "TEXCOORD"};
+static const char *semanticOutputNames[] =
+	{"SV_Position", "NORMAL", "COLOR", "TANGENT", "TEXCOORD"};
+
+static const char *ConvertSemanticName(const char *name)
+{
+	const size_t num = sizeof(semanticInputNames) / sizeof(const char*);
+	for (size_t i = 0; i < num; i++) {
+		if (strcmp(name, semanticInputNames[i]) == 0)
+			return semanticOutputNames[i];
+	}
+
+	throw "Unknown Semantic Name";
+	return NULL;
+}
+
+static void GetSemanticInfo(shader_var *var, const char *&name,
+		uint32_t &index)
+{
+	const char *mapping = var->mapping;
+	const char *indexStr = mapping;
+
+	while (*indexStr && !isdigit(*indexStr))
+		indexStr++;
+	index = (*indexStr) ? strtol(indexStr, NULL, 10) : 0;
+
+	string nameStr;
+	nameStr.assign(mapping, indexStr-mapping);
+	name = ConvertSemanticName(nameStr.c_str());
+}
+
+static void AddInputLayoutVar(shader_var *var,
+		vector<D3D11_INPUT_ELEMENT_DESC> &layout)
+{
+	D3D11_INPUT_ELEMENT_DESC ied;
+	const char *semanticName;
+	uint32_t semanticIndex;
+
+	GetSemanticInfo(var, semanticName, semanticIndex);
+
+	memset(&ied, 0, sizeof(ied));
+	ied.SemanticName   = semanticName;
+	ied.SemanticIndex  = semanticIndex;
+	ied.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
+
+	if (strcmp(var->mapping, "COLOR") == 0) {
+		ied.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+
+	} else if (strcmp(var->mapping, "POSITION")  == 0 ||
+	           strcmp(var->mapping, "NORMAL")    == 0 ||
+	           strcmp(var->mapping, "TANGENT")   == 0) {
+		ied.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
+
+	} else if (astrcmp_n(var->mapping, "TEXCOORD", 8) == 0) {
+		/* type is always a 'float' type */
+		switch (var->type[5]) {
+		case 0:   ied.Format = DXGI_FORMAT_R32_FLOAT; break;
+		case '2': ied.Format = DXGI_FORMAT_R32G32_FLOAT; break;
+		case '3':
+		case '4': ied.Format = DXGI_FORMAT_R32G32B32A32_FLOAT; break;
+		}
+	}
+
+	layout.push_back(ied);
+}
+
+static inline bool SetSlot(vector<D3D11_INPUT_ELEMENT_DESC> &layout,
+		const char *name, uint32_t index, uint32_t &slotIdx)
+{
+	for (size_t i = 0; i < layout.size(); i++) {
+		D3D11_INPUT_ELEMENT_DESC &input = layout[i];
+		if (input.SemanticIndex == index &&
+		    strcmpi(input.SemanticName, name) == 0) {
+			layout[i].InputSlot = slotIdx++;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static void BuildInputLayoutFromVars(shader_parser *parser, darray *vars,
+		vector<D3D11_INPUT_ELEMENT_DESC> &layout)
+{
+	shader_var *array = (shader_var*)vars->array;
+
+	for (size_t i = 0; i < vars->num; i++) {
+		shader_var *var = array+i;
+
+		if (var->mapping) {
+			AddInputLayoutVar(var, layout);
+		} else {
+			shader_struct *st = shader_parser_getstruct(parser,
+					var->type);
+			if (st)
+				BuildInputLayoutFromVars(parser, &st->vars.da,
+						layout);
+		}
+	}
+
+	/*
+	 * Sets the input slot value for each semantic, however we do it in
+	 * a specific order so that it will always match the vertex buffer's
+	 * sub-buffer order (points-> normals-> colors-> tangents-> uvcoords)
+	 */
+	uint32_t slot = 0;
+	SetSlot(layout, "SV_Position", 0, slot);
+	SetSlot(layout, "NORMAL", 0, slot);
+	SetSlot(layout, "COLOR", 0, slot);
+	SetSlot(layout, "TANGENT", 0, slot);
+
+	uint32_t index = 0;
+	while (SetSlot(layout, "TEXCOORD", index++, slot));
+}
+
+void ShaderProcessor::BuildInputLayout(
+		vector<D3D11_INPUT_ELEMENT_DESC> &layout)
+{
+	shader_func *func = shader_parser_getfunc(&parser, "main");
+	if (!func)
+		throw "Failed to find 'main' shader function";
+
+	BuildInputLayoutFromVars(&parser, &func->params.da, layout);
+}
+
+shader_param::shader_param(shader_var &var, uint32_t &texCounter)
+	: type       (get_shader_param_type(var.type)),
+	  name       (var.name),
+	  textureID  (texCounter),
+	  arrayCount (var.array_count),
+	  changed    (false)
+{
+	defaultValue.resize(var.default_val.num);
+	memcpy(defaultValue.data(), var.default_val.array, var.default_val.num);
+
+	if (type == SHADER_PARAM_TEXTURE)
+		texCounter++;
+	else
+		textureID = 0;
+}
+
+static inline void AddParam(shader_var &var, vector<shader_param> &params,
+		uint32_t &texCounter)
+{
+	if (var.var_type != SHADER_VAR_UNIFORM ||
+	    strcmp(var.type, "sampler") == 0)
+		return;
+
+	params.push_back(shader_param(var, texCounter));
+}
+
+void ShaderProcessor::BuildParams(vector<shader_param> &params)
+{
+	uint32_t texCounter = 0;
+
+	for (size_t i = 0; i < parser.params.num; i++)
+		AddParam(parser.params.array[i], params, texCounter);
+}
+
+static inline void AddSampler(device_t device, shader_sampler &sampler,
+		vector<ShaderSampler> &samplers)
+{
+	gs_sampler_info si;
+	shader_sampler_convert(&sampler, &si);
+	samplers.push_back(ShaderSampler(sampler.name, device, &si));
+}
+
+void ShaderProcessor::BuildSamplers(vector<ShaderSampler> &samplers)
+{
+	for (size_t i = 0; i < parser.samplers.num; i++)
+		AddSampler(device, parser.samplers.array[i], samplers);
+}
+
+void ShaderProcessor::BuildString(string &outputString)
+{
+	stringstream output;
+	cf_token *token = cf_preprocessor_gettokens(&parser.cfp.pp);
+	while (token->type != CFTOKEN_NONE) {
+		/* cheaply just replace specific tokens */
+		if (strref_cmp(&token->str, "POSITION") == 0)
+			output << "SV_Position";
+		else if (strref_cmp(&token->str, "TARGET") == 0)
+			output << "SV_Target";
+		else if (strref_cmp(&token->str, "texture2d") == 0)
+			output << "Texture2D";
+		else if (strref_cmp(&token->str, "texture3d") == 0)
+			output << "Texture3D";
+		else if (strref_cmp(&token->str, "texture_cube") == 0)
+			output << "TextureCube";
+		else if (strref_cmp(&token->str, "sampler_state") == 0)
+			output << "SamplerState";
+		else
+			output.write(token->str.array, token->str.len);
+
+		token++;
+	}
+
+	outputString = move(output.str());
+}
+
+void ShaderProcessor::Process(const char *shader_string, const char *file)
+{
+	if (!shader_parse(&parser, shader_string, file)) {
+		char *str = error_data_buildstring(&parser.cfp.error_list);
+		if (str) {
+			blog(LOG_WARNING, "Shader parser errors/warnings:\n"
+			                  "%s\n", str);
+			bfree(str);
+		}
+		throw "Failed to parse shader";
+	}
+}

+ 40 - 0
libobs-d3d11/GS_D3D11ShaderProcessor.hpp

@@ -0,0 +1,40 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include "graphics/shader-parser.h"
+
+struct ShaderParser : shader_parser {
+	inline ShaderParser()  {shader_parser_init(this);}
+	inline ~ShaderParser() {shader_parser_free(this);}
+};
+
+struct ShaderProcessor {
+	device_t device;
+	ShaderParser parser;
+
+	void BuildInputLayout(vector<D3D11_INPUT_ELEMENT_DESC> &inputs);
+	void BuildParams(vector<shader_param> &params);
+	void BuildSamplers(vector<ShaderSampler> &samplers);
+	void BuildString(string &outputString);
+	void Process(const char *shader_string, const char *file);
+
+	inline ShaderProcessor(device_t device) : device(device)
+	{
+	}
+};

+ 44 - 0
libobs-d3d11/GS_D3D11StageSurf.cpp

@@ -0,0 +1,44 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "GS_D3D11SubSystem.hpp"
+
+gs_stage_surface::gs_stage_surface(device_t device, uint32_t width,
+		uint32_t height, gs_color_format colorFormat)
+	: device     (device),
+	  width      (width),
+	  height     (height),
+	  format     (colorFormat),
+	  dxgiFormat (ConvertGSTextureFormat(colorFormat))
+{
+	D3D11_TEXTURE2D_DESC td;
+	HRESULT hr;
+
+	memset(&td, 0, sizeof(td));
+	td.Width            = width;
+	td.Height           = height;
+	td.MipLevels        = 1;
+	td.ArraySize        = 1;
+	td.Format           = dxgiFormat;
+	td.SampleDesc.Count = 1;
+	td.CPUAccessFlags   = D3D11_CPU_ACCESS_READ;
+	td.Usage            = D3D11_USAGE_STAGING;
+
+	hr = device->device->CreateTexture2D(&td, NULL, texture.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create 2D texture", hr);
+}

+ 1574 - 0
libobs-d3d11/GS_D3D11SubSystem.cpp

@@ -0,0 +1,1574 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "util/base.h"
+#include "util/platform.h"
+#include "graphics/matrix3.h"
+#include "GS_D3D11SubSystem.hpp"
+
+static const IID dxgiFactory2 =
+{0x50c83a1c, 0xe072, 0x4c48, {0x87, 0xb0, 0x36, 0x30, 0xfa, 0x36, 0xa6, 0xd0}};
+
+static inline void make_swap_desc(DXGI_SWAP_CHAIN_DESC &desc,
+		gs_init_data *data)
+{
+	memset(&desc, 0, sizeof(desc));
+	desc.BufferCount       = data->num_backbuffers;
+	desc.BufferDesc.Format = ConvertGSTextureFormat(data->format);
+	desc.BufferDesc.Width  = data->cx;
+	desc.BufferDesc.Height = data->cy;
+	desc.BufferUsage       = DXGI_USAGE_RENDER_TARGET_OUTPUT;
+	desc.OutputWindow      = (HWND)data->hwnd;
+	desc.SampleDesc.Count  = 1;
+	desc.Windowed          = true;
+}
+
+void gs_swap_chain::InitTarget(uint32_t cx, uint32_t cy)
+{
+	HRESULT hr;
+
+	target.width  = cx;
+	target.height = cy;
+
+	hr = swap->GetBuffer(0, __uuidof(ID3D11Texture2D),
+			(void**)target.texture.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to get swap buffer texture", hr);
+
+	hr = device->device->CreateRenderTargetView(target.texture, NULL,
+			target.renderTarget[0].Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create swap render target view", hr);
+}
+
+void gs_swap_chain::InitZStencilBuffer(uint32_t cx, uint32_t cy)
+{
+	zs.width  = cx;
+	zs.height = cy;
+
+	if (zs.format != GS_ZS_NONE && cx != 0 && cy != 0) {
+		zs.InitBuffer();
+	} else {
+		zs.texture.Clear();
+		zs.view.Clear();
+	}
+}
+
+void gs_swap_chain::Resize(uint32_t cx, uint32_t cy)
+{
+	RECT clientRect;
+	HRESULT hr;
+
+	target.texture.Clear();
+	target.renderTarget[0].Clear();
+	zs.texture.Clear();
+	zs.view.Clear();
+
+	if (cx == 0 || cy == 0) {
+		GetClientRect(hwnd, &clientRect);
+		if (cx == 0) cx = clientRect.right;
+		if (cy == 0) cy = clientRect.bottom;
+	}
+
+	hr = swap->ResizeBuffers(numBuffers, cx, cy, target.dxgiFormat, 0);
+	if (FAILED(hr))
+		throw HRError("Failed to resize swap buffers", hr);
+
+	InitTarget(cx, cy);
+	InitZStencilBuffer(cx, cy);
+}
+
+void gs_swap_chain::Init(gs_init_data *data)
+{
+	target.device         = device;
+	target.isRenderTarget = true;
+	target.format         = data->format;
+	target.dxgiFormat     = ConvertGSTextureFormat(data->format);
+	InitTarget(data->cx, data->cy);
+
+	zs.device     = device;
+	zs.format     = data->zsformat;
+	zs.dxgiFormat = ConvertGSZStencilFormat(data->zsformat);
+	InitZStencilBuffer(data->cx, data->cy);
+}
+
+gs_swap_chain::gs_swap_chain(gs_device *device, gs_init_data *data)
+	: device     (device),
+	  numBuffers (data->num_backbuffers),
+	  hwnd       ((HWND)data->hwnd)
+{
+	HRESULT hr;
+	DXGI_SWAP_CHAIN_DESC swapDesc;
+
+	make_swap_desc(swapDesc, data);
+	hr = device->factory->CreateSwapChain(device->device, &swapDesc,
+			swap.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create swap chain", hr);
+
+	Init(data);
+}
+
+void gs_device::InitFactory(uint32_t adapterIdx, IDXGIAdapter1 **padapter)
+{
+	HRESULT hr;
+	IID factoryIID = (GetWinVer() >= 0x602) ? dxgiFactory2 :
+		__uuidof(IDXGIFactory1);
+
+	hr = CreateDXGIFactory1(factoryIID, (void**)factory.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create DXGIFactory", hr);
+
+	hr = factory->EnumAdapters1(adapterIdx, padapter);
+	if (FAILED(hr))
+		throw HRError("Failed to enumerate DXGIAdapter", hr);
+}
+
+const static D3D_FEATURE_LEVEL featureLevels[] =
+{
+	D3D_FEATURE_LEVEL_11_0,
+	D3D_FEATURE_LEVEL_10_1,
+	D3D_FEATURE_LEVEL_10_0,
+	D3D_FEATURE_LEVEL_9_3,
+};
+
+void gs_device::InitDevice(gs_init_data *data, IDXGIAdapter *adapter)
+{
+	wstring adapterName;
+	DXGI_SWAP_CHAIN_DESC swapDesc;
+	DXGI_ADAPTER_DESC desc;
+	D3D_FEATURE_LEVEL levelUsed;
+	HRESULT hr;
+
+	make_swap_desc(swapDesc, data);
+
+	uint32_t createFlags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
+#ifdef _DEBUG
+	createFlags |= D3D11_CREATE_DEVICE_DEBUG;
+#endif
+
+	adapterName = (adapter->GetDesc(&desc) == S_OK) ? desc.Description :
+		L"<unknown>";
+
+	char *adapterNameUTF8;
+	os_wcs_to_utf8(adapterName.c_str(), 0, &adapterNameUTF8);
+	blog(LOG_INFO, "Loading up D3D11 on adapter %s", adapterNameUTF8);
+	bfree(adapterNameUTF8);
+
+	hr = D3D11CreateDeviceAndSwapChain(adapter, D3D_DRIVER_TYPE_UNKNOWN,
+			NULL, createFlags, featureLevels,
+			sizeof(featureLevels) / sizeof(D3D_FEATURE_LEVEL),
+			D3D11_SDK_VERSION, &swapDesc,
+			defaultSwap.swap.Assign(), device.Assign(),
+			&levelUsed, context.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create device and swap chain", hr);
+
+	blog(LOG_INFO, "D3D11 loaded sucessfully, feature level used: %u",
+			(uint32_t)levelUsed);
+
+	defaultSwap.device     = this;
+	defaultSwap.hwnd       = (HWND)data->hwnd;
+	defaultSwap.numBuffers = data->num_backbuffers;
+	defaultSwap.Init(data);
+}
+
+static inline void ConvertStencilSide(D3D11_DEPTH_STENCILOP_DESC &desc,
+		const StencilSide &side)
+{
+	desc.StencilFunc        = ConvertGSDepthTest(side.test);
+	desc.StencilFailOp      = ConvertGSStencilOp(side.fail);
+	desc.StencilDepthFailOp = ConvertGSStencilOp(side.zfail);
+	desc.StencilPassOp      = ConvertGSStencilOp(side.zpass);
+}
+
+ID3D11DepthStencilState *gs_device::AddZStencilState()
+{
+	HRESULT hr;
+	D3D11_DEPTH_STENCIL_DESC dsd;
+	SavedZStencilState savedState(zstencilState);
+	ID3D11DepthStencilState *state;
+
+	dsd.DepthEnable      = zstencilState.depthEnabled;
+	dsd.DepthFunc        = ConvertGSDepthTest(zstencilState.depthFunc);
+	dsd.DepthWriteMask   = zstencilState.depthWriteEnabled ?
+		D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
+	dsd.StencilEnable    = zstencilState.stencilEnabled;
+	dsd.StencilReadMask  = D3D11_DEFAULT_STENCIL_READ_MASK;
+	dsd.StencilWriteMask = zstencilState.stencilWriteEnabled ?
+		D3D11_DEFAULT_STENCIL_WRITE_MASK : 0;
+	ConvertStencilSide(dsd.FrontFace, zstencilState.stencilFront);
+	ConvertStencilSide(dsd.BackFace,  zstencilState.stencilBack);
+
+	hr = device->CreateDepthStencilState(&dsd, savedState.state.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create depth stencil state", hr);
+
+	state = savedState.state;
+	zstencilStates.push_back(savedState);
+
+	return state;
+}
+
+ID3D11RasterizerState *gs_device::AddRasterState()
+{
+	HRESULT hr;
+	D3D11_RASTERIZER_DESC rd;
+	SavedRasterState savedState(rasterState);
+	ID3D11RasterizerState *state;
+
+	memset(&rd, 0, sizeof(rd));
+	rd.FillMode        = D3D11_FILL_SOLID;
+	rd.CullMode        = ConvertGSCullMode(rasterState.cullMode);
+	rd.DepthClipEnable = true;
+	rd.ScissorEnable   = rasterState.scissorEnabled;
+
+	hr = device->CreateRasterizerState(&rd, savedState.state.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create rasterizer state", hr);
+
+	state = savedState.state;
+	rasterStates.push_back(savedState);
+
+	return state;
+}
+
+ID3D11BlendState *gs_device::AddBlendState()
+{
+	HRESULT hr;
+	D3D11_BLEND_DESC bd;
+	SavedBlendState savedState(blendState);
+	ID3D11BlendState *state;
+
+	memset(&bd, 0, sizeof(bd));
+	for (int i = 0; i < 8; i++) {
+		bd.RenderTarget[i].BlendEnable    = blendState.blendEnabled;
+		bd.RenderTarget[i].BlendOp        = D3D11_BLEND_OP_ADD;
+		bd.RenderTarget[i].BlendOpAlpha   = D3D11_BLEND_OP_ADD;
+		bd.RenderTarget[i].SrcBlendAlpha  = D3D11_BLEND_ONE;
+		bd.RenderTarget[i].DestBlendAlpha = D3D11_BLEND_ZERO;
+		bd.RenderTarget[i].SrcBlend =
+			ConvertGSBlendType(blendState.srcFactor);
+		bd.RenderTarget[i].DestBlend =
+			ConvertGSBlendType(blendState.destFactor);
+		bd.RenderTarget[i].RenderTargetWriteMask =
+			D3D11_COLOR_WRITE_ENABLE_ALL;
+	}
+
+	hr = device->CreateBlendState(&bd, savedState.state.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create disabled blend state", hr);
+
+	state = savedState.state;
+	blendStates.push_back(savedState);
+
+	return state;
+}
+
+void gs_device::UpdateZStencilState()
+{
+	ID3D11DepthStencilState *state = NULL;
+
+	if (!zstencilStateChanged)
+		return;
+
+	for (size_t i = 0; i < zstencilStates.size(); i++) {
+		SavedZStencilState &s = zstencilStates[i];
+		if (memcmp(&s, &zstencilState, sizeof(zstencilState)) == 0) {
+			state = s.state;
+			break;
+		}
+	}
+
+	if (!state)
+		state = AddZStencilState();
+
+	if (state != curDepthStencilState) {
+		context->OMSetDepthStencilState(state, 0);
+		curDepthStencilState = state;
+	}
+
+	zstencilStateChanged = false;
+}
+
+void gs_device::UpdateRasterState()
+{
+	ID3D11RasterizerState *state = NULL;
+
+	if (!rasterStateChanged)
+		return;
+
+	for (size_t i = 0; i < rasterStates.size(); i++) {
+		SavedRasterState &s = rasterStates[i];
+		if (memcmp(&s, &rasterState, sizeof(rasterState)) == 0) {
+			state = s.state;
+			break;
+		}
+	}
+
+	if (!state)
+		state = AddRasterState();
+
+	if (state != curRasterState) {
+		context->RSSetState(state);
+		curRasterState = state;
+	}
+
+	rasterStateChanged = false;
+}
+
+void gs_device::UpdateBlendState()
+{
+	ID3D11BlendState *state = NULL;
+
+	if (!blendStateChanged)
+		return;
+
+	for (size_t i = 0; i < blendStates.size(); i++) {
+		SavedBlendState &s = blendStates[i];
+		if (memcmp(&s, &blendState, sizeof(blendState)) == 0) {
+			state = s.state;
+			break;
+		}
+	}
+
+	if (!state)
+		state = AddBlendState();
+
+	if (state != curBlendState) {
+		float f[4] = {1.0f, 1.0f, 1.0f, 1.0f};
+		context->OMSetBlendState(state, f, 0xFFFFFFFF);
+		curBlendState = state;
+	}
+
+	blendStateChanged = false;
+}
+
+void gs_device::UpdateViewProjMatrix()
+{
+	matrix3 cur_matrix;
+	gs_matrix_get(&cur_matrix);
+
+	matrix4_from_matrix3(&curViewMatrix, &cur_matrix);
+	matrix4_mul(&curViewProjMatrix, &curViewMatrix, &curProjMatrix);
+	matrix4_transpose(&curViewProjMatrix, &curViewProjMatrix);
+
+	if (curVertexShader->viewProj)
+		shader_setmatrix4(curVertexShader, curVertexShader->viewProj,
+				&curViewProjMatrix);
+}
+
+gs_device::gs_device(gs_init_data *data)
+	: curRenderTarget      (NULL),
+	  curZStencilBuffer    (NULL),
+	  curRenderSide        (0),
+	  curIndexBuffer       (NULL),
+	  curVertexBuffer      (NULL),
+	  curVertexShader      (NULL),
+	  curPixelShader       (NULL),
+	  curSwapChain         (&defaultSwap),
+	  zstencilStateChanged (true),
+	  rasterStateChanged   (true),
+	  blendStateChanged    (true),
+	  curDepthStencilState (NULL),
+	  curRasterState       (NULL),
+	  curBlendState        (NULL),
+	  curToplogy           (D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED)
+{
+	ComPtr<IDXGIAdapter1> adapter;
+
+	matrix4_identity(&curProjMatrix);
+	matrix4_identity(&curViewMatrix);
+	matrix4_identity(&curViewProjMatrix);
+
+	memset(&viewport, 0, sizeof(viewport));
+
+	for (size_t i = 0; i < MAX_TEXTURES; i++) {
+		curTextures[i] = NULL;
+		curSamplers[i] = NULL;
+	}
+
+	InitFactory(data->adapter, adapter.Assign());
+	InitDevice(data, adapter);
+	device_setrendertarget(this, NULL, NULL);
+}
+
+gs_device *device_create(gs_init_data *data)
+{
+	gs_device *device = NULL;
+
+	try {
+		device = new gs_device(data);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create (D3D11): %s (%08lX)", error.str,
+				error.hr);
+	}
+
+	return device;
+}
+
+void device_destroy(device_t device)
+{
+	delete device;
+}
+
+swapchain_t device_create_swapchain(device_t device, struct gs_init_data *data)
+{
+	gs_swap_chain *swap = NULL;
+
+	try {
+		swap = new gs_swap_chain(device, data);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_swapchain (D3D11): %s (%08lX)",
+				error.str, error.hr);
+	}
+
+	return swap;
+}
+
+void device_resize(device_t device, uint32_t cx, uint32_t cy)
+{
+	try {
+		ID3D11RenderTargetView *renderView = NULL;
+		ID3D11DepthStencilView *depthView  = NULL;
+		int i = device->curRenderSide;
+
+		device->context->OMSetRenderTargets(1, &renderView, depthView);
+		device->curSwapChain->Resize(cx, cy);
+
+		if (device->curRenderTarget)
+			renderView = device->curRenderTarget->renderTarget[i];
+		if (device->curZStencilBuffer)
+			depthView  = device->curZStencilBuffer->view;
+		device->context->OMSetRenderTargets(1, &renderView, depthView);
+
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_resize (D3D11): %s (%08lX)",
+				error.str, error.hr);
+	}
+}
+
+void device_getsize(device_t device, uint32_t *cx, uint32_t *cy)
+{
+	*cx = device->curSwapChain->target.width;
+	*cy = device->curSwapChain->target.height;
+}
+
+uint32_t device_getwidth(device_t device)
+{
+	return device->curSwapChain->target.width;
+}
+
+uint32_t device_getheight(device_t device)
+{
+	return device->curSwapChain->target.height;
+}
+
+texture_t device_create_texture(device_t device, uint32_t width,
+		uint32_t height, enum gs_color_format color_format, void *data,
+		uint32_t flags)
+{
+	gs_texture *texture = NULL;
+	try {
+		texture = new gs_texture_2d(device, width, height, color_format,
+				data, flags, false, false, false);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_texture (D3D11): %s (%08lX)",
+				error.str, error.hr);
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_create_texture (D3D11): %s", error);
+	}
+
+	return texture;
+}
+
+texture_t device_create_cubetexture(device_t device, uint32_t size,
+		enum gs_color_format color_format, void *data[6],
+		uint32_t flags)
+{
+	gs_texture *texture = NULL;
+	try {
+		texture = new gs_texture_2d(device, size, size, color_format,
+				data, flags, true, false, false);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_cubetexture (D3D11): %s "
+		                "(%08lX)",
+		                error.str, error.hr);
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_create_cubetexture (D3D11): %s",
+				error);
+	}
+
+	return texture;
+}
+
+texture_t device_create_volumetexture(device_t device, uint32_t width,
+		uint32_t height, uint32_t depth,
+		enum gs_color_format color_format, void *data, uint32_t flags)
+{
+	/* TODO */
+	return NULL;
+}
+
+zstencil_t device_create_zstencil(device_t device, uint32_t width,
+		uint32_t height, enum gs_zstencil_format format)
+{
+	gs_zstencil_buffer *zstencil = NULL;
+	try {
+		zstencil = new gs_zstencil_buffer(device, width, height,
+				format);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_zstencil (D3D11): %s (%08lX)",
+				error.str, error.hr);
+	}
+
+	return zstencil;
+}
+
+stagesurf_t device_create_stagesurface(device_t device, uint32_t width,
+		uint32_t height, enum gs_color_format color_format)
+{
+	gs_stage_surface *surf = NULL;
+	try {
+		surf = new gs_stage_surface(device, width, height,
+				color_format);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_stagesurface (D3D11): %s "
+		                "(%08lX)",
+				error.str, error.hr);
+	}
+
+	return surf;
+}
+
+samplerstate_t device_create_samplerstate(device_t device,
+		struct gs_sampler_info *info)
+{
+	gs_sampler_state *ss = NULL;
+	try {
+		ss = new gs_sampler_state(device, info);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_samplerstate (D3D11): %s "
+		                "(%08lX)",
+				error.str, error.hr);
+	}
+
+	return ss;
+}
+
+shader_t device_create_vertexshader(device_t device,
+		const char *shader_string, const char *file,
+		char **error_string)
+{
+	gs_vertex_shader *shader = NULL;
+	try {
+		shader = new gs_vertex_shader(device, file, shader_string);
+
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_vertexshader (D3D11): %s "
+		                "(%08lX)",
+				error.str, error.hr);
+
+	} catch (ShaderError error) {
+		const char *buf = (const char*)error.errors->GetBufferPointer();
+		if (error_string)
+			*error_string = bstrdup(buf);
+		blog(LOG_ERROR, "device_create_vertexshader (D3D11): "
+		                "Compile errors for %s:\n%s",
+		                file, buf);
+
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_create_vertexshader (D3D11): %s",
+				error);
+	}
+
+	return shader;
+}
+
+shader_t device_create_pixelshader(device_t device,
+		const char *shader_string, const char *file,
+		char **error_string)
+{
+	gs_pixel_shader *shader = NULL;
+	try {
+		shader = new gs_pixel_shader(device, file, shader_string);
+
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_pixelshader (D3D11): %s "
+		                "(%08lX)",
+				error.str, error.hr);
+
+	} catch (ShaderError error) {
+		const char *buf = (const char*)error.errors->GetBufferPointer();
+		if (error_string)
+			*error_string = bstrdup(buf);
+		blog(LOG_ERROR, "device_create_pixelshader (D3D11): "
+		                "Compiler errors for %s:\n%s",
+		                file, buf);
+
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_create_pixelshader (D3D11): %s",
+				error);
+	}
+
+	return shader;
+}
+
+vertbuffer_t device_create_vertexbuffer(device_t device,
+		struct vb_data *data, uint32_t flags)
+{
+	gs_vertex_buffer *buffer = NULL;
+	try {
+		buffer = new gs_vertex_buffer(device, data, flags);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_vertexbuffer (D3D11): %s "
+		                "(%08lX)",
+				error.str, error.hr);
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_create_vertexbuffer (D3D11): %s",
+				error);
+	}
+
+	return buffer;
+}
+
+indexbuffer_t device_create_indexbuffer(device_t device,
+		enum gs_index_type type, void *indices, size_t num,
+		uint32_t flags)
+{
+	gs_index_buffer *buffer = NULL;
+	try {
+		buffer = new gs_index_buffer(device, type, indices, num, flags);
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_create_indexbuffer (D3D11): %s (%08lX)",
+				error.str, error.hr);
+	}
+
+	return buffer;
+}
+
+enum gs_texture_type device_gettexturetype(device_t device, texture_t texture)
+{
+	return texture->type;
+}
+
+void device_load_vertexbuffer(device_t device, vertbuffer_t vertbuffer)
+{
+	if (device->curVertexBuffer == vertbuffer)
+		return;
+
+	device->curVertexBuffer = vertbuffer;
+
+	if (!device->curVertexShader)
+		return;
+
+	vector<ID3D11Buffer*> buffers;
+	vector<uint32_t> strides;
+	vector<uint32_t> offsets;
+
+	if (vertbuffer) {
+		vertbuffer->MakeBufferList(device->curVertexShader,
+				buffers, strides);
+	} else {
+		size_t buffersToClear =
+			device->curVertexShader->NumBuffersExpected();
+		buffers.resize(buffersToClear);
+		strides.resize(buffersToClear);
+	}
+
+	offsets.resize(buffers.size());
+	device->context->IASetVertexBuffers(0, (UINT)buffers.size(),
+			buffers.data(), strides.data(), offsets.data());
+}
+
+void device_load_indexbuffer(device_t device, indexbuffer_t indexbuffer)
+{
+	DXGI_FORMAT format;
+	ID3D11Buffer *buffer;
+
+	if (device->curIndexBuffer == indexbuffer)
+		return;
+
+	if (indexbuffer) {
+		switch (indexbuffer->indexSize) {
+		case 2: format = DXGI_FORMAT_R16_UINT; break;
+		case 4: format = DXGI_FORMAT_R32_UINT; break;
+		}
+
+		buffer = indexbuffer->indexBuffer;
+	} else {
+		buffer = NULL;
+		format = DXGI_FORMAT_R32_UINT;
+	}
+
+	device->curIndexBuffer = indexbuffer;
+	device->context->IASetIndexBuffer(buffer, format, 0);
+}
+
+void device_load_texture(device_t device, texture_t tex, int unit)
+{
+	ID3D11ShaderResourceView *view = NULL;
+
+	if (device->curTextures[unit] == tex)
+		return;
+
+	if (tex)
+		view = tex->shaderRes;
+
+	device->curTextures[unit] = tex;
+	device->context->PSSetShaderResources(unit, 1, &view);
+}
+
+void device_load_samplerstate(device_t device,
+		samplerstate_t samplerstate, int unit)
+{
+	ID3D11SamplerState *state = NULL;
+
+	if (device->curSamplers[unit] == samplerstate)
+		return;
+
+	if (samplerstate)
+		state = samplerstate->state;
+
+	device->curSamplers[unit] = samplerstate;
+	device->context->PSSetSamplers(unit, 1, &state);
+}
+
+void device_load_vertexshader(device_t device, shader_t vertshader)
+{
+	ID3D11VertexShader *shader    = NULL;
+	ID3D11InputLayout  *layout    = NULL;
+	ID3D11Buffer       *constants = NULL;
+
+	if (device->curVertexShader == vertshader)
+		return;
+
+	gs_vertex_shader *vs = static_cast<gs_vertex_shader*>(vertshader);
+	gs_vertex_buffer *curVB = device->curVertexBuffer;
+
+	if (vertshader) {
+		if (vertshader->type != SHADER_VERTEX) {
+			blog(LOG_ERROR, "device_load_vertexshader (D3D11): "
+			                "Shader is not a vertex shader");
+			return;
+		}
+
+		if (curVB)
+			device_load_vertexbuffer(device, NULL);
+
+		shader    = vs->shader;
+		layout    = vs->layout;
+		constants = vs->constants;
+	}
+
+	device->curVertexShader = vs;
+	device->context->VSSetShader(shader, NULL, 0);
+	device->context->IASetInputLayout(layout);
+	device->context->VSSetConstantBuffers(0, 1, &constants);
+
+	if (vertshader && curVB)
+		device_load_vertexbuffer(device, curVB);
+}
+
+static inline void clear_textures(device_t device)
+{
+	ID3D11ShaderResourceView *views[MAX_TEXTURES];
+	memset(views,               0, sizeof(views));
+	memset(device->curTextures, 0, sizeof(device->curTextures));
+	device->context->PSSetShaderResources(0, MAX_TEXTURES, views);
+}
+
+void device_load_pixelshader(device_t device, shader_t pixelshader)
+{
+	ID3D11PixelShader  *shader    = NULL;
+	ID3D11Buffer       *constants = NULL;
+	ID3D11SamplerState *states[MAX_TEXTURES];
+
+	if (device->curPixelShader == pixelshader)
+		return;
+
+	gs_pixel_shader *ps = static_cast<gs_pixel_shader*>(pixelshader);
+
+	if (pixelshader) {
+		if (pixelshader->type != SHADER_PIXEL) {
+			blog(LOG_ERROR, "device_load_pixelshader (D3D11): "
+			                "Shader is not a pixel shader");
+			return;
+		}
+
+		shader    = ps->shader;
+		constants = ps->constants;
+		ps->GetSamplerStates(states);
+	} else {
+		memset(states, 0, sizeof(states));
+	}
+
+	clear_textures(device);
+
+	device->curPixelShader = ps;
+	device->context->PSSetShader(shader, NULL, 0);
+	device->context->PSSetConstantBuffers(0, 1, &constants);
+	device->context->PSSetSamplers(0, MAX_TEXTURES, states);
+}
+
+void device_load_defaultsamplerstate(device_t device, bool b_3d,
+		int unit)
+{
+	/* TODO */
+}
+
+shader_t device_getvertexshader(device_t device)
+{
+	return device->curVertexShader;
+}
+
+shader_t device_getpixelshader(device_t device)
+{
+	return device->curPixelShader;
+}
+
+texture_t device_getrendertarget(device_t device)
+{
+	if (device->curRenderTarget == &device->curSwapChain->target)
+		return NULL;
+
+	return device->curRenderTarget;
+}
+
+zstencil_t device_getzstenciltarget(device_t device)
+{
+	if (device->curZStencilBuffer == &device->curSwapChain->zs)
+		return NULL;
+
+	return device->curZStencilBuffer;
+}
+
+void device_setrendertarget(device_t device, texture_t tex, zstencil_t zstencil)
+{
+	if (!tex)
+		tex = &device->curSwapChain->target;
+	if (!zstencil)
+		zstencil = &device->curSwapChain->zs;
+
+	if (device->curRenderTarget   == tex &&
+	    device->curZStencilBuffer == zstencil)
+		return;
+
+	if (tex->type != GS_TEXTURE_2D) {
+		blog(LOG_ERROR, "device_setrendertarget (D3D11): "
+				"texture is not a 2D texture");
+		return;
+	}
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(tex);
+	if (!tex2d->renderTarget[0]) {
+		blog(LOG_ERROR, "device_setrendertarget (D3D11): "
+				"texture is not a render target");
+		return;
+	}
+
+	ID3D11RenderTargetView *rt = tex2d->renderTarget[0];
+
+	device->curRenderTarget   = tex2d;
+	device->curRenderSide     = 0;
+	device->curZStencilBuffer = zstencil;
+	device->context->OMSetRenderTargets(1, &rt, zstencil->view);
+}
+
+void device_setcuberendertarget(device_t device, texture_t tex, int side,
+		zstencil_t zstencil)
+{
+	if (!tex) {
+		tex = &device->curSwapChain->target;
+		side = 0;
+	}
+
+	if (!zstencil)
+		zstencil = &device->curSwapChain->zs;
+
+	if (device->curRenderTarget   == tex  &&
+	    device->curRenderSide     == side &&
+	    device->curZStencilBuffer == zstencil)
+		return;
+
+	if (tex->type != GS_TEXTURE_CUBE) {
+		blog(LOG_ERROR, "device_setcuberendertarget (D3D11): "
+		                "texture is not a cube texture");
+		return;
+	}
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(tex);
+	if (!tex2d->renderTarget[side]) {
+		blog(LOG_ERROR, "device_setcuberendertarget (D3D11): "
+				"texture is not a render target");
+		return;
+	}
+
+	ID3D11RenderTargetView *rt = tex2d->renderTarget[0];
+
+	device->curRenderTarget   = tex2d;
+	device->curRenderSide     = side;
+	device->curZStencilBuffer = zstencil;
+	device->context->OMSetRenderTargets(1, &rt, zstencil->view);
+}
+
+inline void gs_device::CopyTex(ID3D11Texture2D *dst, texture_t src)
+{
+	if (!src)
+		src = curRenderTarget;
+	if (src->type != GS_TEXTURE_2D)
+		throw "Source texture must be a 2D texture";
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(src);
+	context->CopyResource(dst, tex2d->texture);
+}
+
+void device_copy_texture(device_t device, texture_t dst, texture_t src)
+{
+	try {
+		if (!dst)
+			throw "Failed because destination texture is NULL";
+		if (dst->type != GS_TEXTURE_2D)
+			throw "Destination texture must be a 2D texture";
+
+		gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(dst);
+		device->CopyTex(tex2d->texture, src);
+
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_copy_texture (D3D11): %s", error);
+	}
+}
+
+void device_stage_texture(device_t device, stagesurf_t dst, texture_t src)
+{
+	try {
+		if (!dst)
+			throw "Failed because destination texture is NULL";
+
+		device->CopyTex(dst->texture, src);
+
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_copy_texture (D3D11): %s", error);
+	}
+}
+
+void device_beginscene(device_t device)
+{
+	clear_textures(device);
+}
+
+void device_draw(device_t device, enum gs_draw_mode draw_mode,
+		uint32_t start_vert, uint32_t num_verts)
+{
+	try {
+		if (!device->curVertexShader)
+			throw "No vertex shader specified";
+
+		if (!device->curPixelShader)
+			throw "No pixel shader specified";
+
+		if (!device->curVertexBuffer)
+			throw "No vertex buffer specified";
+
+		effect_t effect = gs_geteffect();
+		if (effect)
+			effect_updateparams(effect);
+
+		device->UpdateBlendState();
+		device->UpdateRasterState();
+		device->UpdateZStencilState();
+		device->UpdateViewProjMatrix();
+		device->curVertexShader->UploadParams();
+		device->curPixelShader->UploadParams();
+
+	} catch (const char *error) {
+		blog(LOG_ERROR, "device_draw (D3D11): %s", error);
+		return;
+
+	} catch (HRError error) {
+		blog(LOG_ERROR, "device_draw (D3D11): %s (%08lX)", error.str,
+				error.hr);
+		return;
+	}
+
+	D3D10_PRIMITIVE_TOPOLOGY newTopology = ConvertGSTopology(draw_mode);
+	if (device->curToplogy != newTopology) {
+		device->context->IASetPrimitiveTopology(newTopology);
+		device->curToplogy = newTopology;
+	}
+
+	if (num_verts == 0)
+		num_verts = (uint32_t)device->curVertexBuffer->numVerts;
+
+	if (device->curIndexBuffer)
+		device->context->DrawIndexed(num_verts, start_vert, 0);
+	else
+		device->context->Draw(num_verts, start_vert);
+}
+
+void device_endscene(device_t device)
+{
+	/* does nothing in D3D11 */
+}
+
+void device_load_swapchain(device_t device, swapchain_t swapchain)
+{
+	texture_t  target = device->curRenderTarget;
+	zstencil_t zs     = device->curZStencilBuffer;
+	bool is_cube = device->curRenderTarget->type == GS_TEXTURE_CUBE;
+
+	if (target == &device->curSwapChain->target)
+		target = NULL;
+	if (zs == &device->curSwapChain->zs)
+		zs = NULL;
+
+	if (swapchain == NULL)
+		swapchain = &device->defaultSwap;
+
+	device->curSwapChain = swapchain;
+
+	if (is_cube)
+		device_setcuberendertarget(device, target,
+				device->curRenderSide, zs);
+	else
+		device_setrendertarget(device, target, zs);
+}
+
+void device_clear(device_t device, uint32_t clear_flags, struct vec4 *color,
+		float depth, uint8_t stencil)
+{
+	int side = device->curRenderSide;
+	if ((clear_flags & GS_CLEAR_COLOR) != 0 && device->curRenderTarget)
+		device->context->ClearRenderTargetView(
+				device->curRenderTarget->renderTarget[side],
+				color->ptr);
+
+	if (device->curZStencilBuffer) {
+		uint32_t flags = 0;
+		if ((clear_flags & GS_CLEAR_DEPTH) != 0)
+			flags |= D3D11_CLEAR_DEPTH;
+		if ((clear_flags & GS_CLEAR_STENCIL) != 0)
+			flags |= D3D11_CLEAR_STENCIL;
+
+		if (flags && device->curZStencilBuffer->view)
+			device->context->ClearDepthStencilView(
+					device->curZStencilBuffer->view,
+					flags, depth, stencil);
+	}
+}
+
+void device_present(device_t device)
+{
+	device->curSwapChain->swap->Present(0, 0);
+}
+
+void device_setcullmode(device_t device, enum gs_cull_mode mode)
+{
+	if (mode == device->rasterState.cullMode)
+		return;
+
+	device->rasterState.cullMode = mode;
+	device->rasterStateChanged = true;
+}
+
+enum gs_cull_mode device_getcullmode(device_t device)
+{
+	return device->rasterState.cullMode;
+}
+
+void device_enable_blending(device_t device, bool enable)
+{
+	if (enable == device->blendState.blendEnabled)
+		return;
+
+	device->blendState.blendEnabled = enable;
+	device->blendStateChanged = true;
+}
+
+void device_enable_depthtest(device_t device, bool enable)
+{
+	if (enable == device->zstencilState.depthEnabled)
+		return;
+
+	device->zstencilState.depthEnabled = enable;
+	device->zstencilStateChanged = true;
+}
+
+void device_enable_stenciltest(device_t device, bool enable)
+{
+	if (enable == device->zstencilState.stencilEnabled)
+		return;
+
+	device->zstencilState.stencilEnabled = enable;
+	device->zstencilStateChanged = true;
+}
+
+void device_enable_stencilwrite(device_t device, bool enable)
+{
+	if (enable == device->zstencilState.stencilWriteEnabled)
+		return;
+
+	device->zstencilState.stencilWriteEnabled = enable;
+	device->zstencilStateChanged = true;
+}
+
+void device_enable_color(device_t device, bool red, bool blue,
+		bool green, bool alpha)
+{
+	if (device->blendState.redEnabled   == red   &&
+	    device->blendState.greenEnabled == green &&
+	    device->blendState.blueEnabled  == blue  &&
+	    device->blendState.alphaEnabled == alpha)
+		return;
+
+	device->blendState.redEnabled   = red;
+	device->blendState.greenEnabled = green;
+	device->blendState.blueEnabled  = blue;
+	device->blendState.alphaEnabled = alpha;
+	device->blendStateChanged       = true;
+}
+
+void device_blendfunction(device_t device, enum gs_blend_type src,
+		enum gs_blend_type dest)
+{
+	if (device->blendState.srcFactor  == src &&
+	    device->blendState.destFactor == dest)
+		return;
+
+	device->blendState.srcFactor  = src;
+	device->blendState.destFactor = dest;
+	device->blendStateChanged     = true;
+}
+
+void device_depthfunction(device_t device, enum gs_depth_test test)
+{
+	if (device->zstencilState.depthFunc == test)
+		return;
+
+	device->zstencilState.depthFunc = test;
+	device->zstencilStateChanged    = true;
+}
+
+static inline void update_stencilside_test(device_t device, StencilSide &side,
+		gs_depth_test test)
+{
+	if (side.test == test)
+		return;
+
+	side.test = test;
+	device->zstencilStateChanged = true;
+}
+
+void device_stencilfunction(device_t device, enum gs_stencil_side side,
+		enum gs_depth_test test)
+{
+	int sideVal = (int)side;
+
+	if (sideVal & GS_STENCIL_FRONT)
+		update_stencilside_test(device,
+				device->zstencilState.stencilFront, test);
+	if (sideVal & GS_STENCIL_BACK)
+		update_stencilside_test(device,
+				device->zstencilState.stencilBack, test);
+}
+
+static inline void update_stencilside_op(device_t device, StencilSide &side,
+		enum gs_stencil_op fail, enum gs_stencil_op zfail,
+		enum gs_stencil_op zpass)
+{
+	if (side.fail == fail && side.zfail == zfail && side.zpass == zpass)
+		return;
+
+	side.fail  = fail;
+	side.zfail = zfail;
+	side.zpass = zpass;
+	device->zstencilStateChanged = true;
+}
+
+void device_stencilop(device_t device, enum gs_stencil_side side,
+		enum gs_stencil_op fail, enum gs_stencil_op zfail,
+		enum gs_stencil_op zpass)
+{
+	int sideVal = (int)side;
+
+	if (sideVal & GS_STENCIL_FRONT)
+		update_stencilside_op(device,
+				device->zstencilState.stencilFront,
+				fail, zfail, zpass);
+	if (sideVal & GS_STENCIL_BACK)
+		update_stencilside_op(device,
+				device->zstencilState.stencilBack,
+				fail, zfail, zpass);
+}
+
+void device_enable_fullscreen(device_t device, bool enable)
+{
+	/* TODO */
+}
+
+int device_fullscreen_enabled(device_t device)
+{
+	/* TODO */
+	return 0;
+}
+
+void device_setdisplaymode(device_t device,
+		const struct gs_display_mode *mode)
+{
+	/* TODO */
+}
+
+void device_getdisplaymode(device_t device,
+		struct gs_display_mode *mode)
+{
+	/* TODO */
+}
+
+void device_setcolorramp(device_t device, float gamma, float brightness,
+		float contrast)
+{
+	/* TODO */
+}
+
+void device_setviewport(device_t device, int x, int y, int width,
+		int height)
+{
+	D3D11_VIEWPORT vp;
+	memset(&vp, 0, sizeof(vp));
+	vp.MaxDepth = 1.0f;
+	vp.TopLeftX = (float)x;
+	vp.TopLeftY = (float)y;
+	vp.Width    = (float)width;
+	vp.Height   = (float)height;
+	device->context->RSSetViewports(1, &vp);
+
+	device->viewport.x  = x;
+	device->viewport.y  = y;
+	device->viewport.cx = width;
+	device->viewport.cy = height;
+}
+
+void device_getviewport(device_t device, struct gs_rect *rect)
+{
+	memcpy(rect, &device->viewport, sizeof(gs_rect));
+}
+
+void device_setscissorrect(device_t device, struct gs_rect *rect)
+{
+	D3D11_RECT d3drect;
+	d3drect.left   = rect->x;
+	d3drect.top    = rect->y;
+	d3drect.right  = rect->x + rect->cx;
+	d3drect.bottom = rect->y + rect->cy;
+	device->context->RSSetScissorRects(1, &d3drect);
+}
+
+void device_ortho(device_t device, float left, float right, float top,
+		float bottom, float znear, float zfar)
+{
+	matrix4_ortho(&device->curProjMatrix, left, right, top, bottom, znear,
+			zfar);
+}
+
+void device_frustum(device_t device, float left, float right, float top,
+		float bottom, float znear, float zfar)
+{
+	matrix4_frustum(&device->curProjMatrix, left, right, top, bottom,
+			znear, zfar);
+}
+
+void device_perspective(device_t device, float fovy, float aspect,
+		float znear, float zfar)
+{
+	matrix4_perspective(&device->curProjMatrix, fovy, aspect, znear, zfar);
+}
+
+void device_projection_push(device_t device)
+{
+	mat4float mat;
+	memcpy(&mat, &device->curProjMatrix, sizeof(matrix4));
+	device->projStack.push_back(mat);
+}
+
+void device_projection_pop(device_t device)
+{
+	if (!device->projStack.size())
+		return;
+
+	mat4float *mat = device->projStack.data();
+	size_t end = device->projStack.size()-1;
+
+	/* XXX - does anyone know a better way of doing this? */
+	memcpy(&device->curProjMatrix, mat+end, sizeof(matrix4));
+	device->projStack.pop_back();
+}
+
+void swapchain_destroy(swapchain_t swapchain)
+{
+	if (!swapchain)
+		return;
+
+	gs_device *device = swapchain->device;
+	if (device->curSwapChain == swapchain)
+		device->curSwapChain = &device->defaultSwap;
+
+	delete swapchain;
+}
+
+void texture_destroy(texture_t tex)
+{
+	delete tex;
+}
+
+uint32_t texture_getwidth(texture_t tex)
+{
+	if (tex->type != GS_TEXTURE_2D)
+		return 0;
+
+	return static_cast<gs_texture_2d*>(tex)->width;
+}
+
+uint32_t texture_getheight(texture_t tex)
+{
+	if (tex->type != GS_TEXTURE_2D)
+		return 0;
+
+	return static_cast<gs_texture_2d*>(tex)->height;
+}
+
+enum gs_color_format texture_getcolorformat(texture_t tex)
+{
+	if (tex->type != GS_TEXTURE_2D)
+		return GS_UNKNOWN;
+
+	return static_cast<gs_texture_2d*>(tex)->format;
+}
+
+bool texture_map(texture_t tex, void **ptr, uint32_t *byte_width)
+{
+	HRESULT hr;
+
+	if (tex->type != GS_TEXTURE_2D)
+		return false;
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(tex);
+
+	D3D11_MAPPED_SUBRESOURCE map;
+	hr = tex2d->device->context->Map(tex2d->texture, 0,
+			D3D11_MAP_WRITE_DISCARD, 0, &map);
+	if (FAILED(hr))
+		return false;
+
+	*ptr = map.pData;
+	*byte_width = map.RowPitch;
+	return true;
+}
+
+void texture_unmap(texture_t tex)
+{
+	if (tex->type != GS_TEXTURE_2D)
+		return;
+
+	gs_texture_2d *tex2d = static_cast<gs_texture_2d*>(tex);
+	tex2d->device->context->Unmap(tex2d->texture, 0);
+}
+
+
+void cubetexture_destroy(texture_t cubetex)
+{
+	delete cubetex;
+}
+
+uint32_t cubetexture_getsize(texture_t cubetex)
+{
+	if (cubetex->type != GS_TEXTURE_CUBE)
+		return 0;
+
+	gs_texture_2d *tex = static_cast<gs_texture_2d*>(cubetex);
+	return tex->width;
+}
+
+enum gs_color_format cubetexture_getcolorformat(texture_t cubetex)
+{
+	if (cubetex->type != GS_TEXTURE_CUBE)
+		return GS_UNKNOWN;
+
+	gs_texture_2d *tex = static_cast<gs_texture_2d*>(cubetex);
+	return tex->format;
+}
+
+
+void volumetexture_destroy(texture_t voltex)
+{
+	delete voltex;
+}
+
+uint32_t volumetexture_getwidth(texture_t voltex)
+{
+	/* TODO */
+	return 0;
+}
+
+uint32_t volumetexture_getheight(texture_t voltex)
+{
+	/* TODO */
+	return 0;
+}
+
+uint32_t volumetexture_getdepth(texture_t voltex)
+{
+	/* TODO */
+	return 0;
+}
+
+enum gs_color_format volumetexture_getcolorformat(texture_t voltex)
+{
+	/* TODO */
+	return GS_UNKNOWN;
+}
+
+
+void stagesurface_destroy(stagesurf_t stagesurf)
+{
+	delete stagesurf;
+}
+
+uint32_t stagesurface_getwidth(stagesurf_t stagesurf)
+{
+	return stagesurf->width;
+}
+
+uint32_t stagesurface_getheight(stagesurf_t stagesurf)
+{
+	return stagesurf->height;
+}
+
+enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
+{
+	return stagesurf->format;
+}
+
+bool stagesurface_map(stagesurf_t stagesurf, const void **data,
+		uint32_t *byte_width)
+{
+	D3D11_MAPPED_SUBRESOURCE map;
+	if (FAILED(stagesurf->device->context->Map(stagesurf->texture, 0,
+			D3D11_MAP_READ, 0, &map)))
+		return false;
+
+	*data = map.pData;
+	*byte_width = map.RowPitch;
+	return true;
+}
+
+void stagesurface_unmap(stagesurf_t stagesurf)
+{
+	stagesurf->device->context->Unmap(stagesurf->texture, 0);
+}
+
+
+void zstencil_destroy(zstencil_t zstencil)
+{
+	delete zstencil;
+}
+
+
+void samplerstate_destroy(samplerstate_t samplerstate)
+{
+	delete samplerstate;
+}
+
+
+void vertexbuffer_destroy(vertbuffer_t vertbuffer)
+{
+	delete vertbuffer;
+}
+
+void vertexbuffer_flush(vertbuffer_t vertbuffer, bool rebuild)
+{
+	if (!vertbuffer->dynamic) {
+		blog(LOG_WARNING, "vertexbuffer_flush: vertex buffer is "
+		                  "not dynamic");
+		return;
+	}
+
+	vertbuffer->FlushBuffer(vertbuffer->vertexBuffer,
+			vertbuffer->vbd.data->points, sizeof(vec3));
+
+	if (vertbuffer->normalBuffer)
+		vertbuffer->FlushBuffer(vertbuffer->normalBuffer,
+				vertbuffer->vbd.data->normals, sizeof(vec3));
+
+	if (vertbuffer->tangentBuffer)
+		vertbuffer->FlushBuffer(vertbuffer->tangentBuffer,
+				vertbuffer->vbd.data->tangents, sizeof(vec3));
+
+	if (vertbuffer->colorBuffer)
+		vertbuffer->FlushBuffer(vertbuffer->colorBuffer,
+				vertbuffer->vbd.data->colors, sizeof(uint32_t));
+
+	for (size_t i = 0; i < vertbuffer->uvBuffers.size(); i++) {
+		tvertarray &tv = vertbuffer->vbd.data->tvarray[i];
+		vertbuffer->FlushBuffer(vertbuffer->uvBuffers[i],
+				tv.array, tv.width*sizeof(float));
+	}
+}
+
+struct vb_data *vertexbuffer_getdata(vertbuffer_t vertbuffer)
+{
+	return vertbuffer->vbd.data;
+}
+
+
+void indexbuffer_destroy(indexbuffer_t indexbuffer)
+{
+	delete indexbuffer;
+}
+
+void indexbuffer_flush(indexbuffer_t indexbuffer)
+{
+	HRESULT hr;
+
+	if (!indexbuffer->dynamic)
+		return;
+
+	D3D11_MAPPED_SUBRESOURCE map;
+	hr = indexbuffer->device->context->Map(indexbuffer->indexBuffer, 0,
+			D3D11_MAP_WRITE_DISCARD, 0, &map);
+	if (FAILED(hr))
+		return;
+
+	memcpy(map.pData, indexbuffer->indices.data,
+			indexbuffer->num * indexbuffer->indexSize);
+
+	indexbuffer->device->context->Unmap(indexbuffer->indexBuffer, 0);
+}
+
+void *indexbuffer_getdata(indexbuffer_t indexbuffer)
+{
+	return indexbuffer->indices.data;
+}
+
+size_t indexbuffer_numindices(indexbuffer_t indexbuffer)
+{
+	return indexbuffer->num;
+}
+
+enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer)
+{
+	return indexbuffer->type;
+}

+ 642 - 0
libobs-d3d11/GS_D3D11SubSystem.hpp

@@ -0,0 +1,642 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#pragma once
+
+#include <vector>
+#include <string>
+
+#include <windows.h>
+#include <dxgi.h>
+#include <d3d11.h>
+#include <d3dcompiler.h>
+
+#include "util/base.h"
+#include "graphics/matrix4.h"
+#include "graphics/graphics.h"
+#include "util/windows/ComPtr.hpp"
+#include "util/windows/HRError.hpp"
+
+struct shader_var;
+struct shader_sampler;
+struct gs_vertex_shader;
+
+#include "GS_D3D11Exports.h"
+
+using namespace std;
+
+/*
+ * Just to clarify, all structs, and all public.  These are exporting only
+ * via encapsulated C bindings, not C++ bindings, so the whole concept of
+ * "public" and "private" does not matter at all for this subproject.
+ */
+
+#define MAX_TEXTURES 8
+
+static inline uint32_t GetWinVer()
+{
+	OSVERSIONINFO ovi;
+	ovi.dwOSVersionInfoSize = sizeof(ovi);
+	GetVersionEx(&ovi);
+
+	return (ovi.dwMajorVersion << 8) | (ovi.dwMinorVersion);
+}
+
+static inline uint32_t GetFormatBPP(gs_color_format format)
+{
+	switch (format)
+	{
+	case GS_A8:          return 1;
+	case GS_R8:          return 1;
+	case GS_RGBA:        return 4;
+	case GS_BGRX:        return 4;
+	case GS_BGRA:        return 4;
+	case GS_R10G10B10A2: return 4;
+	case GS_RGBA16:      return 8;
+	case GS_R16:         return 2;
+	case GS_RGBA16F:     return 8;
+	case GS_RGBA32F:     return 16;
+	case GS_RG16F:       return 4;
+	case GS_RG32F:       return 8;
+	case GS_R16F:        return 2;
+	case GS_R32F:        return 4;
+	case GS_DXT1:        return 0;
+	case GS_DXT3:        return 0;
+	case GS_DXT5:        return 0;
+	default:             return 0;
+	}
+}
+
+static inline DXGI_FORMAT ConvertGSTextureFormat(gs_color_format format)
+{
+	switch (format)
+	{
+	case GS_A8:          return DXGI_FORMAT_A8_UNORM;
+	case GS_R8:          return DXGI_FORMAT_R8_UNORM;
+	case GS_RGBA:        return DXGI_FORMAT_R8G8B8A8_UNORM;
+	case GS_BGRX:        return DXGI_FORMAT_B8G8R8X8_UNORM;
+	case GS_BGRA:        return DXGI_FORMAT_B8G8R8A8_UNORM;
+	case GS_R10G10B10A2: return DXGI_FORMAT_R10G10B10A2_UNORM;
+	case GS_RGBA16:      return DXGI_FORMAT_R16G16B16A16_UNORM;
+	case GS_R16:         return DXGI_FORMAT_R16_UNORM;
+	case GS_RGBA16F:     return DXGI_FORMAT_R16G16B16A16_FLOAT;
+	case GS_RGBA32F:     return DXGI_FORMAT_R32G32B32A32_FLOAT;
+	case GS_RG16F:       return DXGI_FORMAT_R16G16_FLOAT;
+	case GS_RG32F:       return DXGI_FORMAT_R32G32_FLOAT;
+	case GS_R16F:        return DXGI_FORMAT_R16_FLOAT;
+	case GS_R32F:        return DXGI_FORMAT_R32_FLOAT;
+	case GS_DXT1:        return DXGI_FORMAT_BC1_UNORM;
+	case GS_DXT3:        return DXGI_FORMAT_BC2_UNORM;
+	case GS_DXT5:        return DXGI_FORMAT_BC3_UNORM;
+	default:             return DXGI_FORMAT_UNKNOWN;
+	}
+}
+
+static inline DXGI_FORMAT ConvertGSZStencilFormat(gs_zstencil_format format)
+{
+	switch (format) {
+	case GS_Z16:         return DXGI_FORMAT_D16_UNORM;
+	case GS_Z24_S8:      return DXGI_FORMAT_D24_UNORM_S8_UINT;
+	case GS_Z32F:        return DXGI_FORMAT_D32_FLOAT;
+	case GS_Z32F_S8X24:  return DXGI_FORMAT_D32_FLOAT_S8X24_UINT;
+	default:             return DXGI_FORMAT_UNKNOWN;
+	}
+}
+
+static inline D3D11_COMPARISON_FUNC ConvertGSDepthTest(gs_depth_test test)
+{
+	switch (test) {
+	default:
+	case GS_NEVER:    return D3D11_COMPARISON_NEVER;
+	case GS_LESS:     return D3D11_COMPARISON_LESS;
+	case GS_LEQUAL:   return D3D11_COMPARISON_LESS_EQUAL;
+	case GS_EQUAL:    return D3D11_COMPARISON_EQUAL;
+	case GS_GEQUAL:   return D3D11_COMPARISON_GREATER_EQUAL;
+	case GS_GREATER:  return D3D11_COMPARISON_GREATER;
+	case GS_NOTEQUAL: return D3D11_COMPARISON_NOT_EQUAL;
+	case GS_ALWAYS:   return D3D11_COMPARISON_ALWAYS;
+	}
+}
+
+static inline D3D11_STENCIL_OP ConvertGSStencilOp(gs_stencil_op op)
+{
+	switch (op) {
+	default:
+	case GS_KEEP:    return D3D11_STENCIL_OP_KEEP;
+	case GS_ZERO:    return D3D11_STENCIL_OP_ZERO;
+	case GS_REPLACE: return D3D11_STENCIL_OP_REPLACE;
+	case GS_INCR:    return D3D11_STENCIL_OP_INCR;
+	case GS_DECR:    return D3D11_STENCIL_OP_DECR;
+	case GS_INVERT:  return D3D11_STENCIL_OP_INVERT;
+	}
+}
+
+static inline D3D11_BLEND ConvertGSBlendType(gs_blend_type type)
+{
+	switch (type) {
+	default:
+	case GS_BLEND_ZERO:        return D3D11_BLEND_ZERO;
+	case GS_BLEND_ONE:         return D3D11_BLEND_ONE;
+	case GS_BLEND_SRCCOLOR:    return D3D11_BLEND_SRC_COLOR;
+	case GS_BLEND_INVSRCCOLOR: return D3D11_BLEND_INV_SRC_COLOR;
+	case GS_BLEND_SRCALPHA:    return D3D11_BLEND_SRC_ALPHA;
+	case GS_BLEND_INVSRCALPHA: return D3D11_BLEND_INV_SRC_ALPHA;
+	case GS_BLEND_DSTCOLOR:    return D3D11_BLEND_DEST_COLOR;
+	case GS_BLEND_INVDSTCOLOR: return D3D11_BLEND_INV_DEST_COLOR;
+	case GS_BLEND_DSTALPHA:    return D3D11_BLEND_DEST_ALPHA;
+	case GS_BLEND_INVDSTALPHA: return D3D11_BLEND_INV_DEST_ALPHA;
+	case GS_BLEND_SRCALPHASAT: return D3D11_BLEND_SRC_ALPHA_SAT;
+	}
+}
+
+static inline D3D11_CULL_MODE ConvertGSCullMode(gs_cull_mode mode)
+{
+	switch (mode) {
+	default:
+	case GS_BACK:    return D3D11_CULL_BACK;
+	case GS_FRONT:   return D3D11_CULL_FRONT;
+	case GS_NEITHER: return D3D11_CULL_NONE;
+	}
+}
+
+static inline D3D11_PRIMITIVE_TOPOLOGY ConvertGSTopology(gs_draw_mode mode)
+{
+	switch (mode) {
+	default:
+	case GS_POINTS:    return D3D11_PRIMITIVE_TOPOLOGY_POINTLIST;
+	case GS_LINES:     return D3D11_PRIMITIVE_TOPOLOGY_LINELIST;
+	case GS_LINESTRIP: return D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP;
+	case GS_TRIS:      return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+	case GS_TRISTRIP:  return D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP;
+	}
+}
+
+/* exception-safe RAII wrapper for vertex buffer data (NOTE: not copy-safe) */
+struct VBDataPtr {
+	vb_data *data;
+
+	inline VBDataPtr(vb_data *data) : data(data) {}
+	inline ~VBDataPtr() {vbdata_destroy(data);}
+};
+
+struct gs_vertex_buffer {
+	ComPtr<ID3D11Buffer>         vertexBuffer;
+	ComPtr<ID3D11Buffer>         normalBuffer;
+	ComPtr<ID3D11Buffer>         colorBuffer;
+	ComPtr<ID3D11Buffer>         tangentBuffer;
+	vector<ComPtr<ID3D11Buffer>> uvBuffers;
+
+	device_t       device;
+	bool           dynamic;
+	VBDataPtr      vbd;
+	size_t         numVerts;
+	vector<size_t> uvSizes;
+
+	void FlushBuffer(ID3D11Buffer *buffer, void *array,
+			size_t elementSize);
+
+	void MakeBufferList(gs_vertex_shader *shader,
+			vector<ID3D11Buffer*> &buffers,
+			vector<uint32_t> &strides);
+
+	inline void InitBuffer(const size_t elementSize,
+			const size_t numVerts, void *array,
+			ID3D11Buffer **buffer);
+
+	gs_vertex_buffer(device_t device, struct vb_data *data, uint32_t flags);
+};
+
+/* exception-safe RAII wrapper for index buffer data (NOTE: not copy-safe) */
+struct DataPtr {
+	void *data;
+
+	inline DataPtr(void *data) : data(data) {}
+	inline ~DataPtr() {bfree(data);}
+};
+
+struct gs_index_buffer {
+	ComPtr<ID3D11Buffer> indexBuffer;
+	device_t             device;
+	bool                 dynamic;
+	gs_index_type        type;
+	size_t               indexSize;
+	size_t               num;
+	DataPtr              indices;
+
+	void InitBuffer();
+
+	gs_index_buffer(device_t device, enum gs_index_type type,
+			void *indices, size_t num, uint32_t flags);
+};
+
+struct gs_texture {
+	gs_texture_type type;
+	ComPtr<ID3D11ShaderResourceView> shaderRes;
+	gs_device *device;
+
+	inline gs_texture(gs_device *device, gs_texture_type type)
+		: device (device),
+		  type   (type)
+	{
+	}
+
+	virtual ~gs_texture() {}
+};
+
+struct gs_texture_2d : gs_texture {
+	ComPtr<ID3D11Texture2D>          texture;
+	ComPtr<ID3D11RenderTargetView>   renderTarget[6];
+	ComPtr<IDXGISurface1>            gdiSurface;
+
+	uint32_t        width, height;
+	gs_color_format format;
+	DXGI_FORMAT     dxgiFormat;
+	bool            isRenderTarget;
+	bool            isGDICompatible;
+	bool            isDynamic;
+	bool            isShared;
+	bool            genMipmaps;
+	HANDLE          sharedHandle;
+
+	void InitSRD(D3D11_SUBRESOURCE_DATA *srd, void *data);
+	void InitTexture(void *data);
+	void InitResourceView();
+	void InitRenderTargets();
+
+	inline gs_texture_2d()
+		: gs_texture      (NULL, GS_TEXTURE_2D),
+		  width           (0),
+		  height          (0),
+		  format          (GS_UNKNOWN),
+		  dxgiFormat      (DXGI_FORMAT_UNKNOWN),
+		  isRenderTarget  (false),
+		  isGDICompatible (false),
+		  isDynamic       (false),
+		  isShared        (false),
+		  genMipmaps      (false),
+		  sharedHandle    (NULL)
+	{
+	}
+
+	gs_texture_2d(device_t device, uint32_t width, uint32_t height,
+			gs_color_format colorFormat, void *data,
+			uint32_t flags, bool isCubeMap, bool gdiCompatible,
+			bool shared);
+};
+
+struct gs_zstencil_buffer {
+	ComPtr<ID3D11Texture2D>        texture;
+	ComPtr<ID3D11DepthStencilView> view;
+
+	gs_device          *device;
+	uint32_t           width, height;
+	gs_zstencil_format format;
+	DXGI_FORMAT        dxgiFormat;
+
+	void InitBuffer();
+
+	inline gs_zstencil_buffer()
+		: device     (NULL),
+		  width      (0),
+		  height     (0),
+		  dxgiFormat (DXGI_FORMAT_UNKNOWN)
+	{
+	}
+
+	gs_zstencil_buffer(device_t device, uint32_t width, uint32_t height,
+			gs_zstencil_format format);
+};
+
+struct gs_stage_surface {
+	ComPtr<ID3D11Texture2D> texture;
+
+	gs_device       *device;
+	uint32_t        width, height;
+	gs_color_format format;
+	DXGI_FORMAT     dxgiFormat;
+
+	gs_stage_surface(device_t device, uint32_t width, uint32_t height,
+			gs_color_format colorFormat);
+};
+
+struct gs_sampler_state {
+	ComPtr<ID3D11SamplerState> state;
+	device_t                   device;
+	gs_sampler_info            info;
+
+	gs_sampler_state(device_t device, gs_sampler_info *info);
+};
+
+struct shader_param {
+	string            name;
+	shader_param_type type;
+
+	uint32_t textureID;
+
+	int arrayCount;
+
+	vector<uint8_t> curValue;
+	vector<uint8_t> defaultValue;
+	bool            changed;
+
+	shader_param(shader_var &var, uint32_t &texCounter);
+};
+
+struct ShaderError {
+	ComPtr<ID3D10Blob> errors;
+	HRESULT hr;
+
+	inline ShaderError(const ComPtr<ID3D10Blob> &errors, HRESULT hr)
+		: errors (errors),
+		  hr     (hr)
+	{
+	}
+};
+
+struct gs_shader {
+	device_t             device;
+	shader_type          type;
+	vector<shader_param> params;
+	ComPtr<ID3D11Buffer> constants;
+	size_t               constantSize;
+
+	inline void UpdateParam(vector<uint8_t> &constData, shader_param &param,
+			bool &upload);
+	void UploadParams();
+
+	void BuildConstantBuffer();
+	void Compile(const char *shaderStr, const char *file,
+			const char *target, ID3D10Blob **shader);
+
+	inline gs_shader(device_t device, shader_type type)
+		: device       (device),
+		  type         (type),
+		  constantSize (0)
+	{
+	}
+
+	virtual ~gs_shader() {}
+};
+
+struct ShaderSampler {
+	string           name;
+	gs_sampler_state sampler;
+
+	inline ShaderSampler(const char *name, device_t device,
+			gs_sampler_info *info)
+		: name    (name),
+		  sampler (device, info)
+	{
+	}
+};
+
+struct gs_vertex_shader : gs_shader {
+	ComPtr<ID3D11VertexShader> shader;
+	ComPtr<ID3D11InputLayout>  layout;
+
+	shader_param *world, *viewProj;
+
+	bool     hasNormals;
+	bool     hasColors;
+	bool     hasTangents;
+	uint32_t nTexUnits;
+
+	inline uint32_t NumBuffersExpected() const
+	{
+		uint32_t count = nTexUnits+1;
+		if (hasNormals)  count++;
+		if (hasColors)   count++;
+		if (hasTangents) count++;
+
+		return count;
+	}
+
+	void GetBuffersExpected(const vector<D3D11_INPUT_ELEMENT_DESC> &inputs);
+
+	gs_vertex_shader(device_t device, const char *file,
+			const char *shaderString);
+};
+
+struct gs_pixel_shader : gs_shader {
+	ComPtr<ID3D11PixelShader> shader;
+	vector<ShaderSampler>     samplers;
+
+	inline void GetSamplerStates(ID3D11SamplerState **states)
+	{
+		size_t i;
+		for (i = 0; i < samplers.size(); i++)
+			states[i] = samplers[i].sampler.state;
+		for (; i < MAX_TEXTURES; i++)
+			states[i] = NULL;
+	}
+
+	gs_pixel_shader(device_t device, const char *file,
+			const char *shaderString);
+};
+
+struct gs_swap_chain {
+	gs_device                      *device;
+	uint32_t                       numBuffers;
+	HWND                           hwnd;
+
+	gs_texture_2d                  target;
+	gs_zstencil_buffer             zs;
+	ComPtr<IDXGISwapChain>         swap;
+
+	void InitTarget(uint32_t cx, uint32_t cy);
+	void InitZStencilBuffer(uint32_t cx, uint32_t cy);
+	void Resize(uint32_t cx, uint32_t cy);
+	void Init(gs_init_data *data);
+
+	inline gs_swap_chain()
+		: device     (NULL),
+		  numBuffers (0),
+		  hwnd       (NULL)
+	{
+	}
+
+	gs_swap_chain(gs_device *device, gs_init_data *data);
+};
+
+struct BlendState {
+	bool          blendEnabled;
+	gs_blend_type srcFactor;
+	gs_blend_type destFactor;
+
+	bool          redEnabled;
+	bool          greenEnabled;
+	bool          blueEnabled;
+	bool          alphaEnabled;
+
+	inline BlendState()
+		: blendEnabled (true),
+		  srcFactor    (GS_BLEND_SRCALPHA),
+		  destFactor   (GS_BLEND_INVSRCALPHA),
+		  redEnabled   (true),
+		  greenEnabled (true),
+		  blueEnabled  (true),
+		  alphaEnabled (true)
+	{
+	}
+
+	inline BlendState(const BlendState &state)
+	{
+		memcpy(this, &state, sizeof(BlendState));
+	}
+};
+
+struct SavedBlendState : BlendState {
+	ComPtr<ID3D11BlendState> state;
+
+	inline SavedBlendState(const BlendState &val) : BlendState(val)
+	{
+	}
+};
+
+struct StencilSide {
+	gs_depth_test test;
+	gs_stencil_op fail;
+	gs_stencil_op zfail;
+	gs_stencil_op zpass;
+
+	inline StencilSide()
+		: test  (GS_ALWAYS),
+		  fail  (GS_KEEP),
+		  zfail (GS_KEEP),
+		  zpass (GS_KEEP)
+	{
+	}
+};
+
+struct ZStencilState {
+	bool          depthEnabled;
+	bool          depthWriteEnabled;
+	gs_depth_test depthFunc;
+
+	bool          stencilEnabled;
+	bool          stencilWriteEnabled;
+	StencilSide   stencilFront;
+	StencilSide   stencilBack;
+
+	inline ZStencilState()
+		: depthEnabled        (true),
+		  depthWriteEnabled   (true),
+		  depthFunc           (GS_LESS),
+		  stencilEnabled      (false),
+		  stencilWriteEnabled (true)
+	{
+	}
+
+	inline ZStencilState(const ZStencilState &state)
+	{
+		memcpy(this, &state, sizeof(ZStencilState));
+	}
+};
+
+struct SavedZStencilState : ZStencilState {
+	ComPtr<ID3D11DepthStencilState> state;
+
+	inline SavedZStencilState(const ZStencilState &val)
+		: ZStencilState (val)
+	{
+	}
+};
+
+struct RasterState {
+	gs_cull_mode cullMode;
+	bool         scissorEnabled;
+
+	inline RasterState()
+		: cullMode       (GS_BACK),
+		  scissorEnabled (false)
+	{
+	}
+
+	inline RasterState(const RasterState &state)
+	{
+		memcpy(this, &state, sizeof(RasterState));
+	}
+};
+
+struct SavedRasterState : RasterState {
+	ComPtr<ID3D11RasterizerState> state;
+
+	inline SavedRasterState(const RasterState &val)
+	       : RasterState (val)
+	{
+	}
+};
+
+struct mat4float {
+	float mat[16];
+};
+
+struct gs_device {
+	ComPtr<IDXGIFactory1>       factory;
+	ComPtr<ID3D11Device>        device;
+	ComPtr<ID3D11DeviceContext> context;
+	gs_swap_chain               defaultSwap;
+
+	gs_texture_2d               *curRenderTarget;
+	gs_zstencil_buffer          *curZStencilBuffer;
+	int                         curRenderSide;
+	gs_texture                  *curTextures[MAX_TEXTURES];
+	gs_sampler_state            *curSamplers[MAX_TEXTURES];
+	gs_vertex_buffer            *curVertexBuffer;
+	gs_vertex_shader            *curVertexShader;
+	gs_index_buffer             *curIndexBuffer;
+	gs_pixel_shader             *curPixelShader;
+	gs_swap_chain               *curSwapChain;
+
+	bool                        zstencilStateChanged;
+	bool                        rasterStateChanged;
+	bool                        blendStateChanged;
+	ZStencilState               zstencilState;
+	RasterState                 rasterState;
+	BlendState                  blendState;
+	vector<SavedZStencilState>  zstencilStates;
+	vector<SavedRasterState>    rasterStates;
+	vector<SavedBlendState>     blendStates;
+	ID3D11DepthStencilState     *curDepthStencilState;
+	ID3D11RasterizerState       *curRasterState;
+	ID3D11BlendState            *curBlendState;
+	D3D11_PRIMITIVE_TOPOLOGY    curToplogy;
+
+	gs_rect                     viewport;
+
+	vector<mat4float>           projStack;
+
+	matrix4                     curProjMatrix;
+	matrix4                     curViewMatrix;
+	matrix4                     curViewProjMatrix;
+
+	void InitFactory(uint32_t adapterIdx, IDXGIAdapter1 **adapter);
+	void InitDevice(gs_init_data *data, IDXGIAdapter *adapter);
+
+	ID3D11DepthStencilState *AddZStencilState();
+	ID3D11RasterizerState   *AddRasterState();
+	ID3D11BlendState        *AddBlendState();
+	void UpdateZStencilState();
+	void UpdateRasterState();
+	void UpdateBlendState();
+
+	inline void CopyTex(ID3D11Texture2D *dst, texture_t src);
+
+	void UpdateViewProjMatrix();
+
+	gs_device(gs_init_data *data);
+};

+ 154 - 0
libobs-d3d11/GS_D3D11Texture2D.cpp

@@ -0,0 +1,154 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "util/base.h"
+#include "GS_D3D11SubSystem.hpp"
+
+static inline bool IsPow2(uint32_t num)
+{
+	return num >= 2 && (num & (num-1)) == 0;
+}
+
+void gs_texture_2d::InitSRD(D3D11_SUBRESOURCE_DATA *srd, void *data)
+{
+	uint32_t rowSizeBytes = width * GetFormatBPP(format);
+	uint32_t texSizeBytes = rowSizeBytes * height;
+
+	if (type == GS_TEXTURE_2D) {
+		srd->pSysMem          = (uint8_t*)data;
+		srd->SysMemPitch      = rowSizeBytes; 
+		srd->SysMemSlicePitch = texSizeBytes;
+
+	} else { /* GS_TEXTURE_CUBE */
+		void **buffers = (void**)data;
+		for (size_t i = 0; i < 6; i++) {
+			srd[i].pSysMem          = buffers[i];
+			srd[i].SysMemPitch      = rowSizeBytes;
+			srd[i].SysMemSlicePitch = texSizeBytes;
+		}
+	}
+}
+
+void gs_texture_2d::InitTexture(void *data)
+{
+	D3D11_TEXTURE2D_DESC td;
+	D3D11_SUBRESOURCE_DATA srd[6];
+	HRESULT hr;
+
+	memset(&td, 0, sizeof(td));
+	td.Width            = width;
+	td.Height           = height;
+	td.MipLevels        = genMipmaps ? 0 : 1;
+	td.ArraySize        = type == GS_TEXTURE_CUBE ? 6 : 1;
+	td.Format           = dxgiFormat;
+	td.BindFlags        = D3D10_BIND_SHADER_RESOURCE;
+	td.SampleDesc.Count = 1;
+	td.CPUAccessFlags   = isDynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+	td.Usage            = isDynamic ? D3D11_USAGE_DYNAMIC :
+	                                  D3D11_USAGE_DEFAULT;
+
+	if (type == GS_TEXTURE_CUBE)
+		td.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
+
+	if (isRenderTarget || isGDICompatible)
+		td.BindFlags |= D3D10_BIND_RENDER_TARGET;
+
+	if (data)
+		InitSRD(srd, data);
+
+	hr = device->device->CreateTexture2D(&td, data ? srd : NULL,
+			texture.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create 2D texture", hr);
+}
+
+void gs_texture_2d::InitResourceView()
+{
+	D3D11_SHADER_RESOURCE_VIEW_DESC resourceDesc;
+	HRESULT hr;
+
+	memset(&resourceDesc, 0, sizeof(resourceDesc));
+	resourceDesc.Format = dxgiFormat;
+
+	if (type == GS_TEXTURE_CUBE) {
+		resourceDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURECUBE;
+		resourceDesc.TextureCube.MipLevels = genMipmaps ? -1 : 1;
+	} else {
+		resourceDesc.ViewDimension = D3D10_SRV_DIMENSION_TEXTURE2D;
+		resourceDesc.Texture2D.MipLevels = genMipmaps ? -1 : 1;
+	}
+
+	hr = device->device->CreateShaderResourceView(texture, &resourceDesc,
+			shaderRes.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create resource view", hr);
+}
+
+void gs_texture_2d::InitRenderTargets()
+{
+	HRESULT hr;
+	if (type == GS_TEXTURE_2D) {
+		hr = device->device->CreateRenderTargetView(texture, NULL,
+				renderTarget[0].Assign());
+		if (FAILED(hr))
+			throw HRError("Failed to create render target view",
+					hr);
+	} else {
+		D3D11_RENDER_TARGET_VIEW_DESC rtv;
+		rtv.Format = dxgiFormat;
+		rtv.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
+		rtv.Texture2DArray.MipSlice  = 0;
+		rtv.Texture2DArray.ArraySize = 1;
+
+		for (UINT i = 0; i < 6; i++) {
+			rtv.Texture2DArray.FirstArraySlice = i;
+			hr = device->device->CreateRenderTargetView(texture,
+					&rtv, renderTarget[i].Assign());
+			if (FAILED(hr))
+				throw HRError("Failed to create cube render "
+				              "target view", hr);
+		}
+	}
+}
+
+gs_texture_2d::gs_texture_2d(device_t device, uint32_t width, uint32_t height,
+		gs_color_format colorFormat, void *data, uint32_t flags,
+		bool isCubeMap, bool gdiCompatible, bool shared)
+	: gs_texture (device, isCubeMap ? GS_TEXTURE_CUBE : GS_TEXTURE_2D),
+	  width           (width),
+	  height          (height),
+	  format          (colorFormat),
+	  dxgiFormat      (ConvertGSTextureFormat(format)),
+	  isGDICompatible (gdiCompatible),
+	  isShared        (shared),
+	  isDynamic       ((flags & GS_DYNAMIC) != 0),
+	  isRenderTarget  ((flags & GS_RENDERTARGET) != 0),
+	  genMipmaps      ((flags & GS_BUILDMIPMAPS) != 0)
+{
+	if (genMipmaps && (!IsPow2(width) || !IsPow2(height))) {
+		blog(LOG_WARNING, "Cannot generate mipmaps for a "
+		                  "non-power-of-two texture.  Disabling "
+		                  "mipmaps for this texture.");
+		genMipmaps = false;
+	}
+
+	InitTexture(data);
+	InitResourceView();
+
+	if (isRenderTarget)
+		InitRenderTargets();
+}

+ 143 - 0
libobs-d3d11/GS_D3D11VertexBuffer.cpp

@@ -0,0 +1,143 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "util/base.h"
+#include "graphics/vec3.h"
+#include "GS_D3D11SubSystem.hpp"
+
+static inline void PushBuffer(vector<ID3D11Buffer*> &buffers,
+		vector<uint32_t> &strides, ID3D11Buffer *buffer,
+		size_t elementSize, const char *name)
+{
+	if (buffer) {
+		buffers.push_back(buffer);
+		strides.push_back((uint32_t)elementSize);
+	} else {
+		blog(LOG_ERROR, "This vertex shader requires a %s buffer",
+				name);
+	}
+}
+
+void gs_vertex_buffer::FlushBuffer(ID3D11Buffer *buffer, void *array,
+		size_t elementSize)
+{
+	D3D11_MAPPED_SUBRESOURCE msr;
+	HRESULT hr;
+
+	if (FAILED(hr = device->context->Map(buffer, 0,
+					D3D11_MAP_WRITE_DISCARD, 0, &msr)))
+		throw HRError("Failed to map buffer", hr);
+
+	memcpy(msr.pData, array, elementSize * vbd.data->num);
+	device->context->Unmap(buffer, 0);
+}
+
+void gs_vertex_buffer::MakeBufferList(gs_vertex_shader *shader,
+		vector<ID3D11Buffer*> &buffers, vector<uint32_t> &strides)
+{
+	PushBuffer(buffers, strides, vertexBuffer, sizeof(vec3), "point");
+
+	if (shader->hasNormals)
+		PushBuffer(buffers, strides, vertexBuffer, sizeof(vec3),
+				"normal");
+	if (shader->hasColors)
+		PushBuffer(buffers, strides, vertexBuffer, sizeof(vec3),
+				"color");
+	if (shader->hasTangents)
+		PushBuffer(buffers, strides, vertexBuffer, sizeof(vec3),
+				"tangent");
+	if (shader->nTexUnits <= uvBuffers.size()) {
+		for (size_t i = 0; i < shader->nTexUnits; i++) {
+			buffers.push_back(uvBuffers[i]);
+			strides.push_back((uint32_t)uvSizes[i]);
+		}
+	} else {
+		blog(LOG_ERROR, "This vertex shader requires at least %u "
+		                "texture buffers.",
+		                (uint32_t)shader->nTexUnits);
+	}
+}
+
+inline void gs_vertex_buffer::InitBuffer(const size_t elementSize,
+		const size_t numVerts, void *array, ID3D11Buffer **buffer)
+{
+	D3D11_BUFFER_DESC bd;
+	D3D11_SUBRESOURCE_DATA srd;
+	HRESULT hr;
+
+	memset(&bd,  0, sizeof(bd));
+	memset(&srd, 0, sizeof(srd));
+
+	bd.Usage          = dynamic ? D3D11_USAGE_DYNAMIC : D3D11_USAGE_DEFAULT;
+	bd.CPUAccessFlags = dynamic ? D3D11_CPU_ACCESS_WRITE : 0;
+	bd.BindFlags      = D3D11_BIND_VERTEX_BUFFER;
+	bd.ByteWidth      = UINT(elementSize * numVerts);
+	srd.pSysMem       = array;
+
+	hr = device->device->CreateBuffer(&bd, &srd, buffer);
+	if (FAILED(hr))
+		throw HRError("Failed to create buffer", hr);
+}
+
+gs_vertex_buffer::gs_vertex_buffer(device_t device, struct vb_data *data,
+		uint32_t flags)
+	: device   (device),
+	  vbd      (data),
+	  numVerts (data->num),
+	  dynamic  ((flags & GS_DYNAMIC) != 0)
+{
+	if (!data->num)
+		throw "Cannot initialize vertex buffer with 0 vertices";
+	if (!data->points)
+		throw "No points specified for vertex buffer";
+
+	InitBuffer(sizeof(vec3), data->num, data->points,
+			vertexBuffer.Assign());
+
+	if (data->normals)
+		InitBuffer(sizeof(vec3), data->num, data->normals,
+				normalBuffer.Assign());
+
+	if (data->tangents)
+		InitBuffer(sizeof(vec3), data->num, data->tangents,
+				tangentBuffer.Assign());
+
+	if (data->colors)
+		InitBuffer(sizeof(uint32_t), data->num, data->colors,
+				colorBuffer.Assign());
+
+	for (size_t i = 0; i < data->num_tex; i++) {
+		struct tvertarray *tverts = data->tvarray+i;
+
+		if (tverts->width != 2 && tverts->width != 4)
+			throw "Invalid texture vertex size specified";
+		if (!tverts->array)
+			throw "No texture vertices specified";
+
+		ComPtr<ID3D11Buffer> buffer;
+		InitBuffer(tverts->width * sizeof(float), data->num,
+				tverts->array, buffer.Assign());
+
+		uvBuffers.push_back(buffer);
+		uvSizes.push_back(tverts->width * sizeof(float));
+	}
+
+	if (!dynamic) {
+		bfree(data);
+		data = NULL;
+	}
+}

+ 60 - 0
libobs-d3d11/GS_D3D11ZStencilBuffer.cpp

@@ -0,0 +1,60 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "GS_D3D11SubSystem.hpp"
+
+void gs_zstencil_buffer::InitBuffer()
+{
+	D3D11_TEXTURE2D_DESC td;
+	D3D11_DEPTH_STENCIL_VIEW_DESC dsvd;
+	HRESULT hr;
+
+	memset(&td, 0, sizeof(td));
+	td.Width            = width;
+	td.Height           = height;
+	td.MipLevels        = 1;
+	td.ArraySize        = 1;
+	td.Format           = dxgiFormat;
+	td.BindFlags        = D3D11_BIND_DEPTH_STENCIL;
+	td.SampleDesc.Count = 1;
+	td.Usage            = D3D11_USAGE_DEFAULT;
+
+	hr = device->device->CreateTexture2D(&td, NULL, texture.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create depth stencil texture", hr);
+
+	memset(&dsvd, 0, sizeof(dsvd));
+	dsvd.Format        = dxgiFormat;
+	dsvd.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
+
+	hr = device->device->CreateDepthStencilView(texture, &dsvd,
+			view.Assign());
+	if (FAILED(hr))
+		throw HRError("Failed to create depth stencil view", hr);
+}
+
+gs_zstencil_buffer::gs_zstencil_buffer(device_t device,
+		uint32_t width, uint32_t height,
+		gs_zstencil_format format)
+	: device     (device),
+	  width      (width),
+	  height     (height),
+	  format     (format),
+	  dxgiFormat (ConvertGSZStencilFormat(format))
+{
+	InitBuffer();
+}

+ 51 - 0
libobs-d3d11/makefile

@@ -0,0 +1,51 @@
+include ../config.mak
+
+all: default
+
+SRCFILES=GS_D3D11SubSystem.cpp \
+	 GS_D3D11IndexBuffer.cpp \
+	 GS_D3D11Shader.cpp \
+	 GS_D3D11ShaderProcessor.cpp \
+	 GS_D3D11StageSurf.cpp \
+	 GS_D3D11Texture2D.cpp \
+	 GS_D3D11VertexBuffer.cpp \
+	 GS_D3D11ZStencilBuffer.cpp
+
+SONAME=../build/libobs-d3d11.$(SOEXT)
+
+LD=g++ -o 
+
+CPPFLAGS += -iquote $(BASEINC) 
+CPPFLAGS += -iquote ../libobs-graphics/
+CPPFLAGS += -isystem ./mingw/
+
+LDFLAGS += -L../build/
+
+ifdef monolithic
+LDFLAGS += -lobs
+else
+LDFLAGS += -lobs-util -lobs-graphics
+endif
+
+.PHONY: all monolithic default clean
+
+OBJS += $(SRCFILES:%.cpp=%.$(OBJ))
+
+default: $(SONAME)
+
+$(SONAME): .depend $(OBJS)
+	$(LD)$@ $(LDFLAGS) $(OBJS)
+
+.depend:
+	@rm -f .depend
+	@$(foreach SRC, $(addprefix $(SRCPATH)/, $(SRCFILES)), $(CCDEP) \
+	       -std=c++11 $(CPPFLAGS) $(SRC) -I$(BASEINC) \
+	       -MT $(SRC:$(SRCPATH)/%.cpp=%.$(OBJ)) -MM 1>> .depend;)
+
+depend: .depend
+ifneq ($(wildcard .depend),)
+include .depend
+endif
+
+clean:
+	rm -f $(OBJS) $(SONAME) *.a *.lib *.exp *.pdb .depend

+ 2713 - 0
libobs-d3d11/mingw/dxgi.h

@@ -0,0 +1,2713 @@
+/*** Autogenerated by WIDL 1.5.25 from direct-x/include/dxgi.idl - Do not edit ***/
+
+#ifndef __REQUIRED_RPCNDR_H_VERSION__
+#define __REQUIRED_RPCNDR_H_VERSION__ 475
+#endif
+
+#include <rpc.h>
+#include <rpcndr.h>
+
+#ifndef COM_NO_WINDOWS_H
+#include <windows.h>
+#include <ole2.h>
+#endif
+
+#ifndef __dxgi_h__
+#define __dxgi_h__
+
+/* Forward declarations */
+
+#ifndef __IDXGIObject_FWD_DEFINED__
+#define __IDXGIObject_FWD_DEFINED__
+typedef interface IDXGIObject IDXGIObject;
+#endif
+
+#ifndef __IDXGIDeviceSubObject_FWD_DEFINED__
+#define __IDXGIDeviceSubObject_FWD_DEFINED__
+typedef interface IDXGIDeviceSubObject IDXGIDeviceSubObject;
+#endif
+
+#ifndef __IDXGIResource_FWD_DEFINED__
+#define __IDXGIResource_FWD_DEFINED__
+typedef interface IDXGIResource IDXGIResource;
+#endif
+
+#ifndef __IDXGIKeyedMutex_FWD_DEFINED__
+#define __IDXGIKeyedMutex_FWD_DEFINED__
+typedef interface IDXGIKeyedMutex IDXGIKeyedMutex;
+#endif
+
+#ifndef __IDXGISurface_FWD_DEFINED__
+#define __IDXGISurface_FWD_DEFINED__
+typedef interface IDXGISurface IDXGISurface;
+#endif
+
+#ifndef __IDXGISurface1_FWD_DEFINED__
+#define __IDXGISurface1_FWD_DEFINED__
+typedef interface IDXGISurface1 IDXGISurface1;
+#endif
+
+#ifndef __IDXGIOutput_FWD_DEFINED__
+#define __IDXGIOutput_FWD_DEFINED__
+typedef interface IDXGIOutput IDXGIOutput;
+#endif
+
+#ifndef __IDXGIAdapter_FWD_DEFINED__
+#define __IDXGIAdapter_FWD_DEFINED__
+typedef interface IDXGIAdapter IDXGIAdapter;
+#endif
+
+#ifndef __IDXGISwapChain_FWD_DEFINED__
+#define __IDXGISwapChain_FWD_DEFINED__
+typedef interface IDXGISwapChain IDXGISwapChain;
+#endif
+
+#ifndef __IDXGIFactory_FWD_DEFINED__
+#define __IDXGIFactory_FWD_DEFINED__
+typedef interface IDXGIFactory IDXGIFactory;
+#endif
+
+#ifndef __IDXGIDevice_FWD_DEFINED__
+#define __IDXGIDevice_FWD_DEFINED__
+typedef interface IDXGIDevice IDXGIDevice;
+#endif
+
+#ifndef __IDXGIAdapter1_FWD_DEFINED__
+#define __IDXGIAdapter1_FWD_DEFINED__
+typedef interface IDXGIAdapter1 IDXGIAdapter1;
+#endif
+
+#ifndef __IDXGIFactory1_FWD_DEFINED__
+#define __IDXGIFactory1_FWD_DEFINED__
+typedef interface IDXGIFactory1 IDXGIFactory1;
+#endif
+
+/* Headers for imported files */
+
+#include <oaidl.h>
+#include <ocidl.h>
+#include <dxgitype.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define _FACDXGI (0x87a)
+
+#define MAKE_DXGI_STATUS(x)                         MAKE_HRESULT(0, _FACDXGI, x)
+#define DXGI_STATUS_OCCLUDED                        MAKE_DXGI_STATUS(1)
+#define DXGI_STATUS_CLIPPED                         MAKE_DXGI_STATUS(2)
+#define DXGI_STATUS_NO_REDIRECTION                  MAKE_DXGI_STATUS(4)
+#define DXGI_STATUS_NO_DESKTOP_ACCESS               MAKE_DXGI_STATUS(5)
+#define DXGI_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE    MAKE_DXGI_STATUS(6)
+#define DXGI_STATUS_MODE_CHANGED                    MAKE_DXGI_STATUS(7)
+#define DXGI_STATUS_MODE_CHANGE_IN_PROGRESS         MAKE_DXGI_STATUS(8)
+#define MAKE_DXGI_HRESULT(x)                        MAKE_HRESULT(1, _FACDXGI, x)
+#define DXGI_ERROR_INVALID_CALL                     MAKE_DXGI_HRESULT(1)
+#define DXGI_ERROR_NOT_FOUND                        MAKE_DXGI_HRESULT(2)
+#define DXGI_ERROR_MORE_DATA                        MAKE_DXGI_HRESULT(3)
+#define DXGI_ERROR_UNSUPPORTED                      MAKE_DXGI_HRESULT(4)
+#define DXGI_ERROR_DEVICE_REMOVED                   MAKE_DXGI_HRESULT(5)
+#define DXGI_ERROR_DEVICE_HUNG                      MAKE_DXGI_HRESULT(6)
+#define DXGI_ERROR_DEVICE_RESET                     MAKE_DXGI_HRESULT(7)
+#define DXGI_ERROR_WAS_STILL_DRAWING                MAKE_DXGI_HRESULT(10)
+#define DXGI_ERROR_FRAME_STATISTICS_DISJOINT        MAKE_DXGI_HRESULT(11)
+#define DXGI_ERROR_GRAPHICS_VIDPN_SOURCE_IN_USE     MAKE_DXGI_HRESULT(12)
+#define DXGI_ERROR_DRIVER_INTERNAL_ERROR            MAKE_DXGI_HRESULT(32)
+#define DXGI_ERROR_NONEXCLUSIVE                     MAKE_DXGI_HRESULT(33)
+#define DXGI_ERROR_NOT_CURRENTLY_AVAILABLE          MAKE_DXGI_HRESULT(34)
+#if 0
+typedef HANDLE HMONITOR;
+typedef struct _LUID {
+    DWORD LowPart;
+    LONG HighPart;
+} LUID;
+typedef struct _LUID *PLUID;
+#endif
+typedef UINT DXGI_USAGE;
+#define DXGI_USAGE_SHADER_INPUT (0x10)
+
+#define DXGI_USAGE_RENDER_TARGET_OUTPUT (0x20)
+
+#define DXGI_USAGE_BACK_BUFFER (0x40)
+
+#define DXGI_USAGE_SHARED (0x80)
+
+#define DXGI_USAGE_READ_ONLY (0x100)
+
+#define DXGI_ENUM_MODES_INTERLACED (1)
+
+#define DXGI_ENUM_MODES_SCALING (2)
+
+typedef enum DXGI_SWAP_EFFECT {
+    DXGI_SWAP_EFFECT_DISCARD = 0,
+    DXGI_SWAP_EFFECT_SEQUENTIAL = 1
+} DXGI_SWAP_EFFECT;
+typedef enum DXGI_RESIDENCY {
+    DXGI_RESIDENCY_FULLY_RESIDENT = 1,
+    DXGI_RESIDENCY_RESIDENT_IN_SHARED_MEMORY = 2,
+    DXGI_RESIDENCY_EVICTED_TO_DISK = 3
+} DXGI_RESIDENCY;
+typedef struct DXGI_SURFACE_DESC {
+    UINT Width;
+    UINT Height;
+    DXGI_FORMAT Format;
+    DXGI_SAMPLE_DESC SampleDesc;
+} DXGI_SURFACE_DESC;
+typedef struct DXGI_MAPPED_RECT {
+    INT Pitch;
+    BYTE *pBits;
+} DXGI_MAPPED_RECT;
+typedef struct DXGI_OUTPUT_DESC {
+    WCHAR DeviceName[32];
+    RECT DesktopCoordinates;
+    WINBOOL AttachedToDesktop;
+    DXGI_MODE_ROTATION Rotation;
+    HMONITOR Monitor;
+} DXGI_OUTPUT_DESC;
+typedef struct DXGI_FRAME_STATISTICS {
+    UINT PresentCount;
+    UINT PresentRefreshCount;
+    UINT SyncRefreshCount;
+    LARGE_INTEGER SyncQPCTime;
+    LARGE_INTEGER SyncGPUTime;
+} DXGI_FRAME_STATISTICS;
+typedef struct DXGI_ADAPTER_DESC {
+    WCHAR Description[128];
+    UINT VendorId;
+    UINT DeviceId;
+    UINT SubSysId;
+    UINT Revision;
+    SIZE_T DedicatedVideoMemory;
+    SIZE_T DedicatedSystemMemory;
+    SIZE_T SharedSystemMemory;
+    LUID AdapterLuid;
+} DXGI_ADAPTER_DESC;
+typedef enum DXGI_SWAP_CHAIN_FLAG {
+    DXGI_SWAP_CHAIN_FLAG_NONPREROTATED = 1,
+    DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH = 2,
+    DXGI_SWAP_CHAIN_FLAG_GDI_COMPATIBLE = 4
+} DXGI_SWAP_CHAIN_FLAG;
+typedef struct DXGI_SWAP_CHAIN_DESC {
+    DXGI_MODE_DESC BufferDesc;
+    DXGI_SAMPLE_DESC SampleDesc;
+    DXGI_USAGE BufferUsage;
+    UINT BufferCount;
+    HWND OutputWindow;
+    WINBOOL Windowed;
+    DXGI_SWAP_EFFECT SwapEffect;
+    UINT Flags;
+} DXGI_SWAP_CHAIN_DESC;
+typedef struct DXGI_SHARED_RESOURCE {
+    HANDLE Handle;
+} DXGI_SHARED_RESOURCE;
+/*****************************************************************************
+ * IDXGIObject interface
+ */
+#ifndef __IDXGIObject_INTERFACE_DEFINED__
+#define __IDXGIObject_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIObject, 0xaec22fb8, 0x76f3, 0x4639, 0x9b,0xe0, 0x28,0xeb,0x43,0xa6,0x7a,0x2e);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("aec22fb8-76f3-4639-9be0-28eb43a67a2e")
+IDXGIObject : public IUnknown
+{
+    virtual HRESULT STDMETHODCALLTYPE SetPrivateData(
+        REFGUID guid,
+        UINT data_size,
+        const void *data) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetPrivateDataInterface(
+        REFGUID guid,
+        const IUnknown *object) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetPrivateData(
+        REFGUID guid,
+        UINT *data_size,
+        void *data) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetParent(
+        REFIID riid,
+        void **parent) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIObject, 0xaec22fb8, 0x76f3, 0x4639, 0x9b,0xe0, 0x28,0xeb,0x43,0xa6,0x7a,0x2e)
+#endif
+#else
+typedef struct IDXGIObjectVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIObject* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIObject* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIObject* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIObject* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIObject* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIObject* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIObject* This,
+        REFIID riid,
+        void **parent);
+
+    END_INTERFACE
+} IDXGIObjectVtbl;
+interface IDXGIObject {
+    CONST_VTBL IDXGIObjectVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIObject_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIObject_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIObject_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIObject_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIObject_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIObject_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIObject_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIObject_QueryInterface(IDXGIObject* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIObject_AddRef(IDXGIObject* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIObject_Release(IDXGIObject* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIObject_SetPrivateData(IDXGIObject* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIObject_SetPrivateDataInterface(IDXGIObject* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIObject_GetPrivateData(IDXGIObject* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIObject_GetParent(IDXGIObject* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIObject_SetPrivateData_Proxy(
+    IDXGIObject* This,
+    REFGUID guid,
+    UINT data_size,
+    const void *data);
+void __RPC_STUB IDXGIObject_SetPrivateData_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIObject_SetPrivateDataInterface_Proxy(
+    IDXGIObject* This,
+    REFGUID guid,
+    const IUnknown *object);
+void __RPC_STUB IDXGIObject_SetPrivateDataInterface_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIObject_GetPrivateData_Proxy(
+    IDXGIObject* This,
+    REFGUID guid,
+    UINT *data_size,
+    void *data);
+void __RPC_STUB IDXGIObject_GetPrivateData_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIObject_GetParent_Proxy(
+    IDXGIObject* This,
+    REFIID riid,
+    void **parent);
+void __RPC_STUB IDXGIObject_GetParent_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIObject_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGIDeviceSubObject interface
+ */
+#ifndef __IDXGIDeviceSubObject_INTERFACE_DEFINED__
+#define __IDXGIDeviceSubObject_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIDeviceSubObject, 0x3d3e0379, 0xf9de, 0x4d58, 0xbb,0x6c, 0x18,0xd6,0x29,0x92,0xf1,0xa6);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("3d3e0379-f9de-4d58-bb6c-18d62992f1a6")
+IDXGIDeviceSubObject : public IDXGIObject
+{
+    virtual HRESULT STDMETHODCALLTYPE GetDevice(
+        REFIID riid,
+        void **device) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIDeviceSubObject, 0x3d3e0379, 0xf9de, 0x4d58, 0xbb,0x6c, 0x18,0xd6,0x29,0x92,0xf1,0xa6)
+#endif
+#else
+typedef struct IDXGIDeviceSubObjectVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIDeviceSubObject* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIDeviceSubObject* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIDeviceSubObject* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIDeviceSubObject* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIDeviceSubObject* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIDeviceSubObject* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIDeviceSubObject* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIDeviceSubObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDevice)(
+        IDXGIDeviceSubObject* This,
+        REFIID riid,
+        void **device);
+
+    END_INTERFACE
+} IDXGIDeviceSubObjectVtbl;
+interface IDXGIDeviceSubObject {
+    CONST_VTBL IDXGIDeviceSubObjectVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIDeviceSubObject_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIDeviceSubObject_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIDeviceSubObject_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIDeviceSubObject_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIDeviceSubObject_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIDeviceSubObject_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIDeviceSubObject_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIDeviceSubObject methods ***/
+#define IDXGIDeviceSubObject_GetDevice(This,riid,device) (This)->lpVtbl->GetDevice(This,riid,device)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIDeviceSubObject_QueryInterface(IDXGIDeviceSubObject* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIDeviceSubObject_AddRef(IDXGIDeviceSubObject* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIDeviceSubObject_Release(IDXGIDeviceSubObject* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIDeviceSubObject_SetPrivateData(IDXGIDeviceSubObject* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIDeviceSubObject_SetPrivateDataInterface(IDXGIDeviceSubObject* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIDeviceSubObject_GetPrivateData(IDXGIDeviceSubObject* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIDeviceSubObject_GetParent(IDXGIDeviceSubObject* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIDeviceSubObject methods ***/
+static FORCEINLINE HRESULT IDXGIDeviceSubObject_GetDevice(IDXGIDeviceSubObject* This,REFIID riid,void **device) {
+    return This->lpVtbl->GetDevice(This,riid,device);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIDeviceSubObject_GetDevice_Proxy(
+    IDXGIDeviceSubObject* This,
+    REFIID riid,
+    void **device);
+void __RPC_STUB IDXGIDeviceSubObject_GetDevice_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIDeviceSubObject_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGIResource interface
+ */
+#ifndef __IDXGIResource_INTERFACE_DEFINED__
+#define __IDXGIResource_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIResource, 0x035f3ab4, 0x482e, 0x4e50, 0xb4,0x1f, 0x8a,0x7f,0x8b,0xd8,0x96,0x0b);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("035f3ab4-482e-4e50-b41f-8a7f8bd8960b")
+IDXGIResource : public IDXGIDeviceSubObject
+{
+    virtual HRESULT STDMETHODCALLTYPE GetSharedHandle(
+        HANDLE *pSharedHandle) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetUsage(
+        DXGI_USAGE *pUsage) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetEvictionPriority(
+        UINT EvictionPriority) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetEvictionPriority(
+        UINT *pEvictionPriority) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIResource, 0x035f3ab4, 0x482e, 0x4e50, 0xb4,0x1f, 0x8a,0x7f,0x8b,0xd8,0x96,0x0b)
+#endif
+#else
+typedef struct IDXGIResourceVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIResource* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIResource* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIResource* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIResource* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIResource* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIResource* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIResource* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIDeviceSubObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDevice)(
+        IDXGIResource* This,
+        REFIID riid,
+        void **device);
+
+    /*** IDXGIResource methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetSharedHandle)(
+        IDXGIResource* This,
+        HANDLE *pSharedHandle);
+
+    HRESULT (STDMETHODCALLTYPE *GetUsage)(
+        IDXGIResource* This,
+        DXGI_USAGE *pUsage);
+
+    HRESULT (STDMETHODCALLTYPE *SetEvictionPriority)(
+        IDXGIResource* This,
+        UINT EvictionPriority);
+
+    HRESULT (STDMETHODCALLTYPE *GetEvictionPriority)(
+        IDXGIResource* This,
+        UINT *pEvictionPriority);
+
+    END_INTERFACE
+} IDXGIResourceVtbl;
+interface IDXGIResource {
+    CONST_VTBL IDXGIResourceVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIResource_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIResource_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIResource_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIResource_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIResource_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIResource_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIResource_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIDeviceSubObject methods ***/
+#define IDXGIResource_GetDevice(This,riid,device) (This)->lpVtbl->GetDevice(This,riid,device)
+/*** IDXGIResource methods ***/
+#define IDXGIResource_GetSharedHandle(This,pSharedHandle) (This)->lpVtbl->GetSharedHandle(This,pSharedHandle)
+#define IDXGIResource_GetUsage(This,pUsage) (This)->lpVtbl->GetUsage(This,pUsage)
+#define IDXGIResource_SetEvictionPriority(This,EvictionPriority) (This)->lpVtbl->SetEvictionPriority(This,EvictionPriority)
+#define IDXGIResource_GetEvictionPriority(This,pEvictionPriority) (This)->lpVtbl->GetEvictionPriority(This,pEvictionPriority)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIResource_QueryInterface(IDXGIResource* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIResource_AddRef(IDXGIResource* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIResource_Release(IDXGIResource* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIResource_SetPrivateData(IDXGIResource* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIResource_SetPrivateDataInterface(IDXGIResource* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIResource_GetPrivateData(IDXGIResource* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIResource_GetParent(IDXGIResource* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIDeviceSubObject methods ***/
+static FORCEINLINE HRESULT IDXGIResource_GetDevice(IDXGIResource* This,REFIID riid,void **device) {
+    return This->lpVtbl->GetDevice(This,riid,device);
+}
+/*** IDXGIResource methods ***/
+static FORCEINLINE HRESULT IDXGIResource_GetSharedHandle(IDXGIResource* This,HANDLE *pSharedHandle) {
+    return This->lpVtbl->GetSharedHandle(This,pSharedHandle);
+}
+static FORCEINLINE HRESULT IDXGIResource_GetUsage(IDXGIResource* This,DXGI_USAGE *pUsage) {
+    return This->lpVtbl->GetUsage(This,pUsage);
+}
+static FORCEINLINE HRESULT IDXGIResource_SetEvictionPriority(IDXGIResource* This,UINT EvictionPriority) {
+    return This->lpVtbl->SetEvictionPriority(This,EvictionPriority);
+}
+static FORCEINLINE HRESULT IDXGIResource_GetEvictionPriority(IDXGIResource* This,UINT *pEvictionPriority) {
+    return This->lpVtbl->GetEvictionPriority(This,pEvictionPriority);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIResource_GetSharedHandle_Proxy(
+    IDXGIResource* This,
+    HANDLE *pSharedHandle);
+void __RPC_STUB IDXGIResource_GetSharedHandle_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIResource_GetUsage_Proxy(
+    IDXGIResource* This,
+    DXGI_USAGE *pUsage);
+void __RPC_STUB IDXGIResource_GetUsage_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIResource_SetEvictionPriority_Proxy(
+    IDXGIResource* This,
+    UINT EvictionPriority);
+void __RPC_STUB IDXGIResource_SetEvictionPriority_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIResource_GetEvictionPriority_Proxy(
+    IDXGIResource* This,
+    UINT *pEvictionPriority);
+void __RPC_STUB IDXGIResource_GetEvictionPriority_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIResource_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGIKeyedMutex interface
+ */
+#ifndef __IDXGIKeyedMutex_INTERFACE_DEFINED__
+#define __IDXGIKeyedMutex_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIKeyedMutex, 0x9d8e1289, 0xd7b3, 0x465f, 0x81,0x26, 0x25,0x0e,0x34,0x9a,0xf8,0x5d);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("9d8e1289-d7b3-465f-8126-250e349af85d")
+IDXGIKeyedMutex : public IDXGIDeviceSubObject
+{
+    virtual HRESULT STDMETHODCALLTYPE AcquireSync(
+        UINT64 Key,
+        DWORD dwMilliseconds) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE ReleaseSync(
+        UINT64 Key) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIKeyedMutex, 0x9d8e1289, 0xd7b3, 0x465f, 0x81,0x26, 0x25,0x0e,0x34,0x9a,0xf8,0x5d)
+#endif
+#else
+typedef struct IDXGIKeyedMutexVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIKeyedMutex* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIKeyedMutex* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIKeyedMutex* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIKeyedMutex* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIKeyedMutex* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIKeyedMutex* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIKeyedMutex* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIDeviceSubObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDevice)(
+        IDXGIKeyedMutex* This,
+        REFIID riid,
+        void **device);
+
+    /*** IDXGIKeyedMutex methods ***/
+    HRESULT (STDMETHODCALLTYPE *AcquireSync)(
+        IDXGIKeyedMutex* This,
+        UINT64 Key,
+        DWORD dwMilliseconds);
+
+    HRESULT (STDMETHODCALLTYPE *ReleaseSync)(
+        IDXGIKeyedMutex* This,
+        UINT64 Key);
+
+    END_INTERFACE
+} IDXGIKeyedMutexVtbl;
+interface IDXGIKeyedMutex {
+    CONST_VTBL IDXGIKeyedMutexVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIKeyedMutex_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIKeyedMutex_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIKeyedMutex_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIKeyedMutex_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIKeyedMutex_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIKeyedMutex_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIKeyedMutex_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIDeviceSubObject methods ***/
+#define IDXGIKeyedMutex_GetDevice(This,riid,device) (This)->lpVtbl->GetDevice(This,riid,device)
+/*** IDXGIKeyedMutex methods ***/
+#define IDXGIKeyedMutex_AcquireSync(This,Key,dwMilliseconds) (This)->lpVtbl->AcquireSync(This,Key,dwMilliseconds)
+#define IDXGIKeyedMutex_ReleaseSync(This,Key) (This)->lpVtbl->ReleaseSync(This,Key)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIKeyedMutex_QueryInterface(IDXGIKeyedMutex* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIKeyedMutex_AddRef(IDXGIKeyedMutex* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIKeyedMutex_Release(IDXGIKeyedMutex* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIKeyedMutex_SetPrivateData(IDXGIKeyedMutex* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIKeyedMutex_SetPrivateDataInterface(IDXGIKeyedMutex* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIKeyedMutex_GetPrivateData(IDXGIKeyedMutex* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIKeyedMutex_GetParent(IDXGIKeyedMutex* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIDeviceSubObject methods ***/
+static FORCEINLINE HRESULT IDXGIKeyedMutex_GetDevice(IDXGIKeyedMutex* This,REFIID riid,void **device) {
+    return This->lpVtbl->GetDevice(This,riid,device);
+}
+/*** IDXGIKeyedMutex methods ***/
+static FORCEINLINE HRESULT IDXGIKeyedMutex_AcquireSync(IDXGIKeyedMutex* This,UINT64 Key,DWORD dwMilliseconds) {
+    return This->lpVtbl->AcquireSync(This,Key,dwMilliseconds);
+}
+static FORCEINLINE HRESULT IDXGIKeyedMutex_ReleaseSync(IDXGIKeyedMutex* This,UINT64 Key) {
+    return This->lpVtbl->ReleaseSync(This,Key);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIKeyedMutex_AcquireSync_Proxy(
+    IDXGIKeyedMutex* This,
+    UINT64 Key,
+    DWORD dwMilliseconds);
+void __RPC_STUB IDXGIKeyedMutex_AcquireSync_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIKeyedMutex_ReleaseSync_Proxy(
+    IDXGIKeyedMutex* This,
+    UINT64 Key);
+void __RPC_STUB IDXGIKeyedMutex_ReleaseSync_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIKeyedMutex_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGISurface interface
+ */
+#ifndef __IDXGISurface_INTERFACE_DEFINED__
+#define __IDXGISurface_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGISurface, 0xcafcb56c, 0x6ac3, 0x4889, 0xbf,0x47, 0x9e,0x23,0xbb,0xd2,0x60,0xec);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("cafcb56c-6ac3-4889-bf47-9e23bbd260ec")
+IDXGISurface : public IDXGIDeviceSubObject
+{
+    virtual HRESULT STDMETHODCALLTYPE GetDesc(
+        DXGI_SURFACE_DESC *desc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE Map(
+        DXGI_MAPPED_RECT *mapped_rect,
+        UINT flags) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE Unmap(
+        ) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGISurface, 0xcafcb56c, 0x6ac3, 0x4889, 0xbf,0x47, 0x9e,0x23,0xbb,0xd2,0x60,0xec)
+#endif
+#else
+typedef struct IDXGISurfaceVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGISurface* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGISurface* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGISurface* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGISurface* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGISurface* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGISurface* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGISurface* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIDeviceSubObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDevice)(
+        IDXGISurface* This,
+        REFIID riid,
+        void **device);
+
+    /*** IDXGISurface methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDesc)(
+        IDXGISurface* This,
+        DXGI_SURFACE_DESC *desc);
+
+    HRESULT (STDMETHODCALLTYPE *Map)(
+        IDXGISurface* This,
+        DXGI_MAPPED_RECT *mapped_rect,
+        UINT flags);
+
+    HRESULT (STDMETHODCALLTYPE *Unmap)(
+        IDXGISurface* This);
+
+    END_INTERFACE
+} IDXGISurfaceVtbl;
+interface IDXGISurface {
+    CONST_VTBL IDXGISurfaceVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGISurface_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGISurface_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGISurface_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGISurface_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGISurface_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGISurface_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGISurface_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIDeviceSubObject methods ***/
+#define IDXGISurface_GetDevice(This,riid,device) (This)->lpVtbl->GetDevice(This,riid,device)
+/*** IDXGISurface methods ***/
+#define IDXGISurface_GetDesc(This,desc) (This)->lpVtbl->GetDesc(This,desc)
+#define IDXGISurface_Map(This,mapped_rect,flags) (This)->lpVtbl->Map(This,mapped_rect,flags)
+#define IDXGISurface_Unmap(This) (This)->lpVtbl->Unmap(This)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGISurface_QueryInterface(IDXGISurface* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGISurface_AddRef(IDXGISurface* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGISurface_Release(IDXGISurface* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGISurface_SetPrivateData(IDXGISurface* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGISurface_SetPrivateDataInterface(IDXGISurface* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGISurface_GetPrivateData(IDXGISurface* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGISurface_GetParent(IDXGISurface* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIDeviceSubObject methods ***/
+static FORCEINLINE HRESULT IDXGISurface_GetDevice(IDXGISurface* This,REFIID riid,void **device) {
+    return This->lpVtbl->GetDevice(This,riid,device);
+}
+/*** IDXGISurface methods ***/
+static FORCEINLINE HRESULT IDXGISurface_GetDesc(IDXGISurface* This,DXGI_SURFACE_DESC *desc) {
+    return This->lpVtbl->GetDesc(This,desc);
+}
+static FORCEINLINE HRESULT IDXGISurface_Map(IDXGISurface* This,DXGI_MAPPED_RECT *mapped_rect,UINT flags) {
+    return This->lpVtbl->Map(This,mapped_rect,flags);
+}
+static FORCEINLINE HRESULT IDXGISurface_Unmap(IDXGISurface* This) {
+    return This->lpVtbl->Unmap(This);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGISurface_GetDesc_Proxy(
+    IDXGISurface* This,
+    DXGI_SURFACE_DESC *desc);
+void __RPC_STUB IDXGISurface_GetDesc_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISurface_Map_Proxy(
+    IDXGISurface* This,
+    DXGI_MAPPED_RECT *mapped_rect,
+    UINT flags);
+void __RPC_STUB IDXGISurface_Map_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISurface_Unmap_Proxy(
+    IDXGISurface* This);
+void __RPC_STUB IDXGISurface_Unmap_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGISurface_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGISurface1 interface
+ */
+#ifndef __IDXGISurface_INTERFACE_DEFINED__
+#define __IDXGISurface_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGISurface, 0x4AE63092, 0x6327, 0x4c1b, 0x80,0xae, 0xbf,0xe1,0x2e,0xa3,0x2b,0xa6);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("4AE63092-6327-4c1b-80AE-BFE12EA32B86")
+IDXGISurface : public IDXGISurface
+{
+    virtual HRESULT STDMETHODCALLTYPE GetDC(
+        BOOL discard,
+        HDC *phdc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE ReleaseDC(
+        RECT *pdirty_rect) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGISurface1, 0x4AE63092, 0x6327, 0x4c1b, 0x80,0xae, 0xbf,0xe1,0x2e,0xa3,0x2b,0xa6) 
+#endif
+#endif  /* c++ */
+
+#endif  /* __IDXGISurface_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGIOutput interface
+ */
+#ifndef __IDXGIOutput_INTERFACE_DEFINED__
+#define __IDXGIOutput_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIOutput, 0xae02eedb, 0xc735, 0x4690, 0x8d,0x52, 0x5a,0x8d,0xc2,0x02,0x13,0xaa);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("ae02eedb-c735-4690-8d52-5a8dc20213aa")
+IDXGIOutput : public IDXGIObject
+{
+    virtual HRESULT STDMETHODCALLTYPE GetDesc(
+        DXGI_OUTPUT_DESC *desc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetDisplayModeList(
+        DXGI_FORMAT format,
+        UINT flags,
+        UINT *mode_count,
+        DXGI_MODE_DESC *desc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE FindClosestMatchingMode(
+        const DXGI_MODE_DESC *mode,
+        DXGI_MODE_DESC *closest_match,
+        IUnknown *device) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE WaitForVBlank(
+        ) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE TakeOwnership(
+        IUnknown *device,
+        WINBOOL exclusive) = 0;
+
+    virtual void STDMETHODCALLTYPE ReleaseOwnership(
+        ) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetGammaControlCapabilities(
+        DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetGammaControl(
+        const DXGI_GAMMA_CONTROL *gamma_control) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetGammaControl(
+        DXGI_GAMMA_CONTROL *gamma_control) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetDisplaySurface(
+        IDXGISurface *surface) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetDisplaySurfaceData(
+        IDXGISurface *surface) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
+        DXGI_FRAME_STATISTICS *stats) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIOutput, 0xae02eedb, 0xc735, 0x4690, 0x8d,0x52, 0x5a,0x8d,0xc2,0x02,0x13,0xaa)
+#endif
+#else
+typedef struct IDXGIOutputVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIOutput* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIOutput* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIOutput* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIOutput* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIOutput* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIOutput* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIOutput* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIOutput methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDesc)(
+        IDXGIOutput* This,
+        DXGI_OUTPUT_DESC *desc);
+
+    HRESULT (STDMETHODCALLTYPE *GetDisplayModeList)(
+        IDXGIOutput* This,
+        DXGI_FORMAT format,
+        UINT flags,
+        UINT *mode_count,
+        DXGI_MODE_DESC *desc);
+
+    HRESULT (STDMETHODCALLTYPE *FindClosestMatchingMode)(
+        IDXGIOutput* This,
+        const DXGI_MODE_DESC *mode,
+        DXGI_MODE_DESC *closest_match,
+        IUnknown *device);
+
+    HRESULT (STDMETHODCALLTYPE *WaitForVBlank)(
+        IDXGIOutput* This);
+
+    HRESULT (STDMETHODCALLTYPE *TakeOwnership)(
+        IDXGIOutput* This,
+        IUnknown *device,
+        WINBOOL exclusive);
+
+    void (STDMETHODCALLTYPE *ReleaseOwnership)(
+        IDXGIOutput* This);
+
+    HRESULT (STDMETHODCALLTYPE *GetGammaControlCapabilities)(
+        IDXGIOutput* This,
+        DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps);
+
+    HRESULT (STDMETHODCALLTYPE *SetGammaControl)(
+        IDXGIOutput* This,
+        const DXGI_GAMMA_CONTROL *gamma_control);
+
+    HRESULT (STDMETHODCALLTYPE *GetGammaControl)(
+        IDXGIOutput* This,
+        DXGI_GAMMA_CONTROL *gamma_control);
+
+    HRESULT (STDMETHODCALLTYPE *SetDisplaySurface)(
+        IDXGIOutput* This,
+        IDXGISurface *surface);
+
+    HRESULT (STDMETHODCALLTYPE *GetDisplaySurfaceData)(
+        IDXGIOutput* This,
+        IDXGISurface *surface);
+
+    HRESULT (STDMETHODCALLTYPE *GetFrameStatistics)(
+        IDXGIOutput* This,
+        DXGI_FRAME_STATISTICS *stats);
+
+    END_INTERFACE
+} IDXGIOutputVtbl;
+interface IDXGIOutput {
+    CONST_VTBL IDXGIOutputVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIOutput_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIOutput_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIOutput_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIOutput_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIOutput_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIOutput_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIOutput_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIOutput methods ***/
+#define IDXGIOutput_GetDesc(This,desc) (This)->lpVtbl->GetDesc(This,desc)
+#define IDXGIOutput_GetDisplayModeList(This,format,flags,mode_count,desc) (This)->lpVtbl->GetDisplayModeList(This,format,flags,mode_count,desc)
+#define IDXGIOutput_FindClosestMatchingMode(This,mode,closest_match,device) (This)->lpVtbl->FindClosestMatchingMode(This,mode,closest_match,device)
+#define IDXGIOutput_WaitForVBlank(This) (This)->lpVtbl->WaitForVBlank(This)
+#define IDXGIOutput_TakeOwnership(This,device,exclusive) (This)->lpVtbl->TakeOwnership(This,device,exclusive)
+#define IDXGIOutput_ReleaseOwnership(This) (This)->lpVtbl->ReleaseOwnership(This)
+#define IDXGIOutput_GetGammaControlCapabilities(This,gamma_caps) (This)->lpVtbl->GetGammaControlCapabilities(This,gamma_caps)
+#define IDXGIOutput_SetGammaControl(This,gamma_control) (This)->lpVtbl->SetGammaControl(This,gamma_control)
+#define IDXGIOutput_GetGammaControl(This,gamma_control) (This)->lpVtbl->GetGammaControl(This,gamma_control)
+#define IDXGIOutput_SetDisplaySurface(This,surface) (This)->lpVtbl->SetDisplaySurface(This,surface)
+#define IDXGIOutput_GetDisplaySurfaceData(This,surface) (This)->lpVtbl->GetDisplaySurfaceData(This,surface)
+#define IDXGIOutput_GetFrameStatistics(This,stats) (This)->lpVtbl->GetFrameStatistics(This,stats)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIOutput_QueryInterface(IDXGIOutput* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIOutput_AddRef(IDXGIOutput* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIOutput_Release(IDXGIOutput* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIOutput_SetPrivateData(IDXGIOutput* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIOutput_SetPrivateDataInterface(IDXGIOutput* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetPrivateData(IDXGIOutput* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetParent(IDXGIOutput* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIOutput methods ***/
+static FORCEINLINE HRESULT IDXGIOutput_GetDesc(IDXGIOutput* This,DXGI_OUTPUT_DESC *desc) {
+    return This->lpVtbl->GetDesc(This,desc);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetDisplayModeList(IDXGIOutput* This,DXGI_FORMAT format,UINT flags,UINT *mode_count,DXGI_MODE_DESC *desc) {
+    return This->lpVtbl->GetDisplayModeList(This,format,flags,mode_count,desc);
+}
+static FORCEINLINE HRESULT IDXGIOutput_FindClosestMatchingMode(IDXGIOutput* This,const DXGI_MODE_DESC *mode,DXGI_MODE_DESC *closest_match,IUnknown *device) {
+    return This->lpVtbl->FindClosestMatchingMode(This,mode,closest_match,device);
+}
+static FORCEINLINE HRESULT IDXGIOutput_WaitForVBlank(IDXGIOutput* This) {
+    return This->lpVtbl->WaitForVBlank(This);
+}
+static FORCEINLINE HRESULT IDXGIOutput_TakeOwnership(IDXGIOutput* This,IUnknown *device,WINBOOL exclusive) {
+    return This->lpVtbl->TakeOwnership(This,device,exclusive);
+}
+static FORCEINLINE void IDXGIOutput_ReleaseOwnership(IDXGIOutput* This) {
+    This->lpVtbl->ReleaseOwnership(This);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetGammaControlCapabilities(IDXGIOutput* This,DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps) {
+    return This->lpVtbl->GetGammaControlCapabilities(This,gamma_caps);
+}
+static FORCEINLINE HRESULT IDXGIOutput_SetGammaControl(IDXGIOutput* This,const DXGI_GAMMA_CONTROL *gamma_control) {
+    return This->lpVtbl->SetGammaControl(This,gamma_control);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetGammaControl(IDXGIOutput* This,DXGI_GAMMA_CONTROL *gamma_control) {
+    return This->lpVtbl->GetGammaControl(This,gamma_control);
+}
+static FORCEINLINE HRESULT IDXGIOutput_SetDisplaySurface(IDXGIOutput* This,IDXGISurface *surface) {
+    return This->lpVtbl->SetDisplaySurface(This,surface);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetDisplaySurfaceData(IDXGIOutput* This,IDXGISurface *surface) {
+    return This->lpVtbl->GetDisplaySurfaceData(This,surface);
+}
+static FORCEINLINE HRESULT IDXGIOutput_GetFrameStatistics(IDXGIOutput* This,DXGI_FRAME_STATISTICS *stats) {
+    return This->lpVtbl->GetFrameStatistics(This,stats);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIOutput_GetDesc_Proxy(
+    IDXGIOutput* This,
+    DXGI_OUTPUT_DESC *desc);
+void __RPC_STUB IDXGIOutput_GetDesc_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_GetDisplayModeList_Proxy(
+    IDXGIOutput* This,
+    DXGI_FORMAT format,
+    UINT flags,
+    UINT *mode_count,
+    DXGI_MODE_DESC *desc);
+void __RPC_STUB IDXGIOutput_GetDisplayModeList_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_FindClosestMatchingMode_Proxy(
+    IDXGIOutput* This,
+    const DXGI_MODE_DESC *mode,
+    DXGI_MODE_DESC *closest_match,
+    IUnknown *device);
+void __RPC_STUB IDXGIOutput_FindClosestMatchingMode_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_WaitForVBlank_Proxy(
+    IDXGIOutput* This);
+void __RPC_STUB IDXGIOutput_WaitForVBlank_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_TakeOwnership_Proxy(
+    IDXGIOutput* This,
+    IUnknown *device,
+    WINBOOL exclusive);
+void __RPC_STUB IDXGIOutput_TakeOwnership_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+void STDMETHODCALLTYPE IDXGIOutput_ReleaseOwnership_Proxy(
+    IDXGIOutput* This);
+void __RPC_STUB IDXGIOutput_ReleaseOwnership_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_GetGammaControlCapabilities_Proxy(
+    IDXGIOutput* This,
+    DXGI_GAMMA_CONTROL_CAPABILITIES *gamma_caps);
+void __RPC_STUB IDXGIOutput_GetGammaControlCapabilities_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_SetGammaControl_Proxy(
+    IDXGIOutput* This,
+    const DXGI_GAMMA_CONTROL *gamma_control);
+void __RPC_STUB IDXGIOutput_SetGammaControl_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_GetGammaControl_Proxy(
+    IDXGIOutput* This,
+    DXGI_GAMMA_CONTROL *gamma_control);
+void __RPC_STUB IDXGIOutput_GetGammaControl_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_SetDisplaySurface_Proxy(
+    IDXGIOutput* This,
+    IDXGISurface *surface);
+void __RPC_STUB IDXGIOutput_SetDisplaySurface_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_GetDisplaySurfaceData_Proxy(
+    IDXGIOutput* This,
+    IDXGISurface *surface);
+void __RPC_STUB IDXGIOutput_GetDisplaySurfaceData_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIOutput_GetFrameStatistics_Proxy(
+    IDXGIOutput* This,
+    DXGI_FRAME_STATISTICS *stats);
+void __RPC_STUB IDXGIOutput_GetFrameStatistics_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIOutput_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGIAdapter interface
+ */
+#ifndef __IDXGIAdapter_INTERFACE_DEFINED__
+#define __IDXGIAdapter_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIAdapter, 0x2411e7e1, 0x12ac, 0x4ccf, 0xbd,0x14, 0x97,0x98,0xe8,0x53,0x4d,0xc0);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("2411e7e1-12ac-4ccf-bd14-9798e8534dc0")
+IDXGIAdapter : public IDXGIObject
+{
+    virtual HRESULT STDMETHODCALLTYPE EnumOutputs(
+        UINT output_idx,
+        IDXGIOutput **output) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetDesc(
+        DXGI_ADAPTER_DESC *desc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE CheckInterfaceSupport(
+        REFGUID guid,
+        LARGE_INTEGER *umd_version) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIAdapter, 0x2411e7e1, 0x12ac, 0x4ccf, 0xbd,0x14, 0x97,0x98,0xe8,0x53,0x4d,0xc0)
+#endif
+#else
+typedef struct IDXGIAdapterVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIAdapter* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIAdapter* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIAdapter* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIAdapter* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIAdapter* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIAdapter* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIAdapter* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIAdapter methods ***/
+    HRESULT (STDMETHODCALLTYPE *EnumOutputs)(
+        IDXGIAdapter* This,
+        UINT output_idx,
+        IDXGIOutput **output);
+
+    HRESULT (STDMETHODCALLTYPE *GetDesc)(
+        IDXGIAdapter* This,
+        DXGI_ADAPTER_DESC *desc);
+
+    HRESULT (STDMETHODCALLTYPE *CheckInterfaceSupport)(
+        IDXGIAdapter* This,
+        REFGUID guid,
+        LARGE_INTEGER *umd_version);
+
+    END_INTERFACE
+} IDXGIAdapterVtbl;
+interface IDXGIAdapter {
+    CONST_VTBL IDXGIAdapterVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIAdapter_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIAdapter_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIAdapter_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIAdapter_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIAdapter_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIAdapter_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIAdapter_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIAdapter methods ***/
+#define IDXGIAdapter_EnumOutputs(This,output_idx,output) (This)->lpVtbl->EnumOutputs(This,output_idx,output)
+#define IDXGIAdapter_GetDesc(This,desc) (This)->lpVtbl->GetDesc(This,desc)
+#define IDXGIAdapter_CheckInterfaceSupport(This,guid,umd_version) (This)->lpVtbl->CheckInterfaceSupport(This,guid,umd_version)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter_QueryInterface(IDXGIAdapter* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIAdapter_AddRef(IDXGIAdapter* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIAdapter_Release(IDXGIAdapter* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter_SetPrivateData(IDXGIAdapter* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIAdapter_SetPrivateDataInterface(IDXGIAdapter* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIAdapter_GetPrivateData(IDXGIAdapter* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIAdapter_GetParent(IDXGIAdapter* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIAdapter methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter_EnumOutputs(IDXGIAdapter* This,UINT output_idx,IDXGIOutput **output) {
+    return This->lpVtbl->EnumOutputs(This,output_idx,output);
+}
+static FORCEINLINE HRESULT IDXGIAdapter_GetDesc(IDXGIAdapter* This,DXGI_ADAPTER_DESC *desc) {
+    return This->lpVtbl->GetDesc(This,desc);
+}
+static FORCEINLINE HRESULT IDXGIAdapter_CheckInterfaceSupport(IDXGIAdapter* This,REFGUID guid,LARGE_INTEGER *umd_version) {
+    return This->lpVtbl->CheckInterfaceSupport(This,guid,umd_version);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIAdapter_EnumOutputs_Proxy(
+    IDXGIAdapter* This,
+    UINT output_idx,
+    IDXGIOutput **output);
+void __RPC_STUB IDXGIAdapter_EnumOutputs_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIAdapter_GetDesc_Proxy(
+    IDXGIAdapter* This,
+    DXGI_ADAPTER_DESC *desc);
+void __RPC_STUB IDXGIAdapter_GetDesc_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIAdapter_CheckInterfaceSupport_Proxy(
+    IDXGIAdapter* This,
+    REFGUID guid,
+    LARGE_INTEGER *umd_version);
+void __RPC_STUB IDXGIAdapter_CheckInterfaceSupport_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIAdapter_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGISwapChain interface
+ */
+#ifndef __IDXGISwapChain_INTERFACE_DEFINED__
+#define __IDXGISwapChain_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGISwapChain, 0x310d36a0, 0xd2e7, 0x4c0a, 0xaa,0x04, 0x6a,0x9d,0x23,0xb8,0x88,0x6a);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("310d36a0-d2e7-4c0a-aa04-6a9d23b8886a")
+IDXGISwapChain : public IDXGIDeviceSubObject
+{
+    virtual HRESULT STDMETHODCALLTYPE Present(
+        UINT sync_interval,
+        UINT flags) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetBuffer(
+        UINT buffer_idx,
+        REFIID riid,
+        void **surface) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetFullscreenState(
+        WINBOOL fullscreen,
+        IDXGIOutput *target) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetFullscreenState(
+        WINBOOL *fullscreen,
+        IDXGIOutput **target) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetDesc(
+        DXGI_SWAP_CHAIN_DESC *desc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE ResizeBuffers(
+        UINT buffer_count,
+        UINT width,
+        UINT height,
+        DXGI_FORMAT format,
+        UINT flags) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE ResizeTarget(
+        const DXGI_MODE_DESC *target_mode_desc) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetContainingOutput(
+        IDXGIOutput **output) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetFrameStatistics(
+        DXGI_FRAME_STATISTICS *stats) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetLastPresentCount(
+        UINT *last_present_count) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGISwapChain, 0x310d36a0, 0xd2e7, 0x4c0a, 0xaa,0x04, 0x6a,0x9d,0x23,0xb8,0x88,0x6a)
+#endif
+#else
+typedef struct IDXGISwapChainVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGISwapChain* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGISwapChain* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGISwapChain* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGISwapChain* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGISwapChain* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGISwapChain* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGISwapChain* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIDeviceSubObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDevice)(
+        IDXGISwapChain* This,
+        REFIID riid,
+        void **device);
+
+    /*** IDXGISwapChain methods ***/
+    HRESULT (STDMETHODCALLTYPE *Present)(
+        IDXGISwapChain* This,
+        UINT sync_interval,
+        UINT flags);
+
+    HRESULT (STDMETHODCALLTYPE *GetBuffer)(
+        IDXGISwapChain* This,
+        UINT buffer_idx,
+        REFIID riid,
+        void **surface);
+
+    HRESULT (STDMETHODCALLTYPE *SetFullscreenState)(
+        IDXGISwapChain* This,
+        WINBOOL fullscreen,
+        IDXGIOutput *target);
+
+    HRESULT (STDMETHODCALLTYPE *GetFullscreenState)(
+        IDXGISwapChain* This,
+        WINBOOL *fullscreen,
+        IDXGIOutput **target);
+
+    HRESULT (STDMETHODCALLTYPE *GetDesc)(
+        IDXGISwapChain* This,
+        DXGI_SWAP_CHAIN_DESC *desc);
+
+    HRESULT (STDMETHODCALLTYPE *ResizeBuffers)(
+        IDXGISwapChain* This,
+        UINT buffer_count,
+        UINT width,
+        UINT height,
+        DXGI_FORMAT format,
+        UINT flags);
+
+    HRESULT (STDMETHODCALLTYPE *ResizeTarget)(
+        IDXGISwapChain* This,
+        const DXGI_MODE_DESC *target_mode_desc);
+
+    HRESULT (STDMETHODCALLTYPE *GetContainingOutput)(
+        IDXGISwapChain* This,
+        IDXGIOutput **output);
+
+    HRESULT (STDMETHODCALLTYPE *GetFrameStatistics)(
+        IDXGISwapChain* This,
+        DXGI_FRAME_STATISTICS *stats);
+
+    HRESULT (STDMETHODCALLTYPE *GetLastPresentCount)(
+        IDXGISwapChain* This,
+        UINT *last_present_count);
+
+    END_INTERFACE
+} IDXGISwapChainVtbl;
+interface IDXGISwapChain {
+    CONST_VTBL IDXGISwapChainVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGISwapChain_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGISwapChain_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGISwapChain_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGISwapChain_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGISwapChain_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGISwapChain_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGISwapChain_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIDeviceSubObject methods ***/
+#define IDXGISwapChain_GetDevice(This,riid,device) (This)->lpVtbl->GetDevice(This,riid,device)
+/*** IDXGISwapChain methods ***/
+#define IDXGISwapChain_Present(This,sync_interval,flags) (This)->lpVtbl->Present(This,sync_interval,flags)
+#define IDXGISwapChain_GetBuffer(This,buffer_idx,riid,surface) (This)->lpVtbl->GetBuffer(This,buffer_idx,riid,surface)
+#define IDXGISwapChain_SetFullscreenState(This,fullscreen,target) (This)->lpVtbl->SetFullscreenState(This,fullscreen,target)
+#define IDXGISwapChain_GetFullscreenState(This,fullscreen,target) (This)->lpVtbl->GetFullscreenState(This,fullscreen,target)
+#define IDXGISwapChain_GetDesc(This,desc) (This)->lpVtbl->GetDesc(This,desc)
+#define IDXGISwapChain_ResizeBuffers(This,buffer_count,width,height,format,flags) (This)->lpVtbl->ResizeBuffers(This,buffer_count,width,height,format,flags)
+#define IDXGISwapChain_ResizeTarget(This,target_mode_desc) (This)->lpVtbl->ResizeTarget(This,target_mode_desc)
+#define IDXGISwapChain_GetContainingOutput(This,output) (This)->lpVtbl->GetContainingOutput(This,output)
+#define IDXGISwapChain_GetFrameStatistics(This,stats) (This)->lpVtbl->GetFrameStatistics(This,stats)
+#define IDXGISwapChain_GetLastPresentCount(This,last_present_count) (This)->lpVtbl->GetLastPresentCount(This,last_present_count)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGISwapChain_QueryInterface(IDXGISwapChain* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGISwapChain_AddRef(IDXGISwapChain* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGISwapChain_Release(IDXGISwapChain* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGISwapChain_SetPrivateData(IDXGISwapChain* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_SetPrivateDataInterface(IDXGISwapChain* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetPrivateData(IDXGISwapChain* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetParent(IDXGISwapChain* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIDeviceSubObject methods ***/
+static FORCEINLINE HRESULT IDXGISwapChain_GetDevice(IDXGISwapChain* This,REFIID riid,void **device) {
+    return This->lpVtbl->GetDevice(This,riid,device);
+}
+/*** IDXGISwapChain methods ***/
+static FORCEINLINE HRESULT IDXGISwapChain_Present(IDXGISwapChain* This,UINT sync_interval,UINT flags) {
+    return This->lpVtbl->Present(This,sync_interval,flags);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetBuffer(IDXGISwapChain* This,UINT buffer_idx,REFIID riid,void **surface) {
+    return This->lpVtbl->GetBuffer(This,buffer_idx,riid,surface);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_SetFullscreenState(IDXGISwapChain* This,WINBOOL fullscreen,IDXGIOutput *target) {
+    return This->lpVtbl->SetFullscreenState(This,fullscreen,target);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetFullscreenState(IDXGISwapChain* This,WINBOOL *fullscreen,IDXGIOutput **target) {
+    return This->lpVtbl->GetFullscreenState(This,fullscreen,target);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetDesc(IDXGISwapChain* This,DXGI_SWAP_CHAIN_DESC *desc) {
+    return This->lpVtbl->GetDesc(This,desc);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_ResizeBuffers(IDXGISwapChain* This,UINT buffer_count,UINT width,UINT height,DXGI_FORMAT format,UINT flags) {
+    return This->lpVtbl->ResizeBuffers(This,buffer_count,width,height,format,flags);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_ResizeTarget(IDXGISwapChain* This,const DXGI_MODE_DESC *target_mode_desc) {
+    return This->lpVtbl->ResizeTarget(This,target_mode_desc);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetContainingOutput(IDXGISwapChain* This,IDXGIOutput **output) {
+    return This->lpVtbl->GetContainingOutput(This,output);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetFrameStatistics(IDXGISwapChain* This,DXGI_FRAME_STATISTICS *stats) {
+    return This->lpVtbl->GetFrameStatistics(This,stats);
+}
+static FORCEINLINE HRESULT IDXGISwapChain_GetLastPresentCount(IDXGISwapChain* This,UINT *last_present_count) {
+    return This->lpVtbl->GetLastPresentCount(This,last_present_count);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_Present_Proxy(
+    IDXGISwapChain* This,
+    UINT sync_interval,
+    UINT flags);
+void __RPC_STUB IDXGISwapChain_Present_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_GetBuffer_Proxy(
+    IDXGISwapChain* This,
+    UINT buffer_idx,
+    REFIID riid,
+    void **surface);
+void __RPC_STUB IDXGISwapChain_GetBuffer_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_SetFullscreenState_Proxy(
+    IDXGISwapChain* This,
+    WINBOOL fullscreen,
+    IDXGIOutput *target);
+void __RPC_STUB IDXGISwapChain_SetFullscreenState_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_GetFullscreenState_Proxy(
+    IDXGISwapChain* This,
+    WINBOOL *fullscreen,
+    IDXGIOutput **target);
+void __RPC_STUB IDXGISwapChain_GetFullscreenState_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_GetDesc_Proxy(
+    IDXGISwapChain* This,
+    DXGI_SWAP_CHAIN_DESC *desc);
+void __RPC_STUB IDXGISwapChain_GetDesc_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_ResizeBuffers_Proxy(
+    IDXGISwapChain* This,
+    UINT buffer_count,
+    UINT width,
+    UINT height,
+    DXGI_FORMAT format,
+    UINT flags);
+void __RPC_STUB IDXGISwapChain_ResizeBuffers_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_ResizeTarget_Proxy(
+    IDXGISwapChain* This,
+    const DXGI_MODE_DESC *target_mode_desc);
+void __RPC_STUB IDXGISwapChain_ResizeTarget_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_GetContainingOutput_Proxy(
+    IDXGISwapChain* This,
+    IDXGIOutput **output);
+void __RPC_STUB IDXGISwapChain_GetContainingOutput_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_GetFrameStatistics_Proxy(
+    IDXGISwapChain* This,
+    DXGI_FRAME_STATISTICS *stats);
+void __RPC_STUB IDXGISwapChain_GetFrameStatistics_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGISwapChain_GetLastPresentCount_Proxy(
+    IDXGISwapChain* This,
+    UINT *last_present_count);
+void __RPC_STUB IDXGISwapChain_GetLastPresentCount_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGISwapChain_INTERFACE_DEFINED__ */
+
+#define DXGI_MWA_NO_WINDOW_CHANGES  0x1
+#define DXGI_MWA_NO_ALT_ENTER       0x2
+#define DXGI_MWA_NO_PRINT_SCREEN    0x4
+#define DXGI_MWA_VALID              0x7
+/*****************************************************************************
+ * IDXGIFactory interface
+ */
+#ifndef __IDXGIFactory_INTERFACE_DEFINED__
+#define __IDXGIFactory_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIFactory, 0x7b7166ec, 0x21c7, 0x44ae, 0xb2,0x1a, 0xc9,0xae,0x32,0x1a,0xe3,0x69);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("7b7166ec-21c7-44ae-b21a-c9ae321ae369")
+IDXGIFactory : public IDXGIObject
+{
+    virtual HRESULT STDMETHODCALLTYPE EnumAdapters(
+        UINT adapter_idx,
+        IDXGIAdapter **adapter) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE MakeWindowAssociation(
+        HWND window,
+        UINT flags) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetWindowAssociation(
+        HWND *window) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE CreateSwapChain(
+        IUnknown *device,
+        DXGI_SWAP_CHAIN_DESC *desc,
+        IDXGISwapChain **swapchain) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE CreateSoftwareAdapter(
+        HMODULE swrast,
+        IDXGIAdapter **adapter) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIFactory, 0x7b7166ec, 0x21c7, 0x44ae, 0xb2,0x1a, 0xc9,0xae,0x32,0x1a,0xe3,0x69)
+#endif
+#else
+typedef struct IDXGIFactoryVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIFactory* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIFactory* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIFactory* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIFactory* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIFactory* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIFactory* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIFactory* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIFactory methods ***/
+    HRESULT (STDMETHODCALLTYPE *EnumAdapters)(
+        IDXGIFactory* This,
+        UINT adapter_idx,
+        IDXGIAdapter **adapter);
+
+    HRESULT (STDMETHODCALLTYPE *MakeWindowAssociation)(
+        IDXGIFactory* This,
+        HWND window,
+        UINT flags);
+
+    HRESULT (STDMETHODCALLTYPE *GetWindowAssociation)(
+        IDXGIFactory* This,
+        HWND *window);
+
+    HRESULT (STDMETHODCALLTYPE *CreateSwapChain)(
+        IDXGIFactory* This,
+        IUnknown *device,
+        DXGI_SWAP_CHAIN_DESC *desc,
+        IDXGISwapChain **swapchain);
+
+    HRESULT (STDMETHODCALLTYPE *CreateSoftwareAdapter)(
+        IDXGIFactory* This,
+        HMODULE swrast,
+        IDXGIAdapter **adapter);
+
+    END_INTERFACE
+} IDXGIFactoryVtbl;
+interface IDXGIFactory {
+    CONST_VTBL IDXGIFactoryVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIFactory_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIFactory_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIFactory_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIFactory_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIFactory_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIFactory_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIFactory_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIFactory methods ***/
+#define IDXGIFactory_EnumAdapters(This,adapter_idx,adapter) (This)->lpVtbl->EnumAdapters(This,adapter_idx,adapter)
+#define IDXGIFactory_MakeWindowAssociation(This,window,flags) (This)->lpVtbl->MakeWindowAssociation(This,window,flags)
+#define IDXGIFactory_GetWindowAssociation(This,window) (This)->lpVtbl->GetWindowAssociation(This,window)
+#define IDXGIFactory_CreateSwapChain(This,device,desc,swapchain) (This)->lpVtbl->CreateSwapChain(This,device,desc,swapchain)
+#define IDXGIFactory_CreateSoftwareAdapter(This,swrast,adapter) (This)->lpVtbl->CreateSoftwareAdapter(This,swrast,adapter)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIFactory_QueryInterface(IDXGIFactory* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIFactory_AddRef(IDXGIFactory* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIFactory_Release(IDXGIFactory* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIFactory_SetPrivateData(IDXGIFactory* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIFactory_SetPrivateDataInterface(IDXGIFactory* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIFactory_GetPrivateData(IDXGIFactory* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIFactory_GetParent(IDXGIFactory* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIFactory methods ***/
+static FORCEINLINE HRESULT IDXGIFactory_EnumAdapters(IDXGIFactory* This,UINT adapter_idx,IDXGIAdapter **adapter) {
+    return This->lpVtbl->EnumAdapters(This,adapter_idx,adapter);
+}
+static FORCEINLINE HRESULT IDXGIFactory_MakeWindowAssociation(IDXGIFactory* This,HWND window,UINT flags) {
+    return This->lpVtbl->MakeWindowAssociation(This,window,flags);
+}
+static FORCEINLINE HRESULT IDXGIFactory_GetWindowAssociation(IDXGIFactory* This,HWND *window) {
+    return This->lpVtbl->GetWindowAssociation(This,window);
+}
+static FORCEINLINE HRESULT IDXGIFactory_CreateSwapChain(IDXGIFactory* This,IUnknown *device,DXGI_SWAP_CHAIN_DESC *desc,IDXGISwapChain **swapchain) {
+    return This->lpVtbl->CreateSwapChain(This,device,desc,swapchain);
+}
+static FORCEINLINE HRESULT IDXGIFactory_CreateSoftwareAdapter(IDXGIFactory* This,HMODULE swrast,IDXGIAdapter **adapter) {
+    return This->lpVtbl->CreateSoftwareAdapter(This,swrast,adapter);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIFactory_EnumAdapters_Proxy(
+    IDXGIFactory* This,
+    UINT adapter_idx,
+    IDXGIAdapter **adapter);
+void __RPC_STUB IDXGIFactory_EnumAdapters_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIFactory_MakeWindowAssociation_Proxy(
+    IDXGIFactory* This,
+    HWND window,
+    UINT flags);
+void __RPC_STUB IDXGIFactory_MakeWindowAssociation_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIFactory_GetWindowAssociation_Proxy(
+    IDXGIFactory* This,
+    HWND *window);
+void __RPC_STUB IDXGIFactory_GetWindowAssociation_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIFactory_CreateSwapChain_Proxy(
+    IDXGIFactory* This,
+    IUnknown *device,
+    DXGI_SWAP_CHAIN_DESC *desc,
+    IDXGISwapChain **swapchain);
+void __RPC_STUB IDXGIFactory_CreateSwapChain_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIFactory_CreateSoftwareAdapter_Proxy(
+    IDXGIFactory* This,
+    HMODULE swrast,
+    IDXGIAdapter **adapter);
+void __RPC_STUB IDXGIFactory_CreateSoftwareAdapter_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIFactory_INTERFACE_DEFINED__ */
+
+HRESULT __stdcall  CreateDXGIFactory(REFIID riid,void **factory);
+HRESULT __stdcall  CreateDXGIFactory1(REFIID riid,void **factory);
+
+/*****************************************************************************
+ * IDXGIDevice interface
+ */
+#ifndef __IDXGIDevice_INTERFACE_DEFINED__
+#define __IDXGIDevice_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIDevice, 0x54ec77fa, 0x1377, 0x44e6, 0x8c,0x32, 0x88,0xfd,0x5f,0x44,0xc8,0x4c);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("54ec77fa-1377-44e6-8c32-88fd5f44c84c")
+IDXGIDevice : public IDXGIObject
+{
+    virtual HRESULT STDMETHODCALLTYPE GetAdapter(
+        IDXGIAdapter **adapter) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE CreateSurface(
+        const DXGI_SURFACE_DESC *desc,
+        UINT surface_count,
+        DXGI_USAGE usage,
+        const DXGI_SHARED_RESOURCE *shared_resource,
+        IDXGISurface **surface) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE QueryResourceResidency(
+        IUnknown *const *resources,
+        DXGI_RESIDENCY *residency,
+        UINT resource_count) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE SetGPUThreadPriority(
+        INT priority) = 0;
+
+    virtual HRESULT STDMETHODCALLTYPE GetGPUThreadPriority(
+        INT *priority) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIDevice, 0x54ec77fa, 0x1377, 0x44e6, 0x8c,0x32, 0x88,0xfd,0x5f,0x44,0xc8,0x4c)
+#endif
+#else
+typedef struct IDXGIDeviceVtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIDevice* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIDevice* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIDevice* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIDevice* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIDevice* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIDevice* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIDevice* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIDevice methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetAdapter)(
+        IDXGIDevice* This,
+        IDXGIAdapter **adapter);
+
+    HRESULT (STDMETHODCALLTYPE *CreateSurface)(
+        IDXGIDevice* This,
+        const DXGI_SURFACE_DESC *desc,
+        UINT surface_count,
+        DXGI_USAGE usage,
+        const DXGI_SHARED_RESOURCE *shared_resource,
+        IDXGISurface **surface);
+
+    HRESULT (STDMETHODCALLTYPE *QueryResourceResidency)(
+        IDXGIDevice* This,
+        IUnknown *const *resources,
+        DXGI_RESIDENCY *residency,
+        UINT resource_count);
+
+    HRESULT (STDMETHODCALLTYPE *SetGPUThreadPriority)(
+        IDXGIDevice* This,
+        INT priority);
+
+    HRESULT (STDMETHODCALLTYPE *GetGPUThreadPriority)(
+        IDXGIDevice* This,
+        INT *priority);
+
+    END_INTERFACE
+} IDXGIDeviceVtbl;
+interface IDXGIDevice {
+    CONST_VTBL IDXGIDeviceVtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIDevice_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIDevice_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIDevice_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIDevice_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIDevice_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIDevice_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIDevice_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIDevice methods ***/
+#define IDXGIDevice_GetAdapter(This,adapter) (This)->lpVtbl->GetAdapter(This,adapter)
+#define IDXGIDevice_CreateSurface(This,desc,surface_count,usage,shared_resource,surface) (This)->lpVtbl->CreateSurface(This,desc,surface_count,usage,shared_resource,surface)
+#define IDXGIDevice_QueryResourceResidency(This,resources,residency,resource_count) (This)->lpVtbl->QueryResourceResidency(This,resources,residency,resource_count)
+#define IDXGIDevice_SetGPUThreadPriority(This,priority) (This)->lpVtbl->SetGPUThreadPriority(This,priority)
+#define IDXGIDevice_GetGPUThreadPriority(This,priority) (This)->lpVtbl->GetGPUThreadPriority(This,priority)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIDevice_QueryInterface(IDXGIDevice* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIDevice_AddRef(IDXGIDevice* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIDevice_Release(IDXGIDevice* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIDevice_SetPrivateData(IDXGIDevice* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIDevice_SetPrivateDataInterface(IDXGIDevice* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIDevice_GetPrivateData(IDXGIDevice* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIDevice_GetParent(IDXGIDevice* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIDevice methods ***/
+static FORCEINLINE HRESULT IDXGIDevice_GetAdapter(IDXGIDevice* This,IDXGIAdapter **adapter) {
+    return This->lpVtbl->GetAdapter(This,adapter);
+}
+static FORCEINLINE HRESULT IDXGIDevice_CreateSurface(IDXGIDevice* This,const DXGI_SURFACE_DESC *desc,UINT surface_count,DXGI_USAGE usage,const DXGI_SHARED_RESOURCE *shared_resource,IDXGISurface **surface) {
+    return This->lpVtbl->CreateSurface(This,desc,surface_count,usage,shared_resource,surface);
+}
+static FORCEINLINE HRESULT IDXGIDevice_QueryResourceResidency(IDXGIDevice* This,IUnknown *const *resources,DXGI_RESIDENCY *residency,UINT resource_count) {
+    return This->lpVtbl->QueryResourceResidency(This,resources,residency,resource_count);
+}
+static FORCEINLINE HRESULT IDXGIDevice_SetGPUThreadPriority(IDXGIDevice* This,INT priority) {
+    return This->lpVtbl->SetGPUThreadPriority(This,priority);
+}
+static FORCEINLINE HRESULT IDXGIDevice_GetGPUThreadPriority(IDXGIDevice* This,INT *priority) {
+    return This->lpVtbl->GetGPUThreadPriority(This,priority);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIDevice_GetAdapter_Proxy(
+    IDXGIDevice* This,
+    IDXGIAdapter **adapter);
+void __RPC_STUB IDXGIDevice_GetAdapter_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIDevice_CreateSurface_Proxy(
+    IDXGIDevice* This,
+    const DXGI_SURFACE_DESC *desc,
+    UINT surface_count,
+    DXGI_USAGE usage,
+    const DXGI_SHARED_RESOURCE *shared_resource,
+    IDXGISurface **surface);
+void __RPC_STUB IDXGIDevice_CreateSurface_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIDevice_QueryResourceResidency_Proxy(
+    IDXGIDevice* This,
+    IUnknown *const *resources,
+    DXGI_RESIDENCY *residency,
+    UINT resource_count);
+void __RPC_STUB IDXGIDevice_QueryResourceResidency_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIDevice_SetGPUThreadPriority_Proxy(
+    IDXGIDevice* This,
+    INT priority);
+void __RPC_STUB IDXGIDevice_SetGPUThreadPriority_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+HRESULT STDMETHODCALLTYPE IDXGIDevice_GetGPUThreadPriority_Proxy(
+    IDXGIDevice* This,
+    INT *priority);
+void __RPC_STUB IDXGIDevice_GetGPUThreadPriority_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIDevice_INTERFACE_DEFINED__ */
+
+typedef enum DXGI_ADAPTER_FLAG {
+    DXGI_ADAPTER_FLAG_NONE = 0,
+    DXGI_ADAPTER_FLAG_REMOTE = 1,
+    DXGI_ADAPTER_FLAG_FORCE_DWORD = 0xffffffff
+} DXGI_ADAPTER_FLAG;
+typedef struct DXGI_ADAPTER_DESC1 {
+    WCHAR Description[128];
+    UINT VendorId;
+    UINT DeviceId;
+    UINT SubSysId;
+    UINT Revision;
+    SIZE_T DedicatedVideoMemory;
+    SIZE_T DedicatedSystemMemory;
+    SIZE_T SharedSystemMemory;
+    LUID AdapterLuid;
+    UINT Flags;
+} DXGI_ADAPTER_DESC1;
+/*****************************************************************************
+ * IDXGIAdapter1 interface
+ */
+#ifndef __IDXGIAdapter1_INTERFACE_DEFINED__
+#define __IDXGIAdapter1_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIAdapter1, 0x29038f61, 0x3839, 0x4626, 0x91,0xfd, 0x08,0x68,0x79,0x01,0x1a,0x05);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("29038f61-3839-4626-91fd-086879011a05")
+IDXGIAdapter1 : public IDXGIAdapter
+{
+    virtual HRESULT STDMETHODCALLTYPE GetDesc1(
+        DXGI_ADAPTER_DESC1 *pDesc) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIAdapter1, 0x29038f61, 0x3839, 0x4626, 0x91,0xfd, 0x08,0x68,0x79,0x01,0x1a,0x05)
+#endif
+#else
+typedef struct IDXGIAdapter1Vtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIAdapter1* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIAdapter1* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIAdapter1* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIAdapter1* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIAdapter1* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIAdapter1* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIAdapter1* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIAdapter methods ***/
+    HRESULT (STDMETHODCALLTYPE *EnumOutputs)(
+        IDXGIAdapter1* This,
+        UINT output_idx,
+        IDXGIOutput **output);
+
+    HRESULT (STDMETHODCALLTYPE *GetDesc)(
+        IDXGIAdapter1* This,
+        DXGI_ADAPTER_DESC *desc);
+
+    HRESULT (STDMETHODCALLTYPE *CheckInterfaceSupport)(
+        IDXGIAdapter1* This,
+        REFGUID guid,
+        LARGE_INTEGER *umd_version);
+
+    /*** IDXGIAdapter1 methods ***/
+    HRESULT (STDMETHODCALLTYPE *GetDesc1)(
+        IDXGIAdapter1* This,
+        DXGI_ADAPTER_DESC1 *pDesc);
+
+    END_INTERFACE
+} IDXGIAdapter1Vtbl;
+interface IDXGIAdapter1 {
+    CONST_VTBL IDXGIAdapter1Vtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIAdapter1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIAdapter1_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIAdapter1_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIAdapter1_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIAdapter1_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIAdapter1_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIAdapter1_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIAdapter methods ***/
+#define IDXGIAdapter1_EnumOutputs(This,output_idx,output) (This)->lpVtbl->EnumOutputs(This,output_idx,output)
+#define IDXGIAdapter1_GetDesc(This,desc) (This)->lpVtbl->GetDesc(This,desc)
+#define IDXGIAdapter1_CheckInterfaceSupport(This,guid,umd_version) (This)->lpVtbl->CheckInterfaceSupport(This,guid,umd_version)
+/*** IDXGIAdapter1 methods ***/
+#define IDXGIAdapter1_GetDesc1(This,pDesc) (This)->lpVtbl->GetDesc1(This,pDesc)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter1_QueryInterface(IDXGIAdapter1* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIAdapter1_AddRef(IDXGIAdapter1* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIAdapter1_Release(IDXGIAdapter1* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter1_SetPrivateData(IDXGIAdapter1* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIAdapter1_SetPrivateDataInterface(IDXGIAdapter1* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIAdapter1_GetPrivateData(IDXGIAdapter1* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIAdapter1_GetParent(IDXGIAdapter1* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIAdapter methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter1_EnumOutputs(IDXGIAdapter1* This,UINT output_idx,IDXGIOutput **output) {
+    return This->lpVtbl->EnumOutputs(This,output_idx,output);
+}
+static FORCEINLINE HRESULT IDXGIAdapter1_GetDesc(IDXGIAdapter1* This,DXGI_ADAPTER_DESC *desc) {
+    return This->lpVtbl->GetDesc(This,desc);
+}
+static FORCEINLINE HRESULT IDXGIAdapter1_CheckInterfaceSupport(IDXGIAdapter1* This,REFGUID guid,LARGE_INTEGER *umd_version) {
+    return This->lpVtbl->CheckInterfaceSupport(This,guid,umd_version);
+}
+/*** IDXGIAdapter1 methods ***/
+static FORCEINLINE HRESULT IDXGIAdapter1_GetDesc1(IDXGIAdapter1* This,DXGI_ADAPTER_DESC1 *pDesc) {
+    return This->lpVtbl->GetDesc1(This,pDesc);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIAdapter1_GetDesc1_Proxy(
+    IDXGIAdapter1* This,
+    DXGI_ADAPTER_DESC1 *pDesc);
+void __RPC_STUB IDXGIAdapter1_GetDesc1_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIAdapter1_INTERFACE_DEFINED__ */
+
+/*****************************************************************************
+ * IDXGIFactory1 interface
+ */
+#ifndef __IDXGIFactory1_INTERFACE_DEFINED__
+#define __IDXGIFactory1_INTERFACE_DEFINED__
+
+DEFINE_GUID(IID_IDXGIFactory1, 0x770aae78, 0xf26f, 0x4dba, 0xa8,0x29, 0x25,0x3c,0x83,0xd1,0xb3,0x87);
+#if defined(__cplusplus) && !defined(CINTERFACE)
+MIDL_INTERFACE("770aae78-f26f-4dba-a829-253c83d1b387")
+IDXGIFactory1 : public IDXGIFactory
+{
+    virtual HRESULT STDMETHODCALLTYPE EnumAdapters1(
+        UINT Adapter,
+        IDXGIAdapter1 **ppAdapter) = 0;
+
+    virtual WINBOOL STDMETHODCALLTYPE IsCurrent(
+        ) = 0;
+
+};
+#ifdef __CRT_UUID_DECL
+__CRT_UUID_DECL(IDXGIFactory1, 0x770aae78, 0xf26f, 0x4dba, 0xa8,0x29, 0x25,0x3c,0x83,0xd1,0xb3,0x87)
+#endif
+#else
+typedef struct IDXGIFactory1Vtbl {
+    BEGIN_INTERFACE
+
+    /*** IUnknown methods ***/
+    HRESULT (STDMETHODCALLTYPE *QueryInterface)(
+        IDXGIFactory1* This,
+        REFIID riid,
+        void **ppvObject);
+
+    ULONG (STDMETHODCALLTYPE *AddRef)(
+        IDXGIFactory1* This);
+
+    ULONG (STDMETHODCALLTYPE *Release)(
+        IDXGIFactory1* This);
+
+    /*** IDXGIObject methods ***/
+    HRESULT (STDMETHODCALLTYPE *SetPrivateData)(
+        IDXGIFactory1* This,
+        REFGUID guid,
+        UINT data_size,
+        const void *data);
+
+    HRESULT (STDMETHODCALLTYPE *SetPrivateDataInterface)(
+        IDXGIFactory1* This,
+        REFGUID guid,
+        const IUnknown *object);
+
+    HRESULT (STDMETHODCALLTYPE *GetPrivateData)(
+        IDXGIFactory1* This,
+        REFGUID guid,
+        UINT *data_size,
+        void *data);
+
+    HRESULT (STDMETHODCALLTYPE *GetParent)(
+        IDXGIFactory1* This,
+        REFIID riid,
+        void **parent);
+
+    /*** IDXGIFactory methods ***/
+    HRESULT (STDMETHODCALLTYPE *EnumAdapters)(
+        IDXGIFactory1* This,
+        UINT adapter_idx,
+        IDXGIAdapter **adapter);
+
+    HRESULT (STDMETHODCALLTYPE *MakeWindowAssociation)(
+        IDXGIFactory1* This,
+        HWND window,
+        UINT flags);
+
+    HRESULT (STDMETHODCALLTYPE *GetWindowAssociation)(
+        IDXGIFactory1* This,
+        HWND *window);
+
+    HRESULT (STDMETHODCALLTYPE *CreateSwapChain)(
+        IDXGIFactory1* This,
+        IUnknown *device,
+        DXGI_SWAP_CHAIN_DESC *desc,
+        IDXGISwapChain **swapchain);
+
+    HRESULT (STDMETHODCALLTYPE *CreateSoftwareAdapter)(
+        IDXGIFactory1* This,
+        HMODULE swrast,
+        IDXGIAdapter **adapter);
+
+    /*** IDXGIFactory1 methods ***/
+    HRESULT (STDMETHODCALLTYPE *EnumAdapters1)(
+        IDXGIFactory1* This,
+        UINT Adapter,
+        IDXGIAdapter1 **ppAdapter);
+
+    WINBOOL (STDMETHODCALLTYPE *IsCurrent)(
+        IDXGIFactory1* This);
+
+    END_INTERFACE
+} IDXGIFactory1Vtbl;
+interface IDXGIFactory1 {
+    CONST_VTBL IDXGIFactory1Vtbl* lpVtbl;
+};
+
+#ifdef COBJMACROS
+#ifndef WIDL_C_INLINE_WRAPPERS
+/*** IUnknown methods ***/
+#define IDXGIFactory1_QueryInterface(This,riid,ppvObject) (This)->lpVtbl->QueryInterface(This,riid,ppvObject)
+#define IDXGIFactory1_AddRef(This) (This)->lpVtbl->AddRef(This)
+#define IDXGIFactory1_Release(This) (This)->lpVtbl->Release(This)
+/*** IDXGIObject methods ***/
+#define IDXGIFactory1_SetPrivateData(This,guid,data_size,data) (This)->lpVtbl->SetPrivateData(This,guid,data_size,data)
+#define IDXGIFactory1_SetPrivateDataInterface(This,guid,object) (This)->lpVtbl->SetPrivateDataInterface(This,guid,object)
+#define IDXGIFactory1_GetPrivateData(This,guid,data_size,data) (This)->lpVtbl->GetPrivateData(This,guid,data_size,data)
+#define IDXGIFactory1_GetParent(This,riid,parent) (This)->lpVtbl->GetParent(This,riid,parent)
+/*** IDXGIFactory methods ***/
+#define IDXGIFactory1_EnumAdapters(This,adapter_idx,adapter) (This)->lpVtbl->EnumAdapters(This,adapter_idx,adapter)
+#define IDXGIFactory1_MakeWindowAssociation(This,window,flags) (This)->lpVtbl->MakeWindowAssociation(This,window,flags)
+#define IDXGIFactory1_GetWindowAssociation(This,window) (This)->lpVtbl->GetWindowAssociation(This,window)
+#define IDXGIFactory1_CreateSwapChain(This,device,desc,swapchain) (This)->lpVtbl->CreateSwapChain(This,device,desc,swapchain)
+#define IDXGIFactory1_CreateSoftwareAdapter(This,swrast,adapter) (This)->lpVtbl->CreateSoftwareAdapter(This,swrast,adapter)
+/*** IDXGIFactory1 methods ***/
+#define IDXGIFactory1_EnumAdapters1(This,Adapter,ppAdapter) (This)->lpVtbl->EnumAdapters1(This,Adapter,ppAdapter)
+#define IDXGIFactory1_IsCurrent(This) (This)->lpVtbl->IsCurrent(This)
+#else
+/*** IUnknown methods ***/
+static FORCEINLINE HRESULT IDXGIFactory1_QueryInterface(IDXGIFactory1* This,REFIID riid,void **ppvObject) {
+    return This->lpVtbl->QueryInterface(This,riid,ppvObject);
+}
+static FORCEINLINE ULONG IDXGIFactory1_AddRef(IDXGIFactory1* This) {
+    return This->lpVtbl->AddRef(This);
+}
+static FORCEINLINE ULONG IDXGIFactory1_Release(IDXGIFactory1* This) {
+    return This->lpVtbl->Release(This);
+}
+/*** IDXGIObject methods ***/
+static FORCEINLINE HRESULT IDXGIFactory1_SetPrivateData(IDXGIFactory1* This,REFGUID guid,UINT data_size,const void *data) {
+    return This->lpVtbl->SetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_SetPrivateDataInterface(IDXGIFactory1* This,REFGUID guid,const IUnknown *object) {
+    return This->lpVtbl->SetPrivateDataInterface(This,guid,object);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_GetPrivateData(IDXGIFactory1* This,REFGUID guid,UINT *data_size,void *data) {
+    return This->lpVtbl->GetPrivateData(This,guid,data_size,data);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_GetParent(IDXGIFactory1* This,REFIID riid,void **parent) {
+    return This->lpVtbl->GetParent(This,riid,parent);
+}
+/*** IDXGIFactory methods ***/
+static FORCEINLINE HRESULT IDXGIFactory1_EnumAdapters(IDXGIFactory1* This,UINT adapter_idx,IDXGIAdapter **adapter) {
+    return This->lpVtbl->EnumAdapters(This,adapter_idx,adapter);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_MakeWindowAssociation(IDXGIFactory1* This,HWND window,UINT flags) {
+    return This->lpVtbl->MakeWindowAssociation(This,window,flags);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_GetWindowAssociation(IDXGIFactory1* This,HWND *window) {
+    return This->lpVtbl->GetWindowAssociation(This,window);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_CreateSwapChain(IDXGIFactory1* This,IUnknown *device,DXGI_SWAP_CHAIN_DESC *desc,IDXGISwapChain **swapchain) {
+    return This->lpVtbl->CreateSwapChain(This,device,desc,swapchain);
+}
+static FORCEINLINE HRESULT IDXGIFactory1_CreateSoftwareAdapter(IDXGIFactory1* This,HMODULE swrast,IDXGIAdapter **adapter) {
+    return This->lpVtbl->CreateSoftwareAdapter(This,swrast,adapter);
+}
+/*** IDXGIFactory1 methods ***/
+static FORCEINLINE HRESULT IDXGIFactory1_EnumAdapters1(IDXGIFactory1* This,UINT Adapter,IDXGIAdapter1 **ppAdapter) {
+    return This->lpVtbl->EnumAdapters1(This,Adapter,ppAdapter);
+}
+static FORCEINLINE WINBOOL IDXGIFactory1_IsCurrent(IDXGIFactory1* This) {
+    return This->lpVtbl->IsCurrent(This);
+}
+#endif
+#endif
+
+#endif
+
+HRESULT STDMETHODCALLTYPE IDXGIFactory1_EnumAdapters1_Proxy(
+    IDXGIFactory1* This,
+    UINT Adapter,
+    IDXGIAdapter1 **ppAdapter);
+void __RPC_STUB IDXGIFactory1_EnumAdapters1_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+WINBOOL STDMETHODCALLTYPE IDXGIFactory1_IsCurrent_Proxy(
+    IDXGIFactory1* This);
+void __RPC_STUB IDXGIFactory1_IsCurrent_Stub(
+    IRpcStubBuffer* This,
+    IRpcChannelBuffer* pRpcChannelBuffer,
+    PRPC_MESSAGE pRpcMessage,
+    DWORD* pdwStubPhase);
+
+#endif  /* __IDXGIFactory1_INTERFACE_DEFINED__ */
+
+/* Begin additional prototypes for all interfaces */
+
+
+/* End additional prototypes */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __dxgi_h__ */

+ 0 - 0
libobs-d3d9/.gitignore


+ 0 - 0
libobs-opengl/.gitignore


+ 38 - 0
libobs/graphics/axisang.c

@@ -0,0 +1,38 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "axisang.h"
+#include "quat.h"
+
+void axisang_from_quat(struct axisang *dst, const struct quat *q)
+{
+	float len, leni;
+
+	len = q->x*q->x + q->y*q->y + q->z*q->z;
+	if (!close_float(len, 0.0f, EPSILON)) {
+		leni = 1.0f/sqrtf(len);
+		dst->x = q->x * leni;
+		dst->y = q->y * leni;
+		dst->z = q->z * leni;
+		dst->w  = acosf(q->w)*2.0f;
+	} else {
+		dst->x = 0.0f;
+		dst->y = 0.0f;
+		dst->z = 0.0f;
+		dst->w = 0.0f;
+	}
+}

+ 67 - 0
libobs/graphics/axisang.h

@@ -0,0 +1,67 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef AXISANG_H
+#define AXISANG_H
+
+#include "../util/c99defs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct quat;
+
+struct axisang {
+	union {
+		struct {float x, y, z, w;};
+		float ptr[4];
+	};
+};
+
+static inline void axisang_zero(struct axisang *dst)
+{
+	dst->x = 0.0f;
+	dst->y = 0.0f;
+	dst->z = 0.0f;
+	dst->w = 0.0f;
+}
+
+static inline void axisang_copy(struct axisang *dst, struct axisang *aa)
+{
+	dst->x = aa->x;
+	dst->y = aa->y;
+	dst->z = aa->z;
+	dst->w = aa->w;
+}
+
+static inline void axisang_set(struct axisang *dst, float x, float y, float z,
+		float w)
+{
+	dst->x = x;
+	dst->y = y;
+	dst->z = z;
+	dst->w = w;
+}
+
+EXPORT void axisang_from_quat(struct axisang *dst, const struct quat *q);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 3 - 0
libobs/graphics/basemath.hpp

@@ -0,0 +1,3 @@
+#pragma once
+
+/* TODO: C++ math wrappers */

+ 263 - 0
libobs/graphics/bounds.c

@@ -0,0 +1,263 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "bounds.h"
+#include "matrix3.h"
+#include "plane.h"
+
+void bounds_move(struct bounds *dst, const struct bounds *b,
+		const struct vec3 *v)
+{
+	vec3_add(&dst->min, &b->min, v);
+	vec3_add(&dst->max, &b->max, v);
+}
+
+void bounds_scale(struct bounds *dst, const struct bounds *b,
+		const struct vec3 *v)
+{
+	vec3_mul(&dst->min, &b->min, v);
+	vec3_mul(&dst->max, &b->max, v);
+}
+
+void bounds_merge(struct bounds *dst, const struct bounds *b1,
+		const struct bounds *b2)
+{
+	vec3_min(&dst->min, &b1->min, &b2->min);
+	vec3_max(&dst->max, &b1->max, &b2->max);
+}
+
+void bounds_merge_point(struct bounds *dst, const struct bounds *b,
+		const struct vec3 *v)
+{
+	vec3_min(&dst->min, &b->min, v);
+	vec3_max(&dst->max, &b->max, v);
+}
+
+void bounds_get_point(struct vec3 *dst, const struct bounds *b,
+		unsigned int i)
+{
+	if (i > 8)
+		return;
+
+	/*
+	 * Note:
+	 * 0 = min.x,min.y,min.z
+	 * 1 = min.x,min.y,MAX.z
+	 * 2 = min.x,MAX.y,min.z
+	 * 3 = min.x,MAX.y,MAX.z
+	 * 4 = MAX.x,min.y,min.z
+	 * 5 = MAX.x,min.y,MAX.z
+
+	 * 6 = MAX.x,MAX.y,min.z
+	 * 7 = MAX.x,MAX.y,MAX.z
+	 */
+
+	if(i > 3)  {dst->x = b->max.x; i -= 4;}
+	else       {dst->x = b->min.x;}
+
+	if(i > 1)  {dst->y = b->max.y; i -= 2;}
+	else       {dst->y = b->min.y;}
+
+	dst->z = (i == 1) ? b->max.z : b->min.z;
+}
+
+void bounds_get_center(struct vec3 *dst, const struct bounds *b)
+{
+	vec3_sub(dst, &b->max, &b->min);
+	vec3_mulf(dst, dst, 0.5f);
+	vec3_add(dst, dst, &b->min);
+}
+
+void bounds_transform(struct bounds *dst, const struct bounds *b,
+		const struct matrix3 *m)
+{
+	struct bounds temp;
+	bool b_init = false;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		struct vec3 p;
+		bounds_get_point(&p, b, i);
+		vec3_transform(&p, &p, m);
+
+		if (!b_init) {
+			vec3_copy(&temp.min, &p);
+			vec3_copy(&temp.max, &p);
+			b_init = true;
+		} else {
+			if (p.x < temp.min.x)
+				temp.min.x = p.x;
+			else if(p.x > temp.max.x)
+				temp.max.x = p.x;
+
+			if(p.y < temp.min.y)
+				temp.min.y = p.y;
+			else if(p.y > temp.max.y)
+				temp.max.y = p.y;
+
+			if(p.z < temp.min.z)
+				temp.min.z = p.z;
+			else if(p.z > temp.max.z)
+				temp.max.z = p.z;
+		}
+	}
+
+	bounds_copy(dst, &temp);
+}
+
+bool bounds_intersection_ray(const struct bounds *b, const struct vec3 *orig,
+		const struct vec3 *dir, float *t)
+{
+	float t_max = M_INFINITE;
+	float t_min = -M_INFINITE;
+	struct vec3 center, max_offset, box_offset;
+	int i;
+
+	bounds_get_center(&center, b);
+	vec3_sub(&max_offset, &b->max, &center);
+	vec3_sub(&box_offset, &center, orig);
+
+	for (i = 0; i < 3; i++) {
+		float e = box_offset.ptr[i];
+		float f = dir->ptr[i];
+
+		if (fabsf(f) > 0.0f) {
+			float fi = 1.0f/f;
+			float t1  = (e+max_offset.ptr[i])*fi;
+			float t2  = (e-max_offset.ptr[i])*fi;
+			if (t1 > t2) {
+				if (t2 > t_min) t_min = t2;
+				if (t1 < t_max) t_max = t1;
+			} else {
+				if (t1 > t_min) t_min = t1;
+				if (t2 < t_max) t_max = t2;
+			}
+			if (t_min > t_max)
+				return false;
+			if (t_max < 0.0f)
+				return false;
+		} else if ((-e - max_offset.ptr[i]) > 0.0f ||
+		           (-e + max_offset.ptr[i]) < 0.0f) {
+			return false;
+		}
+	}
+
+	*t = (t_min > 0.0f) ? t_min : t_max;
+	return true;
+}
+
+bool bounds_intersection_line(const struct bounds *b, const struct vec3 *p1,
+		const struct vec3 *p2, float *t)
+{
+	struct vec3 dir;
+	float length;
+
+	vec3_sub(&dir, p2, p1);
+	length = vec3_len(&dir);
+	if (length <= TINY_EPSILON)
+		return false;
+
+	vec3_mulf(&dir, &dir, 1.0f/length);
+
+	if (!bounds_intersection_ray(b, p1, &dir, t))
+		return false;
+
+	*t /= length;
+	return true;
+}
+
+bool bounds_plane_test(const struct bounds *b, const struct plane *p)
+{
+	struct vec3 vmin, vmax;
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		if (p->dir.ptr[i] >= 0.0f) {
+			vmin.ptr[i] = b->min.ptr[i];
+			vmax.ptr[i] = b->max.ptr[i];
+		} else {
+			vmin.ptr[i] = b->max.ptr[i];
+			vmax.ptr[i] = b->min.ptr[i];
+		}
+	}
+
+	if (vec3_plane_dist(&vmin, p) > 0.0f)
+		return BOUNDS_OUTSIDE;
+
+	if (vec3_plane_dist(&vmax, p) >= 0.0f)
+		return BOUNDS_PARTIAL;
+
+	return BOUNDS_INSIDE;
+}
+
+bool bounds_under_plane(const struct bounds *b, const struct plane *p)
+{
+	struct vec3 vmin;
+
+	vmin.x = (p->dir.x < 0.0f) ? b->max.x : b->min.x;
+	vmin.y = (p->dir.y < 0.0f) ? b->max.y : b->min.y;
+	vmin.z = (p->dir.z < 0.0f) ? b->max.z : b->min.z;
+
+	return (vec3_dot(&vmin, &p->dir) <= p->dist);
+}
+
+bool bounds_intersects(const struct bounds *b, const struct bounds *test,
+		float epsilon)
+{
+	return ((b->min.x - test->max.x) <= epsilon) &&
+	       ((test->min.x - b->max.x) <= epsilon) &&
+	       ((b->min.y - test->max.y) <= epsilon) &&
+	       ((test->min.y - b->max.y) <= epsilon) &&
+	       ((b->min.z - test->max.z) <= epsilon) &&
+	       ((test->min.z - b->max.z) <= epsilon);
+}
+
+bool bounds_intersects_obb(const struct bounds *b, const struct bounds *test,
+		const struct matrix3 *m, float epsilon)
+{
+	struct bounds b_tr, test_tr;
+	struct matrix3 m_inv;
+
+	matrix3_transpose(&m_inv, m);
+
+	bounds_transform(&b_tr, b, m);
+	bounds_transform(&test_tr, test, &m_inv);
+
+	return bounds_intersects(b, &test_tr, epsilon) &&
+	       bounds_intersects(&b_tr, test, epsilon);
+}
+
+static inline float vec3or_offset_len(const struct bounds *b,
+		const struct vec3 *v)
+{
+	struct vec3 temp1, temp2;
+	vec3_sub(&temp1, &b->max, &b->min);
+	vec3_abs(&temp2, v);
+	return vec3_dot(&temp1, &temp2);
+}
+
+float bounds_min_dist(const struct bounds *b, const struct plane *p)
+{
+	struct vec3 center;
+	float vec_len = vec3or_offset_len(b, &p->dir) * 0.5f;
+	float center_dist;
+
+	bounds_get_center(&center, b);
+	center_dist = vec3_plane_dist(&center, p);
+
+	return p->dist + center_dist - vec_len;
+}

+ 135 - 0
libobs/graphics/bounds.h

@@ -0,0 +1,135 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef BOUNDS_H
+#define BOUNDS_H
+
+#include "math-defs.h"
+#include "vec3.h"
+
+/*
+ * Axis Aligned Bounding Box
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BOUNDS_MAX_X 1
+#define BOUNDS_MAX_Y 2
+#define BOUNDS_MAX_Z 4
+
+#define BOUNDS_OUTSIDE 1
+#define BOUNDS_INSIDE  2
+#define BOUNDS_PARTIAL 3
+
+struct bounds {
+	struct vec3 min, max;
+};
+
+static inline void bounds_zero(struct bounds *dst)
+{
+	vec3_zero(&dst->min);
+	vec3_zero(&dst->max);
+}
+
+static inline void bounds_copy(struct bounds *dst, const struct bounds *b)
+{
+	vec3_copy(&dst->min, &b->min);
+	vec3_copy(&dst->max, &b->max);
+}
+
+EXPORT void bounds_move(struct bounds *dst, const struct bounds *b,
+		const struct vec3 *v);
+
+EXPORT void bounds_scale(struct bounds *dst, const struct bounds *b,
+		const struct vec3 *v);
+
+EXPORT void bounds_merge(struct bounds *dst, const struct bounds *b1,
+		const struct bounds *b2);
+EXPORT void bounds_merge_point(struct bounds *dst, const struct bounds *b,
+		const struct vec3 *v);
+
+EXPORT void bounds_get_point(struct vec3 *dst, const struct bounds *b,
+		unsigned int i);
+EXPORT void bounds_get_center(struct vec3 *dst, const struct bounds *b);
+
+/**
+ * Note: transforms as OBB, then converts back to AABB, which can result in
+ * the actual size becoming larger than it originally was.
+ */
+EXPORT void bounds_transform(struct bounds *dst, const struct bounds *b,
+		const struct matrix3 *m);
+
+EXPORT bool bounds_intersection_ray(const struct bounds *b,
+		const struct vec3 *orig, const struct vec3 *dir, float *t);
+EXPORT bool bounds_intersection_line(const struct bounds *b,
+		const struct vec3 *p1, const struct vec3 *p2, float *t);
+
+EXPORT bool bounds_plane_test(const struct bounds *b, const struct plane *p);
+EXPORT bool bounds_under_plane(const struct bounds *b,
+		const struct plane *p);
+
+static inline bool bounds_inside(const struct bounds *b,
+		const struct bounds *test)
+{
+	return test->min.x >= b->min.x &&
+	       test->min.y >= b->min.y &&
+	       test->min.z >= b->min.z &&
+	       test->max.x <= b->max.x &&
+	       test->max.y <= b->max.y &&
+	       test->max.z <= b->max.z;
+}
+
+static inline bool bounds_vec3_inside(const struct bounds *b,
+		const struct vec3 *v)
+{
+	return  v->x >= (b->min.x-EPSILON) &&
+		v->x <= (b->max.x+EPSILON) &&
+		v->y >= (b->min.y-EPSILON) &&
+		v->y <= (b->max.y+EPSILON) &&
+		v->z >= (b->min.z-EPSILON) &&
+		v->z <= (b->max.z+EPSILON);
+}
+
+EXPORT bool bounds_intersects(const struct bounds *b,
+		const struct bounds *test, float epsilon);
+EXPORT bool bounds_intersects_obb(const struct bounds *b,
+		const struct bounds *test, const struct matrix3 *m,
+		float epsilon);
+
+static inline bool bounds_intersects_ray(const struct bounds *b,
+		const struct vec3 *orig, const struct vec3 *dir)
+{
+	float t;
+	return bounds_intersection_ray(b, orig, dir, &t);
+}
+
+static inline bool bounds_intersects_line(const struct bounds *b,
+		const struct vec3 *p1, const struct vec3 *p2)
+{
+	float t;
+	return bounds_intersection_line(b, p1, p2, &t);
+}
+
+EXPORT float bounds_min_dist(const struct bounds *b, const struct plane *p);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 1459 - 0
libobs/graphics/effect-parser.c

@@ -0,0 +1,1459 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <assert.h>
+#include "effect-parser.h"
+#include "effect.h"
+
+void ep_free(struct effect_parser *ep)
+{
+	size_t i;
+	for (i = 0; i < ep->params.num; i++)
+		ep_param_free(ep->params.array+i);
+	for (i = 0; i < ep->structs.num; i++)
+		ep_struct_free(ep->structs.array+i);
+	for (i = 0; i < ep->funcs.num; i++)
+		ep_func_free(ep->funcs.array+i);
+	for (i = 0; i < ep->samplers.num; i++)
+		ep_sampler_free(ep->samplers.array+i);
+	for (i = 0; i < ep->techniques.num; i++)
+		ep_technique_free(ep->techniques.array+i);
+
+	ep->cur_pass = NULL;
+	cf_parser_free(&ep->cfp);
+	da_free(ep->params);
+	da_free(ep->structs);
+	da_free(ep->funcs);
+	da_free(ep->samplers);
+	da_free(ep->techniques);
+}
+
+static inline struct ep_func *ep_getfunc(struct effect_parser *ep,
+		const char *name)
+{
+	size_t i;
+	for (i = 0; i < ep->funcs.num; i++) {
+		if (strcmp(name, ep->funcs.array[i].name) == 0)
+			return ep->funcs.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_struct *ep_getstruct(struct effect_parser *ep,
+		const char *name)
+{
+	size_t i;
+	for (i = 0; i < ep->structs.num; i++) {
+		if (strcmp(name, ep->structs.array[i].name) == 0)
+			return ep->structs.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_sampler *ep_getsampler(struct effect_parser *ep,
+		const char *name)
+{
+	size_t i;
+	for (i = 0; i < ep->samplers.num; i++) {
+		if (strcmp(name, ep->samplers.array[i].name) == 0)
+			return ep->samplers.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_param *ep_getparam(struct effect_parser *ep,
+		const char *name)
+{
+	size_t i;
+	for (i = 0; i < ep->params.num; i++) {
+		if (strcmp(name, ep->params.array[i].name) == 0)
+			return ep->params.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_func *ep_getfunc_strref(struct effect_parser *ep,
+		const struct strref *ref)
+{
+	size_t i;
+	for (i = 0; i < ep->funcs.num; i++) {
+		if (strref_cmp(ref, ep->funcs.array[i].name) == 0)
+			return ep->funcs.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_struct *ep_getstruct_strref(struct effect_parser *ep,
+		const struct strref *ref)
+{
+	size_t i;
+	for (i = 0; i < ep->structs.num; i++) {
+		if (strref_cmp(ref, ep->structs.array[i].name) == 0)
+			return ep->structs.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_sampler *ep_getsampler_strref(struct effect_parser *ep,
+		const struct strref *ref)
+{
+	size_t i;
+	for (i = 0; i < ep->samplers.num; i++) {
+		if (strref_cmp(ref, ep->samplers.array[i].name) == 0)
+			return ep->samplers.array+i;
+	}
+
+	return NULL;
+}
+
+static inline struct ep_param *ep_getparam_strref(struct effect_parser *ep,
+		const struct strref *ref)
+{
+	size_t i;
+	for (i = 0; i < ep->params.num; i++) {
+		if (strref_cmp(ref, ep->params.array[i].name) == 0)
+			return ep->params.array+i;
+	}
+
+	return NULL;
+}
+
+static inline int ep_parse_struct_var(struct effect_parser *ep,
+		struct ep_var *var)
+{
+	int errcode;
+
+	/* -------------------------------------- */
+	/* variable type */
+
+	if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+
+	if (token_is(&ep->cfp, ";")) return PARSE_CONTINUE;
+	if (token_is(&ep->cfp, "}")) return PARSE_BREAK;
+
+	errcode = token_is_type(&ep->cfp, CFTOKEN_NAME, "type name", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	copy_token(&ep->cfp, &var->type);
+
+	/* -------------------------------------- */
+	/* variable name */
+
+	if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+
+	if (token_is(&ep->cfp, ";")) return PARSE_UNEXPECTED_CONTINUE;
+	if (token_is(&ep->cfp, "}")) return PARSE_UNEXPECTED_BREAK;
+
+	errcode = token_is_type(&ep->cfp, CFTOKEN_NAME, "variable name", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	copy_token(&ep->cfp, &var->name);
+
+	/* -------------------------------------- */
+	/* variable mapping if any (POSITION, TEXCOORD, etc) */
+
+	if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+
+	if (token_is(&ep->cfp, ":")) {
+		if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+
+		if (token_is(&ep->cfp, ";")) return PARSE_UNEXPECTED_CONTINUE;
+		if (token_is(&ep->cfp, "}")) return PARSE_UNEXPECTED_BREAK;
+
+		errcode = token_is_type(&ep->cfp, CFTOKEN_NAME,
+				"mapping name", ";");
+		if (errcode != PARSE_SUCCESS)
+			return errcode;
+
+		copy_token(&ep->cfp, &var->mapping);
+
+		if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+	}
+
+	/* -------------------------------------- */
+
+	if (!token_is(&ep->cfp, ";")) {
+		if (!go_to_valid_token(&ep->cfp, ";", "}"))
+			return PARSE_EOF;
+		return PARSE_CONTINUE;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static void ep_parse_struct(struct effect_parser *ep)
+{
+	struct ep_struct eps;
+	ep_struct_init(&eps);
+
+	if (next_name(&ep->cfp, &eps.name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&ep->cfp, "{", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+
+	/* get structure variables */
+	while (true) {
+		bool do_break = false;
+		struct ep_var var;
+
+		ep_var_init(&var);
+
+		switch (ep_parse_struct_var(ep, &var)) {
+
+		case PARSE_UNEXPECTED_CONTINUE:
+			cf_adderror_syntax_error(&ep->cfp);
+		case PARSE_CONTINUE:
+			ep_var_free(&var);
+			continue;
+
+		case PARSE_UNEXPECTED_BREAK:
+			cf_adderror_syntax_error(&ep->cfp);
+		case PARSE_BREAK:
+			ep_var_free(&var);
+			do_break = true;
+			break;
+
+		case PARSE_EOF:
+			ep_var_free(&var);
+			goto error;
+		}
+
+		if (do_break)
+			break;
+
+		da_push_back(eps.vars, &var);
+	}
+
+	if (next_token_should_be(&ep->cfp, ";", NULL, NULL) != PARSE_SUCCESS)
+		goto error;
+
+	da_push_back(ep->structs, &eps);
+	return;
+
+error:
+	ep_struct_free(&eps);
+}
+
+static inline int ep_parse_pass_command_call(struct effect_parser *ep,
+		struct darray *call)
+{
+	struct cf_token end_token;
+	cf_token_clear(&end_token);
+
+	while (!token_is(&ep->cfp, ";")) {
+		if (token_is(&ep->cfp, "}")) {
+			cf_adderror_expecting(&ep->cfp, ";");
+			return PARSE_CONTINUE;
+		}
+
+		darray_push_back(sizeof(struct cf_token), call,
+				ep->cfp.cur_token);
+		if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+	}
+
+	darray_push_back(sizeof(struct cf_token), call, ep->cfp.cur_token);
+	darray_push_back(sizeof(struct cf_token), call, &end_token);
+	return PARSE_SUCCESS;
+}
+
+static int ep_parse_pass_command(struct effect_parser *ep, struct ep_pass *pass)
+{
+	struct darray *call; /* struct cf_token */
+
+	if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+
+	if (token_is(&ep->cfp, "vertex_shader") ||
+	    token_is(&ep->cfp, "vertex_program")) {
+		call = &pass->vertex_program.da;
+
+	} else if (token_is(&ep->cfp, "pixel_shader") ||
+	           token_is(&ep->cfp, "pixel_program")) {
+		call = &pass->fragment_program.da;
+
+	} else {
+		cf_adderror_syntax_error(&ep->cfp);
+		if (!go_to_valid_token(&ep->cfp, ";", "}")) return PARSE_EOF;
+		return PARSE_CONTINUE;
+	}
+
+	if (next_token_should_be(&ep->cfp, "=", ";", "}") != PARSE_SUCCESS)
+		return PARSE_CONTINUE;
+
+	if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+	if (token_is(&ep->cfp, "compile")) {
+		cf_adderror(&ep->cfp, "compile keyword not necessary",
+				LEVEL_WARNING, NULL, NULL, NULL);
+		if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+	}
+
+	return ep_parse_pass_command_call(ep, call);
+}
+
+static int ep_parse_pass(struct effect_parser *ep, struct ep_pass *pass)
+{
+	struct cf_token peek;
+
+	if (!token_is(&ep->cfp, "pass"))
+		return PARSE_UNEXPECTED_CONTINUE;
+
+	if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+
+	if (!token_is(&ep->cfp, "{")) {
+		pass->name = bstrdup_n(ep->cfp.cur_token->str.array,
+		                        ep->cfp.cur_token->str.len);
+		if (!next_valid_token(&ep->cfp)) return PARSE_EOF;
+	}
+
+	if (!peek_valid_token(&ep->cfp, &peek)) return PARSE_EOF;
+
+	while (strref_cmp(&peek.str, "}") != 0) {
+		int ret = ep_parse_pass_command(ep, pass);
+		if (ret < 0 && ret != PARSE_CONTINUE)
+			return ret;
+
+		if (!peek_valid_token(&ep->cfp, &peek))
+			return PARSE_EOF;
+	}
+
+	/* token is '}' */
+	next_token(&ep->cfp);
+
+	return PARSE_SUCCESS;
+}
+
+static void ep_parse_technique(struct effect_parser *ep)
+{
+	struct ep_technique ept;
+	ep_technique_init(&ept);
+
+	if (next_name(&ep->cfp, &ept.name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&ep->cfp, "{", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+
+	if (!next_valid_token(&ep->cfp))
+		goto error;
+
+	while (!token_is(&ep->cfp, "}")) {
+		struct ep_pass pass;
+		ep_pass_init(&pass);
+
+		switch (ep_parse_pass(ep, &pass)) {
+		case PARSE_UNEXPECTED_CONTINUE:
+			ep_pass_free(&pass);
+			if (!go_to_token(&ep->cfp, "}", NULL))
+				goto error;
+			continue;
+		case PARSE_EOF:
+			ep_pass_free(&pass);
+			goto error;
+		}
+
+		da_push_back(ept.passes, &pass);
+
+		if (!next_valid_token(&ep->cfp))
+			goto error;
+	}
+
+	/* pass the current token (which is '}') if we reached here */
+	next_token(&ep->cfp);
+
+	da_push_back(ep->techniques, &ept);
+	return;
+
+error:
+	next_token(&ep->cfp);
+	ep_technique_free(&ept);
+}
+
+static int ep_parse_sampler_state_item(struct effect_parser *ep,
+		struct ep_sampler *eps)
+{
+	int ret;
+	char *state = NULL, *value = NULL;
+
+	ret = next_name(&ep->cfp, &state, "state name", ";");
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	ret = next_token_should_be(&ep->cfp, "=", ";", NULL);
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	ret = next_name(&ep->cfp, &value, "value name", ";");
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	ret = next_token_should_be(&ep->cfp, ";", ";", NULL);
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	da_push_back(eps->states, &state);
+	da_push_back(eps->values, &value);
+	return ret;
+
+fail:
+	bfree(state);
+	bfree(value);
+	return ret;
+}
+
+static void ep_parse_sampler_state(struct effect_parser *ep)
+{
+	struct ep_sampler eps;
+	struct cf_token peek;
+	ep_sampler_init(&eps);
+
+	if (next_name(&ep->cfp, &eps.name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&ep->cfp, "{", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+
+	if (!peek_valid_token(&ep->cfp, &peek))
+		goto error;
+
+	while (strref_cmp(&peek.str, "}") != 0) {
+		int ret = ep_parse_sampler_state_item(ep, &eps);
+		if (ret == PARSE_EOF)
+			goto error;
+
+		if (!peek_valid_token(&ep->cfp, &peek))
+			goto error;
+	}
+
+	if (next_token_should_be(&ep->cfp, "}", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&ep->cfp, ";", NULL, NULL) != PARSE_SUCCESS)
+		goto error;
+
+	da_push_back(ep->samplers, &eps);
+	return;
+
+error:
+	ep_sampler_free(&eps);
+}
+
+static inline int ep_check_for_keyword(struct effect_parser *ep,
+		const char *keyword, bool *val)
+{
+	bool new_val = token_is(&ep->cfp, keyword);
+	if (new_val) {
+		if (!next_valid_token(&ep->cfp))
+			return PARSE_EOF;
+
+		if (new_val && *val)
+			cf_adderror(&ep->cfp, "'$1' keyword already specified",
+					LEVEL_WARNING, keyword, NULL, NULL);
+		*val = new_val;
+
+		return PARSE_CONTINUE;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static inline int ep_parse_func_param(struct effect_parser *ep,
+		struct ep_func *func, struct ep_var *var)
+{
+	int errcode;
+
+	if (!next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	errcode = ep_check_for_keyword(ep, "uniform", &var->uniform);
+	if (errcode == PARSE_EOF)
+		return PARSE_EOF;
+
+	errcode = get_name(&ep->cfp, &var->type, "type", ")");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	errcode = next_name(&ep->cfp, &var->name, "name", ")");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	if (!next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	if (token_is(&ep->cfp, ":")) {
+		errcode = next_name(&ep->cfp, &var->mapping,
+				"mapping specifier", ")");
+		if (errcode != PARSE_SUCCESS)
+			return errcode;
+
+		if (!next_valid_token(&ep->cfp))
+			return PARSE_EOF;
+	}
+
+	if (ep_getstruct(ep, var->type) != NULL)
+		da_push_back(func->struct_deps, &var->type);
+	else if (ep_getsampler(ep, var->type) != NULL)
+		da_push_back(func->sampler_deps, &var->type);
+
+	return PARSE_SUCCESS;
+}
+
+static bool ep_parse_func_params(struct effect_parser *ep, struct ep_func *func)
+{
+	struct cf_token peek;
+	int errcode;
+
+	cf_token_clear(&peek);
+
+	if (!peek_valid_token(&ep->cfp, &peek))
+		return false;
+
+	if (*peek.str.array == ')') {
+		next_token(&ep->cfp);
+		goto exit;
+	}
+
+	do {
+		struct ep_var var;
+		ep_var_init(&var);
+
+		if (!token_is(&ep->cfp, "(") && !token_is(&ep->cfp, ","))
+			cf_adderror_syntax_error(&ep->cfp);
+
+		errcode = ep_parse_func_param(ep, func, &var);
+		if (errcode != PARSE_SUCCESS) {
+			ep_var_free(&var);
+
+			if (errcode == PARSE_CONTINUE)
+				goto exit;
+			else if (errcode == PARSE_EOF)
+				return false;
+		}
+
+		da_push_back(func->param_vars, &var);
+	} while (!token_is(&ep->cfp, ")"));
+
+exit:
+	return true;
+}
+
+static inline bool ep_process_struct_dep(struct effect_parser *ep,
+		struct ep_func *func)
+{
+	struct ep_struct *val = ep_getstruct_strref(ep,
+			&ep->cfp.cur_token->str);
+	if (val)
+		da_push_back(func->struct_deps, &val->name);
+	return val != NULL;
+}
+
+static inline bool ep_process_func_dep(struct effect_parser *ep,
+		struct ep_func *func)
+{
+	struct ep_func *val = ep_getfunc_strref(ep,
+			&ep->cfp.cur_token->str);
+	if (val)
+		da_push_back(func->func_deps, &val->name);
+	return val != NULL;
+}
+
+static inline bool ep_process_sampler_dep(struct effect_parser *ep,
+		struct ep_func *func)
+{
+	struct ep_sampler *val = ep_getsampler_strref(ep,
+			&ep->cfp.cur_token->str);
+	if (val)
+		da_push_back(func->sampler_deps, &val->name);
+	return val != NULL;
+}
+
+static inline bool ep_process_param_dep(struct effect_parser *ep,
+		struct ep_func *func)
+{
+	struct ep_param *val = ep_getparam_strref(ep, &ep->cfp.cur_token->str);
+	if (val)
+		da_push_back(func->param_deps, &val->name);
+	return val != NULL;
+}
+
+static inline bool ep_parse_func_contents(struct effect_parser *ep,
+		struct ep_func *func)
+{
+	int braces = 1;
+
+	dstr_cat_strref(&func->contents, &ep->cfp.cur_token->str);
+
+	while (braces > 0) {
+		if ((ep->cfp.cur_token++)->type == CFTOKEN_NONE)
+			return false;
+
+		if (ep->cfp.cur_token->type == CFTOKEN_SPACETAB ||
+		    ep->cfp.cur_token->type == CFTOKEN_NEWLINE) {
+		} else if (token_is(&ep->cfp, "{")) {
+			braces++;
+		} else if (token_is(&ep->cfp, "}")) {
+			braces--;
+		} else if (ep_process_struct_dep(ep, func)  ||
+		           ep_process_func_dep(ep, func)    ||
+		           ep_process_sampler_dep(ep, func) ||
+		           ep_process_param_dep(ep, func)) {
+		}
+
+		dstr_cat_strref(&func->contents, &ep->cfp.cur_token->str);
+	}
+
+	return true;
+}
+
+static void ep_parse_function(struct effect_parser *ep,
+		char *type, char *name)
+{
+	struct ep_func func;
+	int errcode;
+
+	ep_func_init(&func, type, name);
+	if (ep_getstruct(ep, type))
+		da_push_back(func.struct_deps, &func.ret_type);
+
+	if (!ep_parse_func_params(ep, &func))
+		goto error;
+
+	if (!next_valid_token(&ep->cfp))
+		goto error;
+
+	/* if function is mapped to something, for example COLOR */
+	if (token_is(&ep->cfp, ":")) {
+		errcode = next_name(&ep->cfp, &func.mapping,
+				"mapping specifier", "{");
+		if (errcode == PARSE_EOF)
+			goto error;
+		else if (errcode != PARSE_CONTINUE) {
+			if (!next_valid_token(&ep->cfp))
+				goto error;
+		}
+	}
+
+	if (!token_is(&ep->cfp, "{")) {
+		cf_adderror_expecting(&ep->cfp, "{");
+		goto error;
+	}
+
+	if (!ep_parse_func_contents(ep, &func))
+		goto error;
+
+	/* it is established that the current token is '}' if we reach this */
+	next_token(&ep->cfp);
+
+	da_push_back(ep->funcs, &func);
+	return;
+
+error:
+	ep_func_free(&func);
+}
+
+/* parses "array[count]" */
+static bool ep_parse_param_array(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	if (!next_valid_token(&ep->cfp))
+		return false;
+
+	if (ep->cfp.cur_token->type != CFTOKEN_NUM ||
+	    !valid_int_str(ep->cfp.cur_token->str.array,
+		    ep->cfp.cur_token->str.len))
+		return false;
+
+	param->array_count = strtol(ep->cfp.cur_token->str.array, NULL, 10);
+
+	if (next_token_should_be(&ep->cfp, "]", ";", NULL) == PARSE_EOF)
+		return false;
+
+	if (!next_valid_token(&ep->cfp))
+		return false;
+
+	return true;
+}
+
+static inline int ep_parse_param_assign_texture(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	int errcode;
+	char *str;
+
+	if (!next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	errcode = token_is_type(&ep->cfp, CFTOKEN_STRING,
+			"texture path string", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	str = cf_literal_to_str(ep->cfp.cur_token->str.array,
+	                        ep->cfp.cur_token->str.len);
+
+	if (str) {
+		da_copy_array(param->default_val, str, strlen(str) + 1);
+		bfree(str);
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static inline int ep_parse_param_assign_intfloat(struct effect_parser *ep,
+		struct ep_param *param, bool is_float)
+{
+	int errcode;
+
+	if (!next_valid_token(&ep->cfp))
+		return PARSE_EOF;
+
+	errcode = token_is_type(&ep->cfp, CFTOKEN_NUM, "numeric value", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	if (is_float) {
+		float f = (float)strtod(ep->cfp.cur_token->str.array, NULL);
+		da_push_back_array(param->default_val, &f, sizeof(float));
+	} else {
+		long l = strtol(ep->cfp.cur_token->str.array, NULL, 10);
+		da_push_back_array(param->default_val, &l, sizeof(long));
+	}
+
+	return PARSE_SUCCESS;
+}
+
+/*
+ * parses assignment for float1, float2, float3, float4, and any combination
+ * for float3x3, float4x4, etc
+ */
+static inline int ep_parse_param_assign_float_array(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	const char *float_type = param->type+5;
+	int float_count = 0, errcode, i;
+
+	/* -------------------------------------------- */
+
+	if (float_type[0] < '1' || float_type[0] > '4')
+		cf_adderror(&ep->cfp, "Invalid row count", LEVEL_ERROR,
+				NULL, NULL, NULL);
+
+	float_count = float_type[0]-'0';
+
+	if (float_type[1] == 'x') {
+		if (float_type[2] < '1' || float_type[2] > '4')
+			cf_adderror(&ep->cfp, "Invalid column count",
+					LEVEL_ERROR, NULL, NULL, NULL);
+
+		float_count *= float_type[2]-'0';
+	}
+
+	/* -------------------------------------------- */
+
+	errcode = next_token_should_be(&ep->cfp, "{", ";", NULL);
+	if (errcode != PARSE_SUCCESS) return errcode;
+
+	for (i = 0; i < float_count; i++) {
+		char *next = ((i+1) < float_count) ? "," : "}";
+
+		errcode = ep_parse_param_assign_intfloat(ep, param, true);
+		if (errcode != PARSE_SUCCESS) return errcode;
+
+		errcode = next_token_should_be(&ep->cfp, next, ";", NULL);
+		if (errcode != PARSE_SUCCESS) return errcode;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static int ep_parse_param_assignment_val(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	if (param->is_texture)
+		return ep_parse_param_assign_texture(ep, param);
+	else if (strcmp(param->type, "int") == 0)
+		return ep_parse_param_assign_intfloat(ep, param, false);
+	else if (strcmp(param->type, "float") == 0)
+		return ep_parse_param_assign_intfloat(ep, param, true);
+	else if (astrcmp_n(param->type, "float", 5) == 0)
+		return ep_parse_param_assign_float_array(ep, param);
+
+	cf_adderror(&ep->cfp, "Invalid type '$1' used for assignment",
+			LEVEL_ERROR, param->type, NULL, NULL);
+
+	return PARSE_CONTINUE;
+}
+
+static inline bool ep_parse_param_assignment(struct effect_parser *ep,
+		struct ep_param *param)
+{
+	if (ep_parse_param_assignment_val(ep, param) != PARSE_SUCCESS)
+		return false;
+
+	if (!next_valid_token(&ep->cfp))
+		return false;
+
+	return true;
+}
+
+/* static bool ep_parse_param_property(struct effect_parser *ep,
+		struct ep_param *param)
+{
+} */
+
+static void ep_parse_param(struct effect_parser *ep,
+		char *type, char *name,
+		bool is_property, bool is_const, bool is_uniform)
+{
+	struct ep_param param;
+	ep_param_init(&param, type, name, is_property, is_const);
+
+	if (token_is(&ep->cfp, ";"))
+		goto complete;
+	if (token_is(&ep->cfp, "[") && !ep_parse_param_array(ep, &param))
+		goto error;
+	if (token_is(&ep->cfp, "=") && !ep_parse_param_assignment(ep, &param))
+		goto error;
+	/*if (token_is(&ep->cfp, "<") && !ep_parse_param_property(ep, &param))
+		goto error; */
+	if (!token_is(&ep->cfp, ";"))
+		goto error;
+
+complete:
+	da_push_back(ep->params, &param);
+	return;
+
+error:
+	ep_param_free(&param);
+}
+
+static bool ep_get_var_specifiers(struct effect_parser *ep,
+		bool *is_property, bool *is_const, bool *is_uniform)
+{
+	while(true) {
+		int errcode;
+		errcode = ep_check_for_keyword(ep, "property", is_property);
+		if (errcode == PARSE_EOF)
+			return false;
+		else if (errcode == PARSE_CONTINUE)
+			continue;
+
+		errcode = ep_check_for_keyword(ep, "const", is_const);
+		if (errcode == PARSE_EOF)
+			return false;
+		else if (errcode == PARSE_CONTINUE)
+			continue;
+
+		errcode = ep_check_for_keyword(ep, "uniform", is_uniform);
+		if (errcode == PARSE_EOF)
+			return false;
+		else if (errcode == PARSE_CONTINUE)
+			continue;
+
+		break;
+	}
+
+	return true;
+}
+
+static inline void report_invalid_func_keyword(struct effect_parser *ep,
+		const char *name, bool val)
+{
+	if (val)
+		cf_adderror(&ep->cfp, "'$1' keyword cannot be used with a "
+		                      "function", LEVEL_ERROR,
+		                      name, NULL, NULL);
+}
+
+static void ep_parse_other(struct effect_parser *ep)
+{
+	bool is_property = false, is_const = false, is_uniform = false;
+	char *type = NULL, *name = NULL;
+
+	if (!ep_get_var_specifiers(ep, &is_property, &is_const, &is_uniform))
+		goto error;
+
+	if (get_name(&ep->cfp, &type, "type", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_name(&ep->cfp, &name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+
+	if (!next_valid_token(&ep->cfp))
+		goto error;
+
+	if (token_is(&ep->cfp, "(")) {
+		report_invalid_func_keyword(ep, "property", is_property);
+		report_invalid_func_keyword(ep, "const",    is_const);
+		report_invalid_func_keyword(ep, "uniform",  is_uniform);
+
+		ep_parse_function(ep, type, name);
+		return;
+	} else {
+		ep_parse_param(ep, type, name, is_property, is_const,
+				is_uniform);
+		return;
+	}
+
+error:
+	bfree(type);
+	bfree(name);
+}
+
+static bool ep_compile(struct effect_parser *ep);
+
+bool ep_parse(struct effect_parser *ep, effect_t effect,
+              const char *effect_string, const char *file)
+{
+	bool success;
+
+	ep->effect = effect;
+	if (!cf_parser_parse(&ep->cfp, effect_string, file))
+		return false;
+
+	while (ep->cfp.cur_token && ep->cfp.cur_token->type != CFTOKEN_NONE) {
+		if (token_is(&ep->cfp, ";") ||
+		    is_whitespace(*ep->cfp.cur_token->str.array)) {
+			/* do nothing */
+			ep->cfp.cur_token++;
+
+		} else if (token_is(&ep->cfp, "struct")) {
+			ep_parse_struct(ep);
+
+		} else if (token_is(&ep->cfp, "technique")) {
+			ep_parse_technique(ep);
+
+		} else if (token_is(&ep->cfp, "sampler_state")) {
+			ep_parse_sampler_state(ep);
+
+		} else if (token_is(&ep->cfp, "{")) {
+			/* add error and pass braces */
+			cf_adderror(&ep->cfp, "Unexpected code segment",
+					LEVEL_ERROR, NULL, NULL, NULL);
+			pass_pair(&ep->cfp, '{', '}');
+
+		} else {
+			/* parameters and functions */
+			ep_parse_other(ep);
+		}
+	}
+
+	success = !error_data_has_errors(&ep->cfp.error_list);
+	if (success)
+		success = ep_compile(ep);
+
+	return success;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline void ep_write_param(struct dstr *shader, struct ep_param *param,
+		struct darray *used_params)
+{
+	if (param->written)
+		return;
+
+	if (param->is_const) {
+		dstr_cat(shader, "const ");
+	} else {
+		struct dstr new;
+		dstr_init_copy(&new, param->name);
+		darray_push_back(sizeof(struct dstr), used_params, &new);
+
+		dstr_cat(shader, "uniform ");
+	}
+
+	dstr_cat(shader, param->type);
+	dstr_cat(shader, " ");
+	dstr_cat(shader, param->name);
+
+	if (param->array_count)
+		dstr_catf(shader, "[%u]", param->array_count);
+
+	dstr_cat(shader, ";\n");
+
+	param->written = true;
+}
+
+static inline void ep_write_func_param_deps(struct effect_parser *ep,
+		struct dstr *shader, struct ep_func *func,
+		struct darray *used_params)
+{
+	size_t i;
+	for (i = 0; i < func->param_deps.num; i++) {
+		const char *name = func->param_deps.array[i];
+		struct ep_param *param = ep_getparam(ep, name);
+		ep_write_param(shader, param, used_params);
+	}
+
+	if (func->param_deps.num)
+		dstr_cat(shader, "\n\n");
+}
+
+static void ep_write_sampler(struct dstr *shader, struct ep_sampler *sampler)
+{
+	size_t i;
+
+	if (sampler->written)
+		return;
+
+	dstr_cat(shader, "sampler_state ");
+	dstr_cat(shader, sampler->name);
+	dstr_cat(shader, " {");
+
+	for (i = 0; i <sampler->values.num; i++) {
+		dstr_cat(shader, "\n\t");
+		dstr_cat(shader, sampler->states.array[i]);
+		dstr_cat(shader, " = ");
+		dstr_cat(shader, sampler->values.array[i]);
+		dstr_cat(shader, ";\n");
+	}
+
+	dstr_cat(shader, "\n};\n");
+	sampler->written = true;
+}
+
+static inline void ep_write_func_sampler_deps(struct effect_parser *ep,
+		struct dstr *shader, struct ep_func *func)
+{
+	size_t i;
+	for (i = 0; i < func->sampler_deps.num; i++) {
+		const char *name = func->sampler_deps.array[i];
+
+		struct ep_sampler *sampler = ep_getsampler(ep, name);
+		ep_write_sampler(shader, sampler);
+		dstr_cat(shader, "\n");
+	}
+}
+
+static inline void ep_write_var(struct dstr *shader, struct ep_var *var)
+{
+	if (var->uniform)
+		dstr_cat(shader, "uniform ");
+
+	dstr_cat(shader, var->type);
+	dstr_cat(shader, " ");
+	dstr_cat(shader, var->name);
+
+	if (var->mapping) {
+		dstr_cat(shader, " : ");
+		dstr_cat(shader, var->mapping);
+	}
+}
+
+static void ep_write_struct(struct dstr *shader, struct ep_struct *st)
+{
+	size_t i;
+
+	if (st->written)
+		return;
+
+	dstr_cat(shader, "struct ");
+	dstr_cat(shader, st->name);
+	dstr_cat(shader, " {");
+
+	for (i = 0; i < st->vars.num; i++) {
+		dstr_cat(shader, "\n\t");
+		ep_write_var(shader, st->vars.array+i);
+		dstr_cat(shader, ";");
+	}
+
+	dstr_cat(shader, "\n};\n");
+	st->written = true;
+}
+
+static inline void ep_write_func_struct_deps(struct effect_parser *ep,
+		struct dstr *shader, struct ep_func *func)
+{
+	size_t i;
+	for (i = 0; i < func->struct_deps.num; i++) {
+		const char *name = func->struct_deps.array[i];
+		struct ep_struct *st = ep_getstruct(ep, name);
+
+		if (!st->written) {
+			ep_write_struct(shader, st);
+			dstr_cat(shader, "\n");
+			st->written = true;
+		}
+	}
+}
+
+static void ep_write_func(struct effect_parser *ep, struct dstr *shader,
+		struct ep_func *func, struct darray *used_params);
+
+static inline void ep_write_func_func_deps(struct effect_parser *ep,
+		struct dstr *shader, struct ep_func *func,
+		struct darray *used_params)
+{
+	size_t i;
+	for (i = 0; i < func->func_deps.num; i++) {
+		const char *name = func->func_deps.array[i];
+		struct ep_func *func = ep_getfunc(ep, name);
+
+		if (!func->written) {
+			ep_write_func(ep, shader, func, used_params);
+			dstr_cat(shader, "\n\n");
+		}
+	}
+}
+
+static void ep_write_func(struct effect_parser *ep, struct dstr *shader,
+		struct ep_func *func, struct darray *used_params)
+{
+	size_t i;
+
+	func->written = true;
+	
+	ep_write_func_param_deps(ep, shader, func, used_params);
+	ep_write_func_sampler_deps(ep, shader, func);
+	ep_write_func_struct_deps(ep, shader, func);
+	ep_write_func_func_deps(ep, shader, func, used_params);
+
+	/* ------------------------------------ */
+
+	dstr_cat(shader, func->ret_type);
+	dstr_cat(shader, " ");
+	dstr_cat(shader, func->name);
+	dstr_cat(shader, "(");
+
+	for (i = 0; i < func->param_vars.num; i++) {
+		struct ep_var *var = func->param_vars.array+i;
+
+		if (i)
+			dstr_cat(shader, ", ");
+		ep_write_var(shader, var);
+	}
+
+	dstr_cat(shader, ")\n");
+	dstr_cat_dstr(shader, &func->contents);
+	dstr_cat(shader, "\n");
+}
+
+/* writes mapped vars used by the call as parameters for main */
+static void ep_write_main_params(struct effect_parser *ep,
+		struct dstr *shader, struct dstr *param_str,
+		struct ep_func *func)
+{
+	size_t i;
+	bool empty_params = dstr_isempty(param_str);
+
+	for (i = 0; i < func->param_vars.num; i++) {
+		struct ep_var *var = func->param_vars.array+i;
+		struct ep_struct *st = NULL;
+		bool mapped = (var->mapping != NULL);
+
+		if (!mapped) {
+			st = ep_getstruct(ep, var->type);
+			if (st)
+				mapped = ep_struct_mapped(st);
+		}
+
+		if (mapped) {
+			dstr_cat(shader, var->type);
+			dstr_cat(shader, " ");
+			dstr_cat(shader, var->name);
+
+			if (!st) {
+				dstr_cat(shader, " : ");
+				dstr_cat(shader, var->mapping);
+			}
+
+			if (!dstr_isempty(param_str))
+				dstr_cat(param_str, ", ");
+			dstr_cat(param_str, var->name);
+		}
+	}
+
+	if (!empty_params)
+		dstr_cat(param_str, ", ");
+}
+
+static void ep_write_main(struct effect_parser *ep, struct dstr *shader,
+		struct ep_func *func, struct dstr *call_str)
+{
+	struct dstr param_str;
+	struct dstr adjusted_call;
+
+	dstr_init(&param_str);
+	dstr_init_copy_dstr(&adjusted_call, call_str);
+
+	dstr_cat(shader, "\n");
+	dstr_cat(shader, func->ret_type);
+	dstr_cat(shader, " main(");
+
+	ep_write_main_params(ep, shader, &param_str, func);
+
+	dstr_cat(shader, ")");
+	if (func->mapping) {
+		dstr_cat(shader, " : ");
+		dstr_cat(shader, func->mapping);
+	}
+
+	dstr_cat(shader, "\n{\n\treturn ");
+	dstr_cat_dstr(shader, &adjusted_call);
+	dstr_cat(shader, "\n}\n");
+
+	dstr_free(&adjusted_call);
+	dstr_free(&param_str);
+}
+
+static inline void ep_reset_written(struct effect_parser *ep)
+{
+	size_t i;
+	for (i = 0; i <ep->params.num; i++)
+		ep->params.array[i].written = false;
+	for (i = 0; i <ep->structs.num; i++)
+		ep->structs.array[i].written = false;
+	for (i = 0; i <ep->funcs.num; i++)
+		ep->funcs.array[i].written = false;
+	for (i = 0; i <ep->samplers.num; i++)
+		ep->samplers.array[i].written = false;
+}
+
+static void ep_makeshaderstring(struct effect_parser *ep,
+		struct dstr *shader, struct darray *shader_call,
+		struct darray *used_params)
+{
+	struct cf_token *token = shader_call->array;
+	struct cf_token *func_name;
+	struct ep_func *func;
+	struct dstr call_str;
+
+	dstr_init(&call_str);
+
+	while (token->type != CFTOKEN_NONE && is_whitespace(*token->str.array))
+		token++;
+
+	if (token->type == CFTOKEN_NONE ||
+	    strref_cmp(&token->str, "NULL") == 0)
+		return;
+
+	func_name = token;
+
+	while (token->type != CFTOKEN_NONE) {
+		struct ep_param *param = ep_getparam_strref(ep, &token->str);
+		if (param)
+			ep_write_param(shader, param, used_params);
+
+		dstr_cat_strref(&call_str, &token->str);
+		token++;
+	}
+
+	func = ep_getfunc_strref(ep, &func_name->str);
+	if (!func)
+		return;
+
+	ep_write_func(ep, shader, func, used_params);
+	ep_write_main(ep, shader, func, &call_str);
+
+	dstr_free(&call_str);
+
+	ep_reset_written(ep);
+}
+
+static void ep_compile_param(struct effect_parser *ep, size_t idx)
+{
+	struct effect_param *param;
+	struct ep_param *param_in;
+
+	param = ep->effect->params.array+idx;
+	param_in = ep->params.array+idx;
+	param_in->param = param;
+
+	param->name = bstrdup(param_in->name);
+	param->section = EFFECT_PARAM;
+	da_move(param->default_val, param_in->default_val);
+
+	if (strcmp(param_in->type, "bool") == 0)
+		param->type = SHADER_PARAM_BOOL;
+	else if (strcmp(param_in->type, "float") == 0)
+		param->type = SHADER_PARAM_FLOAT;
+	else if (strcmp(param_in->type, "int") == 0)
+		param->type = SHADER_PARAM_INT;
+	else if (strcmp(param_in->type, "float2") == 0)
+		param->type = SHADER_PARAM_VEC2;
+	else if (strcmp(param_in->type, "float3") == 0)
+		param->type = SHADER_PARAM_VEC3;
+	else if (strcmp(param_in->type, "float4") == 0)
+		param->type = SHADER_PARAM_VEC4;
+	else if (strcmp(param_in->type, "float3x3") == 0)
+		param->type = SHADER_PARAM_MATRIX3X3;
+	else if (strcmp(param_in->type, "float4x4") == 0)
+		param->type = SHADER_PARAM_MATRIX4X4;
+	else if (param_in->is_texture)
+		param->type = SHADER_PARAM_TEXTURE;
+
+	if (strcmp(param_in->name, "ViewProj") == 0)
+		ep->effect->view_proj = param;
+	else if (strcmp(param_in->name, "World") == 0)
+		ep->effect->world = param;
+}
+
+static inline void ep_compile_pass_shaderparams(struct effect_parser *ep,
+		struct darray *pass_params, struct darray *used_params,
+		shader_t shader)
+{
+	size_t i;
+	darray_resize(sizeof(struct pass_shaderparam), pass_params,
+			used_params->num);
+
+	for (i = 0; i < pass_params->num; i++) {
+		struct dstr *param_name;
+		struct pass_shaderparam *param;
+
+		param_name = darray_item(sizeof(struct dstr), used_params, i);
+		param = darray_item(sizeof(struct pass_shaderparam),
+				pass_params, i);
+
+		param->eparam = effect_getparambyname(ep->effect,
+				param_name->array);
+		param->sparam = shader_getparambyname(shader,
+				param_name->array);
+	}
+}
+
+static inline bool ep_compile_pass_shader(struct effect_parser *ep,
+		struct effect_technique *tech,
+		struct effect_pass *pass, struct ep_pass *pass_in,
+		size_t pass_idx, enum shader_type type)
+{
+	struct dstr shader_str;
+	struct dstr location;
+	struct darray used_params; /* struct dstr */
+	struct darray *pass_params; /* struct pass_shaderparam */
+	shader_t shader;
+
+	dstr_init(&shader_str);
+	darray_init(&used_params);
+	dstr_init(&location);
+
+	dstr_copy(&location, ep->cfp.lex.file);
+	if (type == SHADER_VERTEX)
+		dstr_cat(&location, " (Vertex ");
+	else if (type == SHADER_PIXEL)
+		dstr_cat(&location, " (Pixel ");
+	else if (type == SHADER_GEOMETRY)
+		dstr_cat(&location, " (Geometry ");
+
+	dstr_catf(&location, "shader, technique %s, pass %u)", tech->name,
+			pass_idx);
+
+	if (type == SHADER_VERTEX) {
+		ep_makeshaderstring(ep, &shader_str,
+				&pass_in->vertex_program.da, &used_params);
+
+		pass->vertshader = gs_create_vertexshader(shader_str.array,
+				location.array, NULL);
+
+		shader = pass->vertshader;
+		pass_params = &pass->vertshader_params.da;
+	} else if (type == SHADER_PIXEL) {
+		ep_makeshaderstring(ep, &shader_str,
+				&pass_in->fragment_program.da, &used_params);
+
+		pass->pixelshader = gs_create_pixelshader(shader_str.array,
+				location.array, NULL);
+
+		shader = pass->pixelshader;
+		pass_params = &pass->pixelshader_params.da;
+	}
+
+	blog(LOG_DEBUG, "+++++++++++++++++++++++++++++++++++");
+	blog(LOG_DEBUG, "  %s", location.array);
+	blog(LOG_DEBUG, "-----------------------------------");
+	blog(LOG_DEBUG, "%s", shader_str.array);
+	blog(LOG_DEBUG, "+++++++++++++++++++++++++++++++++++");
+
+	if (shader)
+		ep_compile_pass_shaderparams(ep, pass_params, &used_params,
+				shader);
+
+	dstr_free(&location);
+	dstr_array_free(used_params.array, used_params.num);
+	darray_free(&used_params);
+	dstr_free(&shader_str);
+
+	return shader != NULL;
+}
+
+static bool ep_compile_pass(struct effect_parser *ep,
+		struct effect_technique *tech,
+		struct ep_technique *tech_in,
+		size_t idx)
+{
+	struct effect_pass *pass;
+	struct ep_pass *pass_in;
+	bool success = true;
+
+	pass = tech->passes.array+idx;
+	pass_in = tech_in->passes.array+idx;
+
+	pass->name = bstrdup(pass_in->name);
+	pass->section = EFFECT_PASS;
+
+	if (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx,
+				SHADER_VERTEX))
+		success = false;
+
+	if (!ep_compile_pass_shader(ep, tech, pass, pass_in, idx,
+				SHADER_PIXEL))
+		success = false;
+
+	return success;
+}
+
+static inline bool ep_compile_technique(struct effect_parser *ep, size_t idx)
+{
+	struct effect_technique *tech;
+	struct ep_technique *tech_in;
+	bool success = true;
+	size_t i;
+
+	tech = ep->effect->techniques.array+idx;
+	tech_in = ep->techniques.array+idx;
+
+	tech->name = bstrdup(tech_in->name);
+	tech->section = EFFECT_TECHNIQUE;
+	tech->effect = ep->effect;
+
+	da_resize(tech->passes, tech_in->passes.num);
+
+	for (i = 0; i < tech->passes.num; i++) {
+		if (!ep_compile_pass(ep, tech, tech_in, i))
+			success = false;
+	}
+
+	return success;
+}
+
+static bool ep_compile(struct effect_parser *ep)
+{
+	bool success = true;
+	size_t i;
+
+	assert(ep->effect);
+
+	da_resize(ep->effect->params, ep->params.num);
+	da_resize(ep->effect->techniques, ep->techniques.num);
+
+	for (i = 0; i < ep->params.num; i++)
+		ep_compile_param(ep, i);
+	for (i = 0; i < ep->techniques.num; i++) {
+		if (!ep_compile_technique(ep, i))
+			success = false;
+	}
+
+	return success;
+}

+ 285 - 0
libobs/graphics/effect-parser.h

@@ -0,0 +1,285 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef EFFECT_PARSER_H
+#define EFFECT_PARSER_H
+
+#include "../util/darray.h"
+#include "../util/cf-parser.h"
+#include "graphics.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct dstr;
+
+/*
+ * The effect parser takes an effect file and converts it into individual
+ * shaders for each technique's pass.  It automatically writes all dependent
+ * structures/functions/parameters to the shader and builds shader text for
+ * each shader component of each pass.
+ */
+
+/* ------------------------------------------------------------------------- */
+/* effect parser var data */
+
+struct ep_var {
+	char *type, *name, *mapping;
+	bool uniform;
+};
+
+static inline void ep_var_init(struct ep_var *epv)
+{
+	memset(epv, 0, sizeof(struct ep_var));
+}
+
+static inline void ep_var_free(struct ep_var *epv)
+{
+	bfree(epv->type);
+	bfree(epv->name);
+	bfree(epv->mapping);
+}
+
+/* ------------------------------------------------------------------------- */
+/* effect parser param data */
+
+struct ep_param {
+	char             *type, *name;
+	DARRAY(uint8_t)  default_val;
+	DARRAY(char*)    properties;
+	struct effect_param *param;
+	bool is_const, is_property, is_texture, written;
+	int writeorder, array_count;
+};
+
+extern void ep_param_writevar(struct dstr *dst, struct darray *use_params);
+
+static inline void ep_param_init(struct ep_param *epp,
+		char *type, char *name,
+		bool is_property, bool is_const)
+{
+	epp->type        = type;
+	epp->name        = name;
+	epp->is_property = is_property;
+	epp->is_const    = is_const;
+	epp->is_texture  = (astrcmp_n(epp->type, "texture", 7) == 0);
+	epp->written     = false;
+	epp->writeorder  = false;
+	epp->array_count = 0;
+	da_init(epp->default_val);
+	da_init(epp->properties);
+}
+
+static inline void ep_param_free(struct ep_param *epp)
+{
+	bfree(epp->type);
+	bfree(epp->name);
+	da_free(epp->default_val);
+	da_free(epp->properties);
+}
+
+/* ------------------------------------------------------------------------- */
+/* effect parser struct data */
+
+struct ep_struct {
+	char *name;
+	DARRAY(struct ep_var) vars; /* struct ep_var */
+	bool written;
+};
+
+static inline bool ep_struct_mapped(struct ep_struct *eps)
+{
+	if (eps->vars.num > 0)
+		return eps->vars.array[0].mapping != NULL;
+
+	return false;
+}
+
+static inline void ep_struct_init(struct ep_struct *eps)
+{
+	memset(eps, 0, sizeof(struct ep_struct));
+}
+
+static inline void ep_struct_free(struct ep_struct *eps)
+{
+	size_t i;
+
+	bfree(eps->name);
+	for (i = 0; i < eps->vars.num; i++)
+		ep_var_free(eps->vars.array+i);
+	da_free(eps->vars);
+}
+
+/* ------------------------------------------------------------------------- */
+/* effect parser sampler data */
+
+struct ep_sampler {
+	char *name;
+	DARRAY(char*) states;
+	DARRAY(char*) values;
+
+	bool written;
+};
+
+static inline void ep_sampler_init(struct ep_sampler *eps)
+{
+	memset(eps, 0, sizeof(struct ep_sampler));
+}
+
+static inline void ep_sampler_free(struct ep_sampler *eps)
+{
+	size_t i;
+
+	for (i = 0; i < eps->states.num; i++)
+		bfree(eps->states.array[i]);
+	for (i = 0; i < eps->values.num; i++)
+		bfree(eps->values.array[i]);
+
+	bfree(eps->name);
+	da_free(eps->states);
+	da_free(eps->values);
+}
+
+/* ------------------------------------------------------------------------- */
+/* effect parser pass data */
+
+struct ep_pass {
+	char *name;
+	DARRAY(struct cf_token) vertex_program;
+	DARRAY(struct cf_token) fragment_program;
+	struct effect_pass *pass;
+};
+
+static inline void ep_pass_init(struct ep_pass *epp)
+{
+	memset(epp, 0, sizeof(struct ep_pass));
+}
+
+static inline void ep_pass_free(struct ep_pass *epp)
+{
+	bfree(epp->name);
+	da_free(epp->vertex_program);
+	da_free(epp->fragment_program);
+}
+
+/* ------------------------------------------------------------------------- */
+/* effect parser technique data */
+
+struct ep_technique {
+	char *name;
+	DARRAY(struct ep_pass) passes; /* struct ep_pass */
+};
+
+static inline void ep_technique_init(struct ep_technique *ept)
+{
+	memset(ept, 0, sizeof(struct ep_technique));
+}
+
+static inline void ep_technique_free(struct ep_technique *ept)
+{
+	size_t i;
+
+	for (i = 0; i < ept->passes.num; i++)
+		ep_pass_free(ept->passes.array+i);
+
+	bfree(ept->name);
+	da_free(ept->passes);
+}
+
+/* ------------------------------------------------------------------------- */
+/* effect parser function data */
+
+struct ep_func {
+	char *name, *ret_type, *mapping;
+	struct dstr contents;
+	DARRAY(struct ep_var) param_vars;
+	DARRAY(const char*) func_deps;
+	DARRAY(const char*) struct_deps;
+	DARRAY(const char*) param_deps;
+	DARRAY(const char*) sampler_deps;
+	bool written;
+};
+
+static inline void ep_func_init(struct ep_func *epf, char *ret_type,
+		char *name)
+{
+	memset(epf, 0, sizeof(struct ep_func));
+	epf->name     = name;
+	epf->ret_type = ret_type;
+}
+
+static inline void ep_func_free(struct ep_func *epf)
+{
+	size_t i;
+	for (i = 0; i < epf->param_vars.num; i++)
+		ep_var_free(epf->param_vars.array+i);
+
+	bfree(epf->name);
+	bfree(epf->ret_type);
+	bfree(epf->mapping);
+	dstr_free(&epf->contents);
+	da_free(epf->param_vars);
+	da_free(epf->func_deps);
+	da_free(epf->struct_deps);
+	da_free(epf->param_deps);
+	da_free(epf->sampler_deps);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct effect_parser {
+	effect_t effect;
+
+	DARRAY(struct ep_param)     params;
+	DARRAY(struct ep_struct)    structs;
+	DARRAY(struct ep_func)      funcs;
+	DARRAY(struct ep_sampler)   samplers;
+	DARRAY(struct ep_technique) techniques;
+
+	/* internal vars */
+	DARRAY(struct cf_lexer) files;
+	DARRAY(struct cf_token) tokens;
+	struct effect_pass *cur_pass;
+
+	struct cf_parser cfp;
+};
+
+static inline void ep_init(struct effect_parser *ep)
+{
+	da_init(ep->params);
+	da_init(ep->structs);
+	da_init(ep->funcs);
+	da_init(ep->samplers);
+	da_init(ep->techniques);
+	da_init(ep->files);
+	da_init(ep->tokens);
+
+	ep->cur_pass = NULL;
+	cf_parser_init(&ep->cfp);
+}
+
+extern void ep_free(struct effect_parser *ep);
+
+extern bool ep_parse(struct effect_parser *ep, effect_t effect,
+                     const char *effect_string, const char *file);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 313 - 0
libobs/graphics/effect.c

@@ -0,0 +1,313 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "effect.h"
+#include "graphics-internal.h"
+#include "vec2.h"
+#include "vec3.h"
+#include "vec4.h"
+
+void effect_destroy(effect_t effect)
+{
+	if (effect) {
+		effect_free(effect);
+		bfree(effect);
+	}
+}
+
+technique_t effect_gettechnique(effect_t effect, const char *name)
+{
+	size_t i;
+	struct effect_technique *array = effect->techniques.array;
+
+	for (i = 0; i < effect->techniques.num; i++) {
+		struct effect_technique *tech = array+i;
+		if (strcmp(tech->name, name) == 0)
+			return tech;
+	}
+
+	return NULL;
+}
+
+int technique_begin(technique_t tech)
+{
+	tech->effect->cur_technique = tech;
+	tech->effect->graphics->cur_effect = tech->effect;
+
+	return (int)tech->passes.num;
+}
+
+void technique_end(technique_t tech)
+{
+	struct gs_effect *effect = tech->effect;
+	struct effect_param *params = effect->params.array;
+	size_t i;
+
+	gs_load_vertexshader(NULL);
+	gs_load_pixelshader(NULL);
+
+	tech->effect->cur_technique = NULL;
+	tech->effect->graphics->cur_effect = NULL;
+
+	for (i = 0; i < effect->params.num; i++) {
+		struct effect_param *param = params+i;
+
+		da_free(param->cur_val);
+		param->changed = false;
+	}
+}
+
+static inline void reset_params(struct darray *shaderparams)
+{
+	struct pass_shaderparam *params = shaderparams->array;
+	size_t i;
+
+	for (i = 0; i < shaderparams->num; i++)
+		params[i].eparam->changed = false;
+}
+
+static void upload_shader_params(shader_t shader, struct darray *pass_params,
+		bool changed_only)
+{
+	struct pass_shaderparam *params = pass_params->array;
+	size_t i;
+
+	for (i = 0; i < pass_params->num; i++) {
+		struct pass_shaderparam *param = params+i;
+		struct effect_param *eparam = param->eparam;
+		sparam_t sparam = param->sparam;
+
+		if (changed_only && !eparam->changed)
+			continue;
+
+		if (!eparam->cur_val.num) {
+			if (eparam->default_val.num)
+				da_copy(eparam->cur_val, eparam->default_val);
+			else
+				continue;
+		}
+
+		shader_setval(shader, sparam, eparam->cur_val.array,
+				eparam->cur_val.num);
+	}
+}
+
+static inline void upload_parameters(struct gs_effect *effect,
+		bool changed_only)
+{
+	struct darray *vshader_params, *pshader_params;
+
+	if (!effect->cur_pass)
+		return;
+
+	vshader_params = &effect->cur_pass->vertshader_params.da;
+	pshader_params = &effect->cur_pass->pixelshader_params.da;
+
+	upload_shader_params(effect->cur_pass->vertshader, vshader_params,
+			changed_only);
+	upload_shader_params(effect->cur_pass->pixelshader, pshader_params,
+			changed_only);
+	reset_params(vshader_params);
+	reset_params(pshader_params);
+}
+
+void effect_updateparams(effect_t effect)	
+{
+	upload_parameters(effect, true);
+}
+
+bool technique_beginpass(technique_t tech, size_t idx)
+{
+	struct effect_pass *passes;
+	struct effect_pass *cur_pass;
+
+	if (idx >= tech->passes.num)
+		return false;
+
+	passes = tech->passes.array;
+	cur_pass = passes+idx;
+
+	tech->effect->cur_pass = cur_pass;
+	gs_load_vertexshader(cur_pass->vertshader);
+	gs_load_pixelshader(cur_pass->pixelshader);
+	upload_parameters(tech->effect, false);
+
+	return true;
+}
+
+bool technique_beginpassbyname(technique_t tech,
+		const char *name)
+{
+	size_t i;
+	for (i = 0; i < tech->passes.num; i++) {
+		struct effect_pass *pass = tech->passes.array+i;
+		if (strcmp(pass->name, name) == 0) {
+			technique_beginpass(tech, (int)i);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static inline void clear_tex_params(shader_t shader, struct darray *in_params)
+{
+	struct pass_shaderparam *params = in_params->array;
+	size_t i;
+
+	for (i = 0; i < in_params->num; i++) {
+		struct pass_shaderparam *param = params+i;
+		struct shader_param_info info;
+
+		shader_getparaminfo(shader, param->sparam, &info);
+		if (info.type == SHADER_PARAM_TEXTURE)
+			shader_settexture(shader, param->sparam, NULL);
+	}
+}
+
+void technique_endpass(technique_t tech)
+{
+	struct effect_pass *pass = tech->effect->cur_pass;
+	if (!pass)
+		return;
+
+	clear_tex_params(pass->vertshader, &pass->vertshader_params.da);
+	clear_tex_params(pass->pixelshader, &pass->pixelshader_params.da);
+	tech->effect->cur_pass = NULL;
+}
+
+int effect_numparams(effect_t effect)
+{
+	return (int)effect->params.num;
+}
+
+eparam_t effect_getparambyidx(effect_t effect, size_t param)
+{
+	struct effect_param *params = effect->params.array;
+
+	if (param >= effect->params.num)
+		return NULL;
+
+	return params+param;
+}
+
+eparam_t effect_getparambyname(effect_t effect, const char *name)
+{
+	struct effect_param *params = effect->params.array;
+	size_t i;
+
+	for (i = 0; i < effect->params.num; i++) {
+		struct effect_param *param = params+i;
+
+		if (strcmp(param->name, name) == 0)
+			return param;
+	}
+
+	return NULL;
+}
+
+void effect_getparaminfo(effect_t effect, eparam_t param,
+		struct effect_param_info *info)
+{
+	info->name = param->name;
+	info->type = param->type;
+}
+
+eparam_t effect_getviewprojmatrix(effect_t effect)
+{
+	return effect->view_proj;
+}
+
+eparam_t effect_getworldmatrix(effect_t effect)
+{
+	return effect->world;
+}
+
+static inline void effect_setval_inline(effect_t effect, eparam_t param,
+		const void *data, size_t size)
+{
+	bool size_changed = param->cur_val.num != size;
+	if (size_changed)
+		da_resize(param->cur_val, size);
+
+	if (size_changed || memcmp(param->cur_val.array, data, size) != 0) {
+		memcpy(param->cur_val.array, data, size);
+		param->changed = true;
+	}
+}
+
+void effect_setbool(effect_t effect, eparam_t param, bool val)
+{
+	effect_setval_inline(effect, param, &val, sizeof(bool));
+}
+
+void effect_setfloat(effect_t effect, eparam_t param, float val)
+{
+	effect_setval_inline(effect, param, &val, sizeof(float));
+}
+
+void effect_setint(effect_t effect, eparam_t param, int val)
+{
+	effect_setval_inline(effect, param, &val, sizeof(int));
+}
+
+void effect_setmatrix3(effect_t effect, eparam_t param,
+		const struct matrix3 *val)
+{
+	effect_setval_inline(effect, param, val, sizeof(struct matrix3));
+}
+
+void effect_setmatrix4(effect_t effect, eparam_t param,
+		const struct matrix4 *val)
+{
+	effect_setval_inline(effect, param, val, sizeof(struct matrix4));
+}
+
+void effect_setvec2(effect_t effect, eparam_t param,
+		const struct vec2 *val)
+{
+	effect_setval_inline(effect, param, val, sizeof(struct vec2));
+}
+
+void effect_setvec3(effect_t effect, eparam_t param,
+		const struct vec3 *val)
+{
+	effect_setval_inline(effect, param, val, sizeof(float) * 3);
+}
+
+void effect_setvec4(effect_t effect, eparam_t param,
+		const struct vec4 *val)
+{
+	effect_setval_inline(effect, param, val, sizeof(struct vec4));
+}
+
+void effect_settexture(effect_t effect, eparam_t param, texture_t val)
+{
+	effect_setval_inline(effect, param, &val, sizeof(texture_t));
+}
+
+void effect_setval(effect_t effect, eparam_t param, const void *val,
+		size_t size)
+{
+	effect_setval_inline(effect, param, val, size);
+}
+
+void effect_setdefault(effect_t effect, eparam_t param)
+{
+	effect_setval_inline(effect, param, param->default_val.array,
+			param->default_val.num);
+}

+ 181 - 0
libobs/graphics/effect.h

@@ -0,0 +1,181 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef EFFECT_H
+#define EFFECT_H
+
+#include "effect-parser.h"
+#include "graphics.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Effects introduce a means of bundling together shader text into one
+ * file with shared functions and parameters.  This is done because often
+ * shaders must be duplicated when you need to alter minor aspects of the code
+ * that cannot be done via constants.  Effects allow developers to easily
+ * switch shaders and set constants that can be used between shaders.
+ *
+ * Effects are built via the effect parser, and shaders are automatically
+ * generated for each technique's pass.
+ */
+
+/* ------------------------------------------------------------------------- */
+
+enum effect_section {
+	EFFECT_PARAM,
+	EFFECT_TECHNIQUE,
+	EFFECT_SAMPLER,
+	EFFECT_PASS
+};
+
+/* ------------------------------------------------------------------------- */
+
+struct effect_param {
+	char *name;
+	enum effect_section section;
+
+	enum shader_param_type type;
+
+	bool changed;
+	DARRAY(uint8_t) cur_val;
+	DARRAY(uint8_t) default_val;
+
+	/*char *full_name;
+	float scroller_min, scroller_max, scroller_inc, scroller_mul;*/
+};
+
+static inline void effect_param_init(struct effect_param *param)
+{
+	memset(param, 0, sizeof(struct effect_param));
+}
+
+static inline void effect_param_free(struct effect_param *param)
+{
+	bfree(param->name);
+	//bfree(param->full_name);
+	da_free(param->cur_val);
+	da_free(param->default_val);
+}
+
+EXPORT void effect_param_parse_property(eparam_t param,
+		const char *property);
+
+/* ------------------------------------------------------------------------- */
+
+struct pass_shaderparam {
+	struct effect_param *eparam;
+	sparam_t sparam;
+};
+
+struct effect_pass {
+	char *name;
+	enum effect_section section;
+
+	shader_t vertshader;
+	shader_t pixelshader;
+	DARRAY(struct pass_shaderparam) vertshader_params;
+	DARRAY(struct pass_shaderparam) pixelshader_params;
+};
+
+static inline void effect_pass_init(struct effect_pass *pass)
+{
+	memset(pass, 0, sizeof(struct effect_pass));
+}
+
+static inline void effect_pass_free(struct effect_pass *pass)
+{
+	bfree(pass->name);
+	da_free(pass->vertshader_params);
+	da_free(pass->pixelshader_params);
+	shader_destroy(pass->vertshader);
+	shader_destroy(pass->pixelshader);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct effect_technique {
+	char *name;
+	enum effect_section section;
+	struct gs_effect *effect;
+
+	DARRAY(struct effect_pass) passes;
+};
+
+static inline void effect_technique_init(struct effect_technique *t)
+{
+	memset(t, 0, sizeof(struct effect_technique));
+}
+
+static inline void effect_technique_free(struct effect_technique *t)
+{
+	size_t i;
+	for (i = 0; i < t->passes.num; i++)
+		effect_pass_free(t->passes.array+i);
+	da_free(t->passes);
+	bfree(t->name);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct gs_effect {
+	bool processing;
+	char *effect_path, *effect_dir;
+
+	DARRAY(struct effect_param) params;
+	DARRAY(struct effect_technique) techniques;
+
+	struct effect_technique *cur_technique;
+	struct effect_pass *cur_pass;
+
+	eparam_t view_proj, world, scale;
+	graphics_t graphics;
+};
+
+static inline void effect_init(effect_t effect)
+{
+	memset(effect, 0, sizeof(struct gs_effect));
+}
+
+static inline void effect_free(effect_t effect)
+{
+	size_t i;
+	for (i = 0; i < effect->params.num; i++)
+		effect_param_free(effect->params.array+i);
+	for (i = 0; i < effect->techniques.num; i++)
+		effect_technique_free(effect->techniques.array+i);
+
+	da_free(effect->params);
+	da_free(effect->techniques);
+
+	bfree(effect->effect_path);
+	bfree(effect->effect_dir);
+	effect->effect_path = NULL;
+	effect->effect_dir = NULL;
+}
+
+EXPORT void effect_upload_params(effect_t effect, bool changed_only);
+EXPORT void effect_upload_shader_params(effect_t effect, shader_t shader,
+		struct darray *pass_params, bool changed_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 162 - 0
libobs/graphics/graphics-imports.c

@@ -0,0 +1,162 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "../util/base.h"
+#include "../util/dstr.h"
+#include "../util/platform.h"
+#include "graphics-internal.h"
+
+#define GRAPHICS_IMPORT(func) \
+	do { \
+		exports->func = os_dlsym(module, #func); \
+		if (!exports->func) { \
+			success = false; \
+			blog(LOG_ERROR, "Could not load function '%s' from " \
+			                "module '%s'", #func, module_name); \
+		} \
+	} while (false)
+
+bool load_graphics_imports(struct gs_exports *exports, void *module,
+		const char *module_name)
+{
+	bool success = true;
+
+	GRAPHICS_IMPORT(device_create);
+	GRAPHICS_IMPORT(device_destroy);
+	GRAPHICS_IMPORT(device_create_swapchain);
+	GRAPHICS_IMPORT(device_resize);
+	GRAPHICS_IMPORT(device_getsize);
+	GRAPHICS_IMPORT(device_getwidth);
+	GRAPHICS_IMPORT(device_getheight);
+	GRAPHICS_IMPORT(device_create_texture);
+	GRAPHICS_IMPORT(device_create_cubetexture);
+	GRAPHICS_IMPORT(device_create_volumetexture);
+	GRAPHICS_IMPORT(device_create_zstencil);
+	GRAPHICS_IMPORT(device_create_stagesurface);
+	GRAPHICS_IMPORT(device_create_samplerstate);
+	GRAPHICS_IMPORT(device_create_vertexshader);
+	GRAPHICS_IMPORT(device_create_pixelshader);
+	GRAPHICS_IMPORT(device_create_vertexbuffer);
+	GRAPHICS_IMPORT(device_create_indexbuffer);
+	GRAPHICS_IMPORT(device_gettexturetype);
+	GRAPHICS_IMPORT(device_load_vertexbuffer);
+	GRAPHICS_IMPORT(device_load_indexbuffer);
+	GRAPHICS_IMPORT(device_load_texture);
+	GRAPHICS_IMPORT(device_load_samplerstate);
+	GRAPHICS_IMPORT(device_load_vertexshader);
+	GRAPHICS_IMPORT(device_load_pixelshader);
+	GRAPHICS_IMPORT(device_load_defaultsamplerstate);
+	GRAPHICS_IMPORT(device_getvertexshader);
+	GRAPHICS_IMPORT(device_getpixelshader);
+	GRAPHICS_IMPORT(device_getrendertarget);
+	GRAPHICS_IMPORT(device_getzstenciltarget);
+	GRAPHICS_IMPORT(device_setrendertarget);
+	GRAPHICS_IMPORT(device_setcuberendertarget);
+	GRAPHICS_IMPORT(device_copy_texture);
+	GRAPHICS_IMPORT(device_stage_texture);
+	GRAPHICS_IMPORT(device_beginscene);
+	GRAPHICS_IMPORT(device_draw);
+	GRAPHICS_IMPORT(device_load_swapchain);
+	GRAPHICS_IMPORT(device_endscene);
+	GRAPHICS_IMPORT(device_clear);
+	GRAPHICS_IMPORT(device_present);
+	GRAPHICS_IMPORT(device_setcullmode);
+	GRAPHICS_IMPORT(device_getcullmode);
+	GRAPHICS_IMPORT(device_enable_blending);
+	GRAPHICS_IMPORT(device_enable_depthtest);
+	GRAPHICS_IMPORT(device_enable_stenciltest);
+	GRAPHICS_IMPORT(device_enable_stencilwrite);
+	GRAPHICS_IMPORT(device_enable_color);
+	GRAPHICS_IMPORT(device_blendfunction);
+	GRAPHICS_IMPORT(device_depthfunction);
+	GRAPHICS_IMPORT(device_stencilfunction);
+	GRAPHICS_IMPORT(device_stencilop);
+	GRAPHICS_IMPORT(device_enable_fullscreen);
+	GRAPHICS_IMPORT(device_fullscreen_enabled);
+	GRAPHICS_IMPORT(device_setdisplaymode);
+	GRAPHICS_IMPORT(device_getdisplaymode);
+	GRAPHICS_IMPORT(device_setcolorramp);
+	GRAPHICS_IMPORT(device_setviewport);
+	GRAPHICS_IMPORT(device_getviewport);
+	GRAPHICS_IMPORT(device_setscissorrect);
+	GRAPHICS_IMPORT(device_ortho);
+	GRAPHICS_IMPORT(device_frustum);
+	GRAPHICS_IMPORT(device_perspective);
+	GRAPHICS_IMPORT(device_projection_push);
+	GRAPHICS_IMPORT(device_projection_pop);
+
+	GRAPHICS_IMPORT(swapchain_destroy);
+
+	GRAPHICS_IMPORT(texture_destroy);
+	GRAPHICS_IMPORT(texture_getwidth);
+	GRAPHICS_IMPORT(texture_getheight);
+	GRAPHICS_IMPORT(texture_getcolorformat);
+	GRAPHICS_IMPORT(texture_map);
+	GRAPHICS_IMPORT(texture_unmap);
+
+	GRAPHICS_IMPORT(cubetexture_destroy);
+	GRAPHICS_IMPORT(cubetexture_getsize);
+	GRAPHICS_IMPORT(cubetexture_getcolorformat);
+
+	GRAPHICS_IMPORT(volumetexture_destroy);
+	GRAPHICS_IMPORT(volumetexture_getwidth);
+	GRAPHICS_IMPORT(volumetexture_getheight);
+	GRAPHICS_IMPORT(volumetexture_getdepth);
+	GRAPHICS_IMPORT(volumetexture_getcolorformat);
+
+	GRAPHICS_IMPORT(stagesurface_destroy);
+	GRAPHICS_IMPORT(stagesurface_getwidth);
+	GRAPHICS_IMPORT(stagesurface_getheight);
+	GRAPHICS_IMPORT(stagesurface_getcolorformat);
+	GRAPHICS_IMPORT(stagesurface_map);
+	GRAPHICS_IMPORT(stagesurface_unmap);
+
+	GRAPHICS_IMPORT(zstencil_destroy);
+
+	GRAPHICS_IMPORT(samplerstate_destroy);
+
+	GRAPHICS_IMPORT(vertexbuffer_destroy);
+	GRAPHICS_IMPORT(vertexbuffer_flush);
+	GRAPHICS_IMPORT(vertexbuffer_getdata);
+
+	GRAPHICS_IMPORT(indexbuffer_destroy);
+	GRAPHICS_IMPORT(indexbuffer_flush);
+	GRAPHICS_IMPORT(indexbuffer_getdata);
+	GRAPHICS_IMPORT(indexbuffer_numindices);
+	GRAPHICS_IMPORT(indexbuffer_gettype);
+
+	GRAPHICS_IMPORT(shader_destroy);
+	GRAPHICS_IMPORT(shader_numparams);
+	GRAPHICS_IMPORT(shader_getparambyidx);
+	GRAPHICS_IMPORT(shader_getparambyname);
+	GRAPHICS_IMPORT(shader_getparaminfo);
+	GRAPHICS_IMPORT(shader_getviewprojmatrix);
+	GRAPHICS_IMPORT(shader_getworldmatrix);
+	GRAPHICS_IMPORT(shader_setbool);
+	GRAPHICS_IMPORT(shader_setfloat);
+	GRAPHICS_IMPORT(shader_setint);
+	GRAPHICS_IMPORT(shader_setmatrix3);
+	GRAPHICS_IMPORT(shader_setmatrix4);
+	GRAPHICS_IMPORT(shader_setvec2);
+	GRAPHICS_IMPORT(shader_setvec3);
+	GRAPHICS_IMPORT(shader_setvec4);
+	GRAPHICS_IMPORT(shader_settexture);
+	GRAPHICS_IMPORT(shader_setval);
+	GRAPHICS_IMPORT(shader_setdefault);
+
+	return success;
+}

+ 229 - 0
libobs/graphics/graphics-internal.h

@@ -0,0 +1,229 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef GRAPHICS_INTERNAL_H
+#define GRAPHICS_INTERNAL_H
+
+#include "../util/darray.h"
+#include "graphics.h"
+#include "matrix3.h"
+#include "matrix4.h"
+
+struct gs_exports {
+	device_t (*device_create)(struct gs_init_data *data);
+	void (*device_destroy)(device_t device);
+	swapchain_t (*device_create_swapchain)(device_t device,
+			struct gs_init_data *data);
+	void (*device_resize)(device_t device, uint32_t x, uint32_t y);
+	void (*device_getsize)(device_t device, uint32_t *x, uint32_t *y);
+	uint32_t (*device_getwidth)(device_t device);
+	uint32_t (*device_getheight)(device_t device);
+	texture_t (*device_create_texture)(device_t device, uint32_t width,
+			uint32_t height, enum gs_color_format color_format,
+			void *data, uint32_t flags);
+	texture_t (*device_create_cubetexture)(device_t device, uint32_t size,
+			enum gs_color_format color_format, void *data[6],
+			uint32_t flags);
+	texture_t (*device_create_volumetexture)(device_t device,
+			uint32_t width, uint32_t height, uint32_t depth,
+			enum gs_color_format color_format, void *data,
+			uint32_t flags);
+	zstencil_t (*device_create_zstencil)(device_t device,
+			uint32_t width, uint32_t height,
+			enum gs_zstencil_format format);
+	stagesurf_t (*device_create_stagesurface)(device_t device,
+			uint32_t width, uint32_t height,
+			enum gs_color_format color_format);
+	samplerstate_t (*device_create_samplerstate)(device_t device,
+			struct gs_sampler_info *info);
+	shader_t (*device_create_vertexshader)(device_t device,
+			const char *shader, const char *file,
+			char **error_string);
+	shader_t (*device_create_pixelshader)(device_t device,
+			const char *shader, const char *file,
+			char **error_string);
+	vertbuffer_t (*device_create_vertexbuffer)(device_t device,
+			struct vb_data *data, uint32_t flags);
+	indexbuffer_t (*device_create_indexbuffer)(device_t device,
+			enum gs_index_type type, void *indices, size_t num,
+			uint32_t flags);
+	enum gs_texture_type (*device_gettexturetype)(device_t device,
+			texture_t texture);
+	void (*device_load_vertexbuffer)(device_t device,
+			vertbuffer_t vertbuffer);
+	void (*device_load_indexbuffer)(device_t device,
+			indexbuffer_t indexbuffer);
+	void (*device_load_texture)(device_t device, texture_t tex, int unit);
+	void (*device_load_samplerstate)(device_t device,
+			samplerstate_t samplerstate, int unit);
+	void (*device_load_vertexshader)(device_t device, shader_t vertshader);
+	void (*device_load_pixelshader)(device_t device, shader_t pixelshader);
+	void (*device_load_defaultsamplerstate)(device_t device, bool b_3d,
+			int unit);
+	shader_t (*device_getvertexshader)(device_t device);
+	shader_t (*device_getpixelshader)(device_t device);
+	texture_t (*device_getrendertarget)(device_t device);
+	zstencil_t (*device_getzstenciltarget)(device_t device);
+	void (*device_setrendertarget)(device_t device, texture_t tex,
+			zstencil_t zstencil);
+	void (*device_setcuberendertarget)(device_t device, texture_t cubetex,
+			int side, zstencil_t zstencil);
+	void (*device_copy_texture)(device_t device, texture_t dst,
+			texture_t src);
+	void (*device_stage_texture)(device_t device, stagesurf_t dst,
+			texture_t src);
+	void (*device_beginscene)(device_t device);
+	void (*device_draw)(device_t device, enum gs_draw_mode draw_mode,
+			uint32_t start_vert, uint32_t num_verts);
+	void (*device_endscene)(device_t device);
+	void (*device_load_swapchain)(device_t device, swapchain_t swaphchain);
+	void (*device_clear)(device_t device, uint32_t clear_flags,
+			struct vec4 *color, float depth, uint8_t stencil);
+	void (*device_present)(device_t device);
+	void (*device_setcullmode)(device_t device, enum gs_cull_mode mode);
+	enum gs_cull_mode (*device_getcullmode)(device_t device);
+	void (*device_enable_blending)(device_t device, bool enable);
+	void (*device_enable_depthtest)(device_t device, bool enable);
+	void (*device_enable_stenciltest)(device_t device, bool enable);
+	void (*device_enable_stencilwrite)(device_t device, bool enable);
+	void (*device_enable_color)(device_t device, bool red, bool blue,
+			bool green, bool alpha);
+	void (*device_blendfunction)(device_t device, enum gs_blend_type src,
+			enum gs_blend_type dest);
+	void (*device_depthfunction)(device_t device, enum gs_depth_test test);
+	void (*device_stencilfunction)(device_t device,
+			enum gs_stencil_side side, enum gs_depth_test test);
+	void (*device_stencilop)(device_t device, enum gs_stencil_side side,
+			enum gs_stencil_op fail, enum gs_stencil_op zfail,
+			enum gs_stencil_op zpass);
+	void (*device_enable_fullscreen)(device_t device, bool enable);
+	int (*device_fullscreen_enabled)(device_t device);
+	void (*device_setdisplaymode)(device_t device,
+			const struct gs_display_mode *mode);
+	void (*device_getdisplaymode)(device_t device,
+			struct gs_display_mode *mode);
+	void (*device_setcolorramp)(device_t device, float gamma,
+			float brightness, float contrast);
+	void (*device_setviewport)(device_t device, int x, int y, int width,
+			int height);
+	void (*device_getviewport)(device_t device, struct gs_rect *rect);
+	void (*device_setscissorrect)(device_t device, struct gs_rect *rect);
+	void (*device_ortho)(device_t device, float left, float right,
+			float top, float bottom, float znear, float zfar);
+	void (*device_frustum)(device_t device, float left, float right,
+			float top, float bottom, float znear, float zfar);
+	void (*device_perspective)(device_t device, float fovy, float aspect,
+			float znear, float zfar);
+	void (*device_projection_push)(device_t device);
+	void (*device_projection_pop)(device_t device);
+
+	void     (*swapchain_destroy)(swapchain_t swapchain);
+
+	void     (*texture_destroy)(texture_t tex);
+	uint32_t (*texture_getwidth)(texture_t tex);
+	uint32_t (*texture_getheight)(texture_t tex);
+	enum gs_color_format (*texture_getcolorformat)(texture_t tex);
+	bool     (*texture_map)(texture_t tex, void **ptr,
+			uint32_t *byte_width);
+	void     (*texture_unmap)(texture_t tex);
+
+	void     (*cubetexture_destroy)(texture_t cubetex);
+	uint32_t (*cubetexture_getsize)(texture_t cubetex);
+	enum gs_color_format (*cubetexture_getcolorformat)(texture_t cubetex);
+
+	void     (*volumetexture_destroy)(texture_t voltex);
+	uint32_t (*volumetexture_getwidth)(texture_t voltex);
+	uint32_t (*volumetexture_getheight)(texture_t voltex);
+	uint32_t (*volumetexture_getdepth)(texture_t voltex);
+	enum gs_color_format (*volumetexture_getcolorformat)(texture_t voltex);
+
+	void     (*stagesurface_destroy)(stagesurf_t stagesurf);
+	uint32_t (*stagesurface_getwidth)(stagesurf_t stagesurf);
+	uint32_t (*stagesurface_getheight)(stagesurf_t stagesurf);
+	enum gs_color_format (*stagesurface_getcolorformat)(
+			stagesurf_t stagesurf);
+	bool     (*stagesurface_map)(stagesurf_t stagesurf, const void **data,
+			uint32_t *byte_width);
+	void     (*stagesurface_unmap)(stagesurf_t stagesurf);
+
+	void (*zstencil_destroy)(zstencil_t zstencil);
+
+	void (*samplerstate_destroy)(samplerstate_t samplerstate);
+
+	void (*vertexbuffer_destroy)(vertbuffer_t vertbuffer);
+	void (*vertexbuffer_flush)(vertbuffer_t vertbuffer, bool rebuild);
+	struct vb_data *(*vertexbuffer_getdata)(vertbuffer_t vertbuffer);
+
+	void   (*indexbuffer_destroy)(indexbuffer_t indexbuffer);
+	void   (*indexbuffer_flush)(indexbuffer_t indexbuffer);
+	void  *(*indexbuffer_getdata)(indexbuffer_t indexbuffer);
+	size_t (*indexbuffer_numindices)(indexbuffer_t indexbuffer);
+	enum gs_index_type (*indexbuffer_gettype)(indexbuffer_t indexbuffer);
+
+	void (*shader_destroy)(shader_t shader);
+	int (*shader_numparams)(shader_t shader);
+	sparam_t (*shader_getparambyidx)(shader_t shader, int param);
+	sparam_t (*shader_getparambyname)(shader_t shader, const char *name);
+	void (*shader_getparaminfo)(shader_t shader, sparam_t param,
+			struct shader_param_info *info);
+	sparam_t (*shader_getviewprojmatrix)(shader_t shader);
+	sparam_t (*shader_getworldmatrix)(shader_t shader);
+	void (*shader_setbool)(shader_t shader, sparam_t param, bool val);
+	void (*shader_setfloat)(shader_t shader, sparam_t param, float val);
+	void (*shader_setint)(shader_t shader, sparam_t param, int val);
+	void (*shader_setmatrix3)(shader_t shader, sparam_t param,
+			const struct matrix3 *val);
+	void (*shader_setmatrix4)(shader_t shader, sparam_t param,
+			const struct matrix4 *val);
+	void (*shader_setvec2)(shader_t shader, sparam_t param,
+			const struct vec2 *val);
+	void (*shader_setvec3)(shader_t shader, sparam_t param,
+			const struct vec3 *val);
+	void (*shader_setvec4)(shader_t shader, sparam_t param,
+			const struct vec4 *val);
+	void (*shader_settexture)(shader_t shader, sparam_t param,
+			texture_t val);
+	void (*shader_setval)(shader_t shader, sparam_t param, const void *val,
+			size_t size);
+	void (*shader_setdefault)(shader_t shader, sparam_t param);
+};
+
+struct graphics_subsystem {
+	void                   *module;
+	device_t               device;
+	struct gs_exports      exports;
+
+	DARRAY(struct gs_rect) viewport_stack;
+
+	DARRAY(struct matrix3) matrix_stack;
+	size_t                 cur_matrix;
+
+	struct matrix4         projection;
+	struct gs_effect       *cur_effect;
+
+	vertbuffer_t           sprite_buffer;
+
+	bool                   using_immediate;
+	struct vb_data         *vbd;
+	vertbuffer_t           immediate_vertbuffer;
+	DARRAY(struct vec3)    verts;
+	DARRAY(struct vec3)    norms;
+	DARRAY(uint32_t)       colors;
+	DARRAY(struct vec2)    texverts[16];
+};
+
+#endif

+ 1397 - 0
libobs/graphics/graphics.c

@@ -0,0 +1,1397 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <assert.h>
+
+#include "../util/base.h"
+#include "../util/bmem.h"
+#include "../util/platform.h"
+#include "graphics-internal.h"
+#include "vec2.h"
+#include "vec3.h"
+#include "quat.h"
+#include "axisang.h"
+#include "effect-parser.h"
+#include "effect.h"
+
+#if 0
+
+#ifdef _MSC_VER
+static __declspec(thread) graphics_t thread_graphics = NULL;
+#else /* assume GCC or that other compiler we dare not mention */
+static __thread graphics_t thread_graphics = NULL;
+#endif
+
+#else
+static graphics_t thread_graphics = NULL;
+#endif
+
+#define IMMEDIATE_VERTEX_COUNT 512
+
+bool load_graphics_imports(struct gs_exports *exports, void *module,
+		const char *module_name);
+
+static bool graphics_init(struct graphics_subsystem *graphics)
+{
+	struct matrix3 top_mat;
+	struct vb_data *vbd;
+
+	matrix3_identity(&top_mat);
+	da_push_back(graphics->matrix_stack, &top_mat);
+
+	vbd = vbdata_create();
+	vbd->num     = IMMEDIATE_VERTEX_COUNT;
+	vbd->points  = bmalloc(sizeof(struct vec3) * IMMEDIATE_VERTEX_COUNT);
+	vbd->normals = bmalloc(sizeof(struct vec3) * IMMEDIATE_VERTEX_COUNT);
+	vbd->colors  = bmalloc(sizeof(uint32_t)    * IMMEDIATE_VERTEX_COUNT);
+	vbd->num_tex = 1;
+	vbd->tvarray = bmalloc(sizeof(struct tvertarray));
+	vbd->tvarray[0].width = 2;
+	vbd->tvarray[0].array =
+		bmalloc(sizeof(struct vec2) * IMMEDIATE_VERTEX_COUNT);
+
+	graphics->immediate_vertbuffer = graphics->exports.
+		device_create_vertexbuffer(graphics->device, vbd, GS_DYNAMIC);
+	if (!graphics->immediate_vertbuffer)
+		return false;
+
+	vbd = vbdata_create();
+	vbd->num     = 4;
+	vbd->points  = bmalloc(sizeof(struct vec3) * 4);
+	vbd->num_tex = 1;
+	vbd->tvarray = bmalloc(sizeof(struct tvertarray));
+	vbd->tvarray[0].width = 2;
+	vbd->tvarray[0].array = bmalloc(sizeof(struct vec2) * 4);
+
+	memset(vbd->points,           0, sizeof(struct vec3) * 4);
+	memset(vbd->tvarray[0].array, 0, sizeof(struct vec2) * 4);
+
+	graphics->sprite_buffer = graphics->exports.
+		device_create_vertexbuffer(graphics->device, vbd, GS_DYNAMIC);
+	if (!graphics->sprite_buffer)
+		return false;
+
+	return true;
+}
+
+int gs_create(graphics_t *pgraphics, const char *module,
+		struct gs_init_data *data)
+{
+	int errcode = GS_ERROR_FAIL;
+
+	graphics_t graphics = bmalloc(sizeof(struct graphics_subsystem));
+	memset(graphics, 0, sizeof(struct graphics_subsystem));
+
+	graphics->module = os_dlopen(module);
+	if (!graphics->module) {
+		errcode = GS_ERROR_MODULENOTFOUND;
+		goto error;
+	}
+
+	if (!load_graphics_imports(&graphics->exports, graphics->module,
+	                           module))
+		goto error;
+
+	graphics->device = graphics->exports.device_create(data);
+	if (!graphics->device)
+		goto error;
+
+	if (!graphics_init(graphics))
+		goto error;
+
+	*pgraphics = graphics;
+	return GS_SUCCESS;
+
+error:
+	gs_destroy(graphics);
+	return errcode;
+}
+
+void gs_destroy(graphics_t graphics)
+{
+	if (!graphics)
+		return;
+
+	if (graphics->module) {
+		graphics->exports.vertexbuffer_destroy(graphics->sprite_buffer);
+		graphics->exports.vertexbuffer_destroy(
+				graphics->immediate_vertbuffer);
+		graphics->exports.device_destroy(graphics->device);
+	}
+
+	da_free(graphics->matrix_stack);
+	da_free(graphics->viewport_stack);
+	os_dlclose(graphics->module);
+	bfree(graphics);
+
+	if (thread_graphics == graphics)
+		thread_graphics = NULL;
+}
+
+void gs_setcontext(graphics_t graphics)
+{
+	thread_graphics = graphics;
+}
+
+graphics_t gs_getcontext(void)
+{
+	return thread_graphics;
+}
+
+static inline struct matrix3 *top_matrix(graphics_t graphics)
+{
+	return graphics->matrix_stack.array + graphics->cur_matrix;
+}
+
+void gs_matrix_push(void)
+{
+	graphics_t graphics = thread_graphics;
+	struct matrix3 mat, *top_mat = top_matrix(graphics);
+
+	memcpy(&mat, top_mat, sizeof(struct matrix3));
+	da_push_back(graphics->matrix_stack, &mat);
+	graphics->cur_matrix++;
+}
+
+void gs_matrix_pop(void)
+{
+	graphics_t graphics = thread_graphics;
+
+	if (graphics->cur_matrix == 0) {
+		blog(LOG_ERROR, "Tried to pop last matrix on stack");
+		return;
+	}
+
+	da_erase(graphics->matrix_stack, graphics->cur_matrix);
+	graphics->cur_matrix--;
+}
+
+void gs_matrix_identity(void)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_identity(top_mat);
+}
+
+void gs_matrix_transpose(void)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_transpose(top_mat, top_mat);
+}
+
+void gs_matrix_set(const struct matrix3 *matrix)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_copy(top_mat, matrix);
+}
+
+void gs_matrix_get(struct matrix3 *dst)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_copy(dst, top_mat);
+}
+
+void gs_matrix_mul(const struct matrix3 *matrix)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_mul(top_mat, top_mat, matrix);
+}
+
+void gs_matrix_rotquat(const struct quat *rot)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_rotate(top_mat, top_mat, rot);
+}
+
+void gs_matrix_rotaa(const struct axisang *rot)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_rotate_aa(top_mat, top_mat, rot);
+}
+
+void gs_matrix_translate(const struct vec3 *pos)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_translate(top_mat, top_mat, pos);
+}
+
+void gs_matrix_scale(const struct vec3 *scale)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	matrix3_scale(top_mat, top_mat, scale);
+}
+
+void gs_matrix_rotaa4f(float x, float y, float z, float angle)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	struct axisang aa;
+	
+	axisang_set(&aa, x, y, z, angle);
+	matrix3_rotate_aa(top_mat, top_mat, &aa);
+}
+
+void gs_matrix_translate3f(float x, float y, float z)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	struct vec3 p;
+
+	vec3_set(&p, x, y, z);
+	matrix3_translate(top_mat, top_mat, &p);
+}
+
+void gs_matrix_scale3f(float x, float y, float z)
+{
+	struct matrix3 *top_mat = top_matrix(thread_graphics);
+	struct vec3 p;
+
+	vec3_set(&p, x, y, z);
+	matrix3_scale(top_mat, top_mat, &p);
+}
+
+static inline void reset_immediate_arrays(graphics_t graphics)
+{
+	size_t i;
+
+	da_init(graphics->verts);
+	da_init(graphics->norms);
+	da_init(graphics->colors);
+	for (i = 0; i < 16; i++)
+		da_init(graphics->texverts[i]);
+}
+
+void gs_renderstart(bool b_new)
+{
+	graphics_t graphics = thread_graphics;
+
+	graphics->using_immediate = !b_new;
+	reset_immediate_arrays(graphics);
+
+	if (b_new) {
+		graphics->vbd = vbdata_create();
+	} else {
+		graphics->vbd = vertexbuffer_getdata(
+				graphics->immediate_vertbuffer);
+		memset(graphics->vbd->colors, 0xFF,
+				sizeof(uint32_t) * IMMEDIATE_VERTEX_COUNT);
+
+		graphics->verts.array       = graphics->vbd->points;
+		graphics->norms.array       = graphics->vbd->normals;
+		graphics->colors.array      = graphics->vbd->colors;
+		graphics->texverts[0].array = graphics->vbd->tvarray[0].array;
+
+		graphics->verts.capacity       = IMMEDIATE_VERTEX_COUNT;
+		graphics->norms.capacity       = IMMEDIATE_VERTEX_COUNT;
+		graphics->colors.capacity      = IMMEDIATE_VERTEX_COUNT;
+		graphics->texverts[0].capacity = IMMEDIATE_VERTEX_COUNT;
+	}
+}
+
+static inline size_t min_size(const size_t a, const size_t b)
+{
+	return (a < b) ? a : b;
+}
+
+void gs_renderstop(enum gs_draw_mode mode)
+{
+	graphics_t graphics = thread_graphics;
+	size_t i, num = graphics->verts.num;
+
+	if (!num) {
+		if (!graphics->using_immediate) {
+			da_free(graphics->verts);
+			da_free(graphics->norms);
+			da_free(graphics->colors);
+			for (i = 0; i < 16; i++)
+				da_free(graphics->texverts[i]);
+			vbdata_destroy(graphics->vbd);
+		}
+
+		return;
+	}
+
+	if (graphics->norms.num &&
+	    (graphics->norms.num != graphics->verts.num)) {
+		blog(LOG_WARNING, "gs_renderstop: normal count does "
+		                  "not match vertex count");
+		num = min_size(num, graphics->norms.num);
+	}
+
+	if (graphics->colors.num &&
+	    (graphics->colors.num != graphics->verts.num)) {
+		blog(LOG_WARNING, "gs_renderstop: color count does "
+		                  "not match vertex count");
+		num = min_size(num, graphics->colors.num);
+	}
+
+	if (graphics->texverts[0].num &&
+	    (graphics->texverts[0].num  != graphics->verts.num)) {
+		blog(LOG_WARNING, "gs_renderstop: texture vertex count does "
+		                  "not match vertex count");
+		num = min_size(num, graphics->texverts[0].num);
+	}
+
+	if (graphics->using_immediate) {
+		vertexbuffer_flush(graphics->immediate_vertbuffer, false);
+
+		gs_load_vertexbuffer(graphics->immediate_vertbuffer);
+		gs_load_indexbuffer(NULL);
+		gs_draw(mode, 0, (uint32_t)num);
+
+		reset_immediate_arrays(graphics);
+	} else {
+		vertbuffer_t vb = gs_rendersave();
+
+		gs_load_vertexbuffer(vb);
+		gs_load_indexbuffer(NULL);
+		gs_draw(mode, 0, 0);
+
+		vertexbuffer_destroy(vb);
+	}
+
+	graphics->vbd = NULL;
+}
+
+vertbuffer_t gs_rendersave(void)
+{
+	graphics_t graphics = thread_graphics;
+	size_t num_tex, i;
+
+	if (graphics->using_immediate)
+		return NULL;
+
+	if (!graphics->vbd->num) {
+		vbdata_destroy(graphics->vbd);
+		return NULL;
+	}
+
+	for (num_tex = 0; num_tex < 16; num_tex++) {
+		if (!graphics->texverts[num_tex].num)
+			break;
+	}
+
+	graphics->vbd->points  = graphics->verts.array;
+	graphics->vbd->normals = graphics->norms.array;
+	graphics->vbd->colors  = graphics->colors.array;
+	graphics->vbd->num     = graphics->verts.num;
+	graphics->vbd->num_tex = num_tex;
+	graphics->vbd->tvarray = bmalloc(sizeof(struct tvertarray) * num_tex);
+	for (i = 0; i < num_tex; i++) {
+		graphics->vbd->tvarray[i].width = 2;
+		graphics->vbd->tvarray[i].array = graphics->texverts[i].array;
+	}
+
+	reset_immediate_arrays(graphics);
+
+	return gs_create_vertexbuffer(graphics->vbd, 0);
+}
+
+void gs_vertex2f(float x, float y)
+{
+	struct vec3 v3;
+       
+	vec3_set(&v3, x, y, 0.0f);
+	gs_vertex3v(&v3);
+}
+
+void gs_vertex3f(float x, float y, float z)
+{
+	struct vec3 v3;
+
+	vec3_set(&v3, x, y, z);
+	gs_vertex3v(&v3);
+}
+
+void gs_normal3f(float x, float y, float z)
+{
+	struct vec3 v3;
+
+	vec3_set(&v3, x, y, z);
+	gs_normal3v(&v3);
+}
+
+static inline bool validvertsize(graphics_t graphics, size_t num,
+		const char *name)
+{
+	if (graphics->using_immediate &&
+	    graphics->colors.num == IMMEDIATE_VERTEX_COUNT) {
+		blog(LOG_WARNING, "%s: tried to use over %u "
+				  "for immediate rendering",
+				  name, IMMEDIATE_VERTEX_COUNT);
+		return false;
+	}
+
+	return true;
+}
+
+void gs_color(uint32_t color)
+{
+	graphics_t graphics = thread_graphics;
+	if (!validvertsize(graphics, graphics->colors.num, "gs_color"))
+		return;
+	
+	da_push_back(graphics->colors, &color);
+}
+
+void gs_texcoord(float x, float y, int unit)
+{
+	struct vec2 v2;
+
+	vec2_set(&v2, x, y);
+	gs_texcoord2v(&v2, unit);
+}
+
+void gs_vertex2v(const struct vec2 *v)
+{
+	struct vec3 v3;
+
+	vec3_set(&v3, v->x, v->y, 0.0f);
+	gs_vertex3v(&v3);
+}
+
+void gs_vertex3v(const struct vec3 *v)
+{
+	graphics_t graphics = thread_graphics;
+	if (!validvertsize(graphics, graphics->verts.num, "gs_vertex"))
+		return;
+
+	da_push_back(graphics->verts, v);
+}
+
+void gs_normal3v(const struct vec3 *v)
+{
+	graphics_t graphics = thread_graphics;
+	if (!validvertsize(graphics, graphics->norms.num, "gs_normal"))
+		return;
+	
+	da_push_back(graphics->norms, v);
+}
+
+void gs_color4v(const struct vec4 *v)
+{
+	/* TODO */
+}
+
+void gs_texcoord2v(const struct vec2 *v, int unit)
+{
+	graphics_t graphics = thread_graphics;
+	if (!validvertsize(graphics, graphics->texverts[unit].num,
+				"gs_texcoord"))
+		return;
+
+	da_push_back(graphics->texverts[unit], v);
+}
+
+input_t gs_getinput(void)
+{
+	/* TODO */
+	return NULL;
+}
+
+effect_t gs_geteffect(void)
+{
+	return thread_graphics->cur_effect;
+}
+
+effect_t gs_create_effect_from_file(const char *file, char **error_string)
+{
+	char *file_string;
+	effect_t effect = NULL;
+
+	file_string = os_quick_read_utf8_file(file);
+	if (!file_string) {
+		blog(LOG_WARNING, "Could not load effect file '%s'", file);
+		return NULL;
+	}
+
+	effect = gs_create_effect(file_string, file, error_string);
+	bfree(file_string);
+
+	return effect;
+}
+
+effect_t gs_create_effect(const char *effect_string, const char *filename,
+		char **error_string)
+{
+	struct gs_effect *effect = bmalloc(sizeof(struct gs_effect));
+	struct effect_parser parser;
+	bool success;
+
+	memset(effect, 0, sizeof(struct gs_effect));
+	effect->graphics = thread_graphics;
+
+	ep_init(&parser);
+	success = ep_parse(&parser, effect, effect_string, filename);
+	if (!success) {
+		*error_string = error_data_buildstring(
+				&parser.cfp.error_list);
+		effect_destroy(effect);
+		effect = NULL;
+	}
+
+	ep_free(&parser);
+	return effect;
+}
+
+shader_t gs_create_vertexshader_from_file(const char *file,
+	char **error_string)
+{
+	char *file_string;
+	shader_t shader = NULL;
+
+	file_string = os_quick_read_utf8_file(file);
+	if (!file_string) {
+		blog(LOG_WARNING, "Could not load vertex shader file '%s'",
+				file);
+		return NULL;
+	}
+
+	shader = gs_create_vertexshader(file_string, file, error_string);
+	bfree(file_string);
+
+	return shader;
+}
+
+shader_t gs_create_pixelshader_from_file(const char *file,
+	char **error_string)
+{
+	char *file_string;
+	shader_t shader = NULL;
+
+	file_string = os_quick_read_utf8_file(file);
+	if (!file_string) {
+		blog(LOG_WARNING, "Could not load pixel shader file '%s'",
+				file);
+		return NULL;
+	}
+
+	shader = gs_create_pixelshader(file_string, file, error_string);
+	bfree(file_string);
+
+	return shader;
+}
+
+texture_t gs_create_texture_from_file(const char *file, uint32_t flags)
+{
+	/* TODO */
+	return NULL;
+}
+
+texture_t gs_create_cubetexture_from_file(const char *flie, uint32_t flags)
+{
+	/* TODO */
+	return NULL;
+}
+
+texture_t gs_create_volumetexture_from_file(const char *flie, uint32_t flags)
+{
+	/* TODO */
+	return NULL;
+}
+
+void gs_draw_sprite(texture_t tex)
+{
+	graphics_t graphics = thread_graphics;
+	float fcx, fcy;
+	struct vb_data *data;
+	struct vec2 *tvarray;
+
+	assert(tex);
+
+	if (gs_gettexturetype(tex) != GS_TEXTURE_2D) {
+		blog(LOG_ERROR, "A sprite must be a 2D texture");
+		return;
+	}
+
+	fcx = (float)texture_getwidth(tex);
+	fcy = (float)texture_getheight(tex);
+
+	data = vertexbuffer_getdata(graphics->sprite_buffer);
+	tvarray = data->tvarray[0].array;
+	vec3_zero(data->points);
+	vec3_set(data->points+1,  fcx, 0.0f, 0.0f);
+	vec3_set(data->points+2, 0.0f,  fcy, 0.0f);
+	vec3_set(data->points+3,  fcx,  fcy, 0.0f);
+	vec2_zero(tvarray);
+	vec2_set(tvarray+1, 1.0f, 0.0f);
+	vec2_set(tvarray+2, 0.0f, 1.0f);
+	vec2_set(tvarray+3, 1.0f, 1.0f);
+	vertexbuffer_flush(graphics->sprite_buffer, false);
+	gs_load_vertexbuffer(graphics->sprite_buffer);
+
+	gs_draw(GS_TRISTRIP, 0, 0);
+}
+
+void gs_draw_cube_backdrop(texture_t cubetex, const struct quat *rot,
+		float left, float right, float top, float bottom, float znear)
+{
+	/* TODO */
+}
+
+void gs_resetviewport(void)
+{
+	uint32_t cx, cy;
+	gs_getsize(&cx, &cy);
+	gs_setviewport(0, 0, cx, cy);
+}
+
+void gs_set2dmode(void)
+{
+	uint32_t cx, cy;
+	gs_getsize(&cx, &cy);
+
+	gs_ortho(0.0f, (float)cx, 0.0f, (float)cy, -1.0, -1024.0f);
+}
+
+void gs_set3dmode(double fovy, double znear, double zvar)
+{
+	/* TODO */
+}
+
+void gs_viewport_push(void)
+{
+	struct gs_rect *rect = da_push_back_new(
+			thread_graphics->viewport_stack);
+	gs_getviewport(rect);
+}
+
+void gs_viewport_pop(void)
+{
+	struct gs_rect *rect;
+	if (!thread_graphics->viewport_stack.num)
+		return;
+
+	rect = da_end(thread_graphics->viewport_stack);
+	gs_setviewport(rect->x, rect->y, rect->cx, rect->cy);
+	da_pop_back(thread_graphics->viewport_stack);
+}
+
+void texture_setimage(texture_t tex, const void *data, uint32_t byte_width)
+{
+	/* TODO */
+}
+
+void cubetexture_setimage(texture_t cubetex, uint32_t side, const void *data,
+		uint32_t byte_width)
+{
+	/* TODO */
+}
+
+/* ------------------------------------------------------------------------- */
+
+swapchain_t gs_create_swapchain(struct gs_init_data *data)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_swapchain(graphics->device,
+			data);
+}
+
+void gs_resize(uint32_t x, uint32_t y)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_resize(graphics->device, x, y);
+}
+
+void gs_getsize(uint32_t *x, uint32_t *y)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_getsize(graphics->device, x, y);
+}
+
+uint32_t gs_getwidth(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getwidth(graphics->device);
+}
+
+uint32_t gs_getheight(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getheight(graphics->device);
+}
+
+texture_t gs_create_texture(uint32_t width, uint32_t height,
+		enum gs_color_format color_format, void *data, uint32_t flags)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_texture(graphics->device,
+			width, height, color_format, data, flags);
+}
+
+texture_t gs_create_cubetexture(uint32_t size,
+		enum gs_color_format color_format, void *data[6],
+		uint32_t flags)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_cubetexture(graphics->device,
+			size, color_format, data, flags);
+}
+
+texture_t gs_create_volumetexture(uint32_t width, uint32_t height,
+		uint32_t depth, enum gs_color_format color_format, void *data,
+		uint32_t flags)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_volumetexture(graphics->device,
+			width, height, depth, color_format, data, flags);
+}
+
+zstencil_t gs_create_zstencil(uint32_t width, uint32_t height,
+		enum gs_zstencil_format format)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_zstencil(graphics->device,
+			width, height, format);
+}
+
+stagesurf_t gs_create_stagesurface(uint32_t width, uint32_t height,
+		enum gs_color_format color_format)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_stagesurface(graphics->device,
+			width, height, color_format);
+}
+
+samplerstate_t gs_create_samplerstate(struct gs_sampler_info *info)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_samplerstate(graphics->device,
+			info);
+}
+
+shader_t gs_create_vertexshader(const char *shader, const char *file,
+		char **error_string)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_vertexshader(graphics->device,
+			shader, file, error_string);
+}
+
+shader_t gs_create_pixelshader(const char *shader,
+		const char *file, char **error_string)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_pixelshader(graphics->device,
+			shader, file, error_string);
+}
+
+vertbuffer_t gs_create_vertexbuffer(struct vb_data *data,
+		uint32_t flags)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_vertexbuffer(graphics->device,
+			data, flags);
+}
+
+indexbuffer_t gs_create_indexbuffer(enum gs_index_type type,
+		void *indices, size_t num, uint32_t flags)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_create_indexbuffer(graphics->device,
+			type, indices, num, flags);
+}
+
+enum gs_texture_type gs_gettexturetype(texture_t texture)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_gettexturetype(graphics->device,
+			texture);
+}
+
+void gs_load_vertexbuffer(vertbuffer_t vertbuffer)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_vertexbuffer(graphics->device,
+			vertbuffer);
+}
+
+void gs_load_indexbuffer(indexbuffer_t indexbuffer)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_indexbuffer(graphics->device,
+			indexbuffer);
+}
+
+void gs_load_texture(texture_t tex, int unit)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_texture(graphics->device, tex, unit);
+}
+
+void gs_load_samplerstate(samplerstate_t samplerstate, int unit)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_samplerstate(graphics->device,
+			samplerstate, unit);
+}
+
+void gs_load_vertexshader(shader_t vertshader)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_vertexshader(graphics->device,
+			vertshader);
+}
+
+void gs_load_pixelshader(shader_t pixelshader)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_pixelshader(graphics->device,
+			pixelshader);
+}
+
+void gs_load_defaultsamplerstate(bool b_3d, int unit)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_defaultsamplerstate(graphics->device,
+			b_3d, unit);
+}
+
+shader_t gs_getvertexshader(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getvertexshader(graphics->device);
+}
+
+shader_t gs_getpixelshader(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getpixelshader(graphics->device);
+}
+
+texture_t gs_getrendertarget(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getrendertarget(graphics->device);
+}
+
+zstencil_t gs_getzstenciltarget(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getzstenciltarget(graphics->device);
+}
+
+void gs_setrendertarget(texture_t tex, zstencil_t zstencil)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setrendertarget(graphics->device, tex,
+			zstencil);
+}
+
+void gs_setcuberendertarget(texture_t cubetex, int side, zstencil_t zstencil)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setcuberendertarget(graphics->device, cubetex,
+			side, zstencil);
+}
+
+void gs_copy_texture(texture_t dst, texture_t src)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_copy_texture(graphics->device, dst, src);
+}
+
+void gs_stage_texture(stagesurf_t dst, texture_t src)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_stage_texture(graphics->device, dst, src);
+}
+
+void gs_beginscene(void)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_beginscene(graphics->device);
+}
+
+void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert,
+		uint32_t num_verts)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_draw(graphics->device, draw_mode,
+			start_vert, num_verts);
+}
+
+void gs_endscene(void)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_endscene(graphics->device);
+}
+
+void gs_load_swapchain(swapchain_t swapchain)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_load_swapchain(graphics->device, swapchain);
+}
+
+void gs_clear(uint32_t clear_flags, struct vec4 *color, float depth,
+		uint8_t stencil)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_clear(graphics->device, clear_flags, color,
+			depth, stencil);
+}
+
+void gs_present(void)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_present(graphics->device);
+}
+
+void gs_setcullmode(enum gs_cull_mode mode)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setcullmode(graphics->device, mode);
+}
+
+enum gs_cull_mode gs_getcullmode(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_getcullmode(graphics->device);
+}
+
+void gs_enable_blending(bool enable)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_enable_blending(graphics->device, enable);
+}
+
+void gs_enable_depthtest(bool enable)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_enable_depthtest(graphics->device, enable);
+}
+
+void gs_enable_stenciltest(bool enable)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_enable_stenciltest(graphics->device, enable);
+}
+
+void gs_enable_stencilwrite(bool enable)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_enable_stencilwrite(graphics->device, enable);
+}
+
+void gs_enable_color(bool red, bool blue, bool green, bool alpha)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_enable_color(graphics->device, red, blue,
+			green, alpha);
+}
+
+void gs_blendfunction(enum gs_blend_type src, enum gs_blend_type dest)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_blendfunction(graphics->device, src, dest);
+}
+
+void gs_depthfunction(enum gs_depth_test test)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_depthfunction(graphics->device, test);
+}
+
+void gs_stencilfunction(enum gs_stencil_side side, enum gs_depth_test test)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_stencilfunction(graphics->device, side, test);
+}
+
+void gs_stencilop(enum gs_stencil_side side, enum gs_stencil_op fail,
+		enum gs_stencil_op zfail, enum gs_stencil_op zpass)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_stencilop(graphics->device, side, fail, zfail,
+			zpass);
+}
+
+void gs_enable_fullscreen(bool enable)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_enable_fullscreen(graphics->device, enable);
+}
+
+int gs_fullscreen_enabled(void)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.device_fullscreen_enabled(graphics->device);
+}
+
+void gs_setdisplaymode(const struct gs_display_mode *mode)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setdisplaymode(graphics->device, mode);
+}
+
+void gs_getdisplaymode(struct gs_display_mode *mode)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_getdisplaymode(graphics->device, mode);
+}
+
+void gs_setcolorramp(float gamma, float brightness, float contrast)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setcolorramp(graphics->device, gamma,
+			brightness, contrast);
+}
+
+void gs_setviewport(int x, int y, int width, int height)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setviewport(graphics->device, x, y, width,
+			height);
+}
+
+void gs_getviewport(struct gs_rect *rect)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_getviewport(graphics->device, rect);
+}
+
+void gs_setscissorrect(struct gs_rect *rect)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_setscissorrect(graphics->device, rect);
+}
+
+void gs_ortho(float left, float right, float top, float bottom, float znear,
+		float zfar)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_ortho(graphics->device, left, right, top,
+			bottom, znear, zfar);
+}
+
+void gs_frustum(float left, float right, float top, float bottom, float znear,
+		float zfar)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_frustum(graphics->device, left, right, top,
+			bottom, znear, zfar);
+}
+
+void gs_perspective(float fovy, float aspect, float znear, float zfar)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_perspective(graphics->device, fovy, aspect,
+			znear, zfar);
+}
+
+void gs_projection_push(void)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_projection_push(graphics->device);
+}
+
+void gs_projection_pop(void)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.device_projection_pop(graphics->device);
+}
+
+void swapchain_destroy(swapchain_t swapchain)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.swapchain_destroy(swapchain);
+}
+
+void shader_destroy(shader_t shader)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_destroy(shader);
+}
+
+int shader_numparams(shader_t shader)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.shader_numparams(shader);
+}
+
+sparam_t shader_getparambyidx(shader_t shader, int param)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.shader_getparambyidx(shader, param);
+}
+
+sparam_t shader_getparambyname(shader_t shader, const char *name)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.shader_getparambyname(shader, name);
+}
+
+void shader_getparaminfo(shader_t shader, sparam_t param,
+		struct shader_param_info *info)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_getparaminfo(shader, param, info);
+}
+
+sparam_t shader_getviewprojmatrix(shader_t shader)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.shader_getviewprojmatrix(shader);
+}
+
+sparam_t shader_getworldmatrix(shader_t shader)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.shader_getworldmatrix(shader);
+}
+
+void shader_setbool(shader_t shader, sparam_t param, bool val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setbool(shader, param, val);
+}
+
+void shader_setfloat(shader_t shader, sparam_t param, float val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setfloat(shader, param, val);
+}
+
+void shader_setint(shader_t shader, sparam_t param, int val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setint(shader, param, val);
+}
+
+void shader_setmatrix3(shader_t shader, sparam_t param,
+		const struct matrix3 *val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setmatrix3(shader, param, val);
+}
+
+void shader_setmatrix4(shader_t shader, sparam_t param,
+		const struct matrix4 *val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setmatrix4(shader, param, val);
+}
+
+void shader_setvec2(shader_t shader, sparam_t param,
+		const struct vec2 *val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setvec2(shader, param, val);
+}
+
+void shader_setvec3(shader_t shader, sparam_t param,
+		const struct vec3 *val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setvec3(shader, param, val);
+}
+
+void shader_setvec4(shader_t shader, sparam_t param,
+		const struct vec4 *val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setvec4(shader, param, val);
+}
+
+void shader_settexture(shader_t shader, sparam_t param, texture_t val)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_settexture(shader, param, val);
+}
+
+void shader_setval(shader_t shader, sparam_t param, const void *val,
+		size_t size)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setval(shader, param, val, size);
+}
+
+void shader_setdefault(shader_t shader, sparam_t param)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.shader_setdefault(shader, param);
+}
+
+void texture_destroy(texture_t tex)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.texture_destroy(tex);
+}
+
+uint32_t texture_getwidth(texture_t tex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.texture_getwidth(tex);
+}
+
+uint32_t texture_getheight(texture_t tex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.texture_getheight(tex);
+}
+
+enum gs_color_format texture_getcolorformat(texture_t tex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.texture_getcolorformat(tex);
+}
+
+bool texture_map(texture_t tex, void **ptr, uint32_t *byte_width)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.texture_map(tex, ptr, byte_width);
+}
+
+void texture_unmap(texture_t tex)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.texture_unmap(tex);
+}
+
+void cubetexture_destroy(texture_t cubetex)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.cubetexture_destroy(cubetex);
+}
+
+uint32_t cubetexture_getsize(texture_t cubetex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.cubetexture_getsize(cubetex);
+}
+
+enum gs_color_format cubetexture_getcolorformat(texture_t cubetex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.cubetexture_getcolorformat(cubetex);
+}
+
+void volumetexture_destroy(texture_t voltex)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.volumetexture_destroy(voltex);
+}
+
+uint32_t volumetexture_getwidth(texture_t voltex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.volumetexture_getwidth(voltex);
+}
+
+uint32_t volumetexture_getheight(texture_t voltex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.volumetexture_getheight(voltex);
+}
+
+uint32_t volumetexture_getdepth(texture_t voltex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.volumetexture_getdepth(voltex);
+}
+
+enum gs_color_format volumetexture_getcolorformat(texture_t voltex)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.volumetexture_getcolorformat(voltex);
+}
+
+void stagesurface_destroy(stagesurf_t stagesurf)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.stagesurface_destroy(stagesurf);
+}
+
+uint32_t stagesurface_getwidth(stagesurf_t stagesurf)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.stagesurface_getwidth(stagesurf);
+}
+
+uint32_t stagesurface_getheight(stagesurf_t stagesurf)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.stagesurface_getheight(stagesurf);
+}
+
+enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.stagesurface_getcolorformat(stagesurf);
+}
+
+bool stagesurface_map(stagesurf_t stagesurf, const void **data,
+		uint32_t *byte_width)
+{
+	graphics_t graphics = thread_graphics;
+	return graphics->exports.stagesurface_map(stagesurf, data, byte_width);
+}
+
+void stagesurface_unmap(stagesurf_t stagesurf)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.stagesurface_unmap(stagesurf);
+}
+
+void zstencil_destroy(zstencil_t zstencil)
+{
+	thread_graphics->exports.zstencil_destroy(zstencil);
+}
+
+void samplerstate_destroy(samplerstate_t samplerstate)
+{
+	thread_graphics->exports.samplerstate_destroy(samplerstate);
+}
+
+void vertexbuffer_destroy(vertbuffer_t vertbuffer)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.vertexbuffer_destroy(vertbuffer);
+}
+
+void vertexbuffer_flush(vertbuffer_t vertbuffer, bool rebuild)
+{
+	thread_graphics->exports.vertexbuffer_flush(vertbuffer, rebuild);
+}
+
+struct vb_data *vertexbuffer_getdata(vertbuffer_t vertbuffer)
+{
+	return thread_graphics->exports.vertexbuffer_getdata(vertbuffer);
+}
+
+void indexbuffer_destroy(indexbuffer_t indexbuffer)
+{
+	graphics_t graphics = thread_graphics;
+	graphics->exports.indexbuffer_destroy(indexbuffer);
+}
+
+void   indexbuffer_flush(indexbuffer_t indexbuffer)
+{
+	thread_graphics->exports.indexbuffer_flush(indexbuffer);
+}
+
+void  *indexbuffer_getdata(indexbuffer_t indexbuffer)
+{
+	return thread_graphics->exports.indexbuffer_getdata(indexbuffer);
+}
+
+size_t indexbuffer_numindices(indexbuffer_t indexbuffer)
+{
+	return thread_graphics->exports.indexbuffer_numindices(indexbuffer);
+}
+
+enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer)
+{
+	return thread_graphics->exports.indexbuffer_gettype(indexbuffer);
+}

+ 664 - 0
libobs/graphics/graphics.h

@@ -0,0 +1,664 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef GRAPHICS_H
+#define GRAPHICS_H
+
+#include "../util/bmem.h"
+#include "input.h"
+
+/*
+ * This is an API-independent graphics subsystem wrapper.
+ *
+ *   This allows the use of OpenGL and different Direct3D versions through
+ * one shared interface.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct vec2;
+struct vec3;
+struct vec4;
+struct quat;
+struct axisang;
+struct plane;
+struct matrix3;
+struct matrix4;
+
+enum gs_draw_mode {
+	GS_POINTS,
+	GS_LINES,
+	GS_LINESTRIP,
+	GS_TRIS,
+	GS_TRISTRIP
+};
+
+enum gs_color_format {
+	GS_UNKNOWN,
+	GS_A8,
+	GS_R8,
+	GS_RGBA,
+	GS_BGRX,
+	GS_BGRA,
+	GS_R10G10B10A2,
+	GS_RGBA16,
+	GS_R16,
+	GS_RGBA16F,
+	GS_RGBA32F,
+	GS_RG16F,
+	GS_RG32F,
+	GS_R16F,
+	GS_R32F,
+	GS_DXT1,
+	GS_DXT3,
+	GS_DXT5
+};
+
+enum gs_zstencil_format {
+	GS_ZS_NONE,
+	GS_Z16,
+	GS_Z24_S8,
+	GS_Z32F,
+	GS_Z32F_S8X24
+};
+
+enum gs_index_type {
+	GS_UNSIGNED_SHORT,
+	GS_UNSIGNED_LONG
+};
+
+enum gs_cull_mode {
+	GS_BACK,
+	GS_FRONT,
+	GS_NEITHER
+};
+
+enum gs_blend_type {
+	GS_BLEND_ZERO,
+	GS_BLEND_ONE,
+	GS_BLEND_SRCCOLOR,
+	GS_BLEND_INVSRCCOLOR,
+	GS_BLEND_SRCALPHA,
+	GS_BLEND_INVSRCALPHA,
+	GS_BLEND_DSTCOLOR,
+	GS_BLEND_INVDSTCOLOR,
+	GS_BLEND_DSTALPHA,
+	GS_BLEND_INVDSTALPHA,
+	GS_BLEND_SRCALPHASAT
+};
+
+enum gs_depth_test {
+	GS_NEVER,
+	GS_LESS,
+	GS_LEQUAL,
+	GS_EQUAL,
+	GS_GEQUAL,
+	GS_GREATER,
+	GS_NOTEQUAL,
+	GS_ALWAYS
+};
+
+enum gs_stencil_side {
+	GS_STENCIL_FRONT=1,
+	GS_STENCIL_BACK,
+	GS_STENCIL_BOTH
+};
+
+enum gs_stencil_op {
+	GS_KEEP,
+	GS_ZERO,
+	GS_REPLACE,
+	GS_INCR,
+	GS_DECR,
+	GS_INVERT
+};
+
+enum gs_cube_sides {
+	GS_POSITIVE_X,
+	GS_NEGATIVE_X,
+	GS_POSITIVE_Y,
+	GS_NEGATIVE_Y,
+	GS_POSITIVE_Z,
+	GS_NEGATIVE_Z
+};
+
+enum gs_sample_filter {
+	GS_FILTER_POINT,
+	GS_FILTER_LINEAR,
+	GS_FILTER_ANISOTROPIC,
+	GS_FILTER_MIN_MAG_POINT_MIP_LINEAR,
+	GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT,
+	GS_FILTER_MIN_POINT_MAG_MIP_LINEAR,
+	GS_FILTER_MIN_LINEAR_MAG_MIP_POINT,
+	GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR,
+	GS_FILTER_MIN_MAG_LINEAR_MIP_POINT,
+};
+
+enum gs_address_mode {
+	GS_ADDRESS_CLAMP,
+	GS_ADDRESS_WRAP,
+	GS_ADDRESS_MIRROR,
+	GS_ADDRESS_BORDER,
+	GS_ADDRESS_MIRRORONCE
+};
+
+enum gs_texture_type {
+	GS_TEXTURE_2D,
+	GS_TEXTURE_3D,
+	GS_TEXTURE_CUBE
+};
+
+struct tvertarray {
+	size_t width;
+	void *array;
+};
+
+struct vb_data {
+	size_t num;
+	struct vec3 *points;
+	struct vec3 *normals;
+	struct vec3 *tangents;
+	uint32_t *colors;
+
+	size_t num_tex;
+	struct tvertarray *tvarray;
+};
+
+static inline struct vb_data *vbdata_create(void)
+{
+	struct vb_data *vbd = (struct vb_data*)bmalloc(sizeof(struct vb_data));
+	memset(vbd, 0, sizeof(struct vb_data));
+	return vbd;
+}
+
+static inline void vbdata_destroy(struct vb_data *data)
+{
+	uint32_t i;
+	if (!data)
+		return;
+
+	bfree(data->points);
+	bfree(data->normals);
+	bfree(data->tangents);
+	bfree(data->colors);
+	for (i = 0; i < data->num_tex; i++)
+		bfree(data->tvarray[i].array);
+	bfree(data->tvarray);
+	bfree(data);
+}
+
+struct gs_sampler_info {
+	enum gs_sample_filter filter;
+	enum gs_address_mode address_u;
+	enum gs_address_mode address_v;
+	enum gs_address_mode address_w;
+	int max_anisotropy;
+	uint32_t border_color;
+};
+
+struct gs_display_mode {
+	uint32_t width;
+	uint32_t height;
+	uint32_t bits;
+	uint32_t freq;
+};
+
+struct gs_rect {
+	int x;
+	int y;
+	int cx;
+	int cy;
+};
+
+/* wrapped opaque data types */
+
+struct gs_texture;
+struct gs_stage_surface;
+struct gs_zstencil_buffer;
+struct gs_vertex_buffer;
+struct gs_index_buffer;
+struct gs_sampler_state;
+struct gs_shader;
+struct gs_swap_chain;
+struct gs_texrender;
+struct shader_param;
+struct gs_effect;
+struct effect_technique;
+struct effect_pass;
+struct effect_param;
+struct gs_device;
+struct graphics_subsystem;
+
+typedef struct gs_texture         *texture_t;
+typedef struct gs_stage_surface   *stagesurf_t;
+typedef struct gs_zstencil_buffer *zstencil_t;
+typedef struct gs_vertex_buffer   *vertbuffer_t;
+typedef struct gs_index_buffer    *indexbuffer_t;
+typedef struct gs_sampler_state   *samplerstate_t;
+typedef struct gs_swap_chain      *swapchain_t;
+typedef struct gs_texture_render  *texrender_t;
+typedef struct gs_shader          *shader_t;
+typedef struct shader_param       *sparam_t;
+typedef struct gs_effect          *effect_t;
+typedef struct effect_technique   *technique_t;
+typedef struct effect_param       *eparam_t;
+typedef struct gs_device          *device_t;
+typedef struct graphics_subsystem *graphics_t;
+
+/* ---------------------------------------------------
+ * shader functions
+ * --------------------------------------------------- */
+
+enum shader_param_type {
+	SHADER_PARAM_UNKNOWN,
+	SHADER_PARAM_BOOL,
+	SHADER_PARAM_FLOAT,
+	SHADER_PARAM_INT,
+	SHADER_PARAM_STRING,
+	SHADER_PARAM_VEC2,
+	SHADER_PARAM_VEC3,
+	SHADER_PARAM_VEC4,
+	SHADER_PARAM_MATRIX3X3,
+	SHADER_PARAM_MATRIX4X4,
+	SHADER_PARAM_TEXTURE,
+};
+
+struct shader_param_info {
+	enum shader_param_type type;
+	const char *name;
+};
+
+enum shader_type {
+	SHADER_VERTEX,
+	SHADER_PIXEL,
+	SHADER_GEOMETRY
+};
+
+EXPORT void shader_destroy(shader_t shader);
+
+EXPORT int shader_numparams(shader_t shader);
+EXPORT sparam_t shader_getparambyidx(shader_t shader, int param);
+EXPORT sparam_t shader_getparambyname(shader_t shader, const char *name);
+EXPORT void shader_getparaminfo(shader_t shader, sparam_t param,
+		struct shader_param_info *info);
+
+EXPORT sparam_t shader_getviewprojmatrix(shader_t shader);
+EXPORT sparam_t shader_getworldmatrix(shader_t shader);
+
+EXPORT void shader_setbool(shader_t shader, sparam_t param, bool val);
+EXPORT void shader_setfloat(shader_t shader, sparam_t param, float val);
+EXPORT void shader_setint(shader_t shader, sparam_t param, int val);
+EXPORT void shader_setmatrix3(shader_t shader, sparam_t param,
+		const struct matrix3 *val);
+EXPORT void shader_setmatrix4(shader_t shader, sparam_t param,
+		const struct matrix4 *val);
+EXPORT void shader_setvec2(shader_t shader, sparam_t param,
+		const struct vec2 *val);
+EXPORT void shader_setvec3(shader_t shader, sparam_t param,
+		const struct vec3 *val);
+EXPORT void shader_setvec4(shader_t shader, sparam_t param,
+		const struct vec4 *val);
+EXPORT void shader_settexture(shader_t shader, sparam_t param, texture_t val);
+EXPORT void shader_setval(shader_t shader, sparam_t param, const void *val,
+		size_t size);
+EXPORT void shader_setdefault(shader_t shader, sparam_t param);
+
+/* ---------------------------------------------------
+ * effect functions
+ * --------------------------------------------------- */
+
+/*enum effect_property_type {
+	EFFECT_NONE,
+	EFFECT_BOOL,
+	EFFECT_FLOAT,
+	EFFECT_COLOR,
+	EFFECT_TEXTURE
+};*/
+
+struct effect_param_info {
+	const char *name;
+	enum shader_param_type type;
+
+	/* const char *full_name;
+	enum effect_property_type prop_type;
+
+	float min, max, inc, mul; */
+};
+
+EXPORT void effect_destroy(effect_t effect);
+
+EXPORT technique_t effect_gettechnique(effect_t effect, const char *name);
+
+EXPORT int technique_begin(technique_t technique);
+EXPORT void technique_end(technique_t technique);
+EXPORT bool technique_beginpass(technique_t technique, size_t pass);
+EXPORT bool technique_beginpassbyname(technique_t technique,
+		const char *name);
+EXPORT void technique_endpass(technique_t technique);
+
+EXPORT int effect_numparams(effect_t effect);
+EXPORT eparam_t effect_getparambyidx(effect_t effect, size_t param);
+EXPORT eparam_t effect_getparambyname(effect_t effect, const char *name);
+EXPORT void effect_getparaminfo(effect_t effect, eparam_t param,
+		struct effect_param_info *info);
+
+/** used internally */
+EXPORT void effect_updateparams(effect_t effect);
+
+EXPORT eparam_t effect_getviewprojmatrix(effect_t effect);
+EXPORT eparam_t effect_getworldmatrix(effect_t effect);
+
+EXPORT void effect_setbool(effect_t effect, eparam_t param, bool val);
+EXPORT void effect_setfloat(effect_t effect, eparam_t param, float val);
+EXPORT void effect_setint(effect_t effect, eparam_t param, int val);
+EXPORT void effect_setmatrix3(effect_t effect, eparam_t param,
+		const struct matrix3 *val);
+EXPORT void effect_setmatrix4(effect_t effect, eparam_t param,
+		const struct matrix4 *val);
+EXPORT void effect_setvec2(effect_t effect, eparam_t param,
+		const struct vec2 *val);
+EXPORT void effect_setvec3(effect_t effect, eparam_t param,
+		const struct vec3 *val);
+EXPORT void effect_setvec4(effect_t effect, eparam_t param,
+		const struct vec4 *val);
+EXPORT void effect_settexture(effect_t effect, eparam_t param, texture_t val);
+EXPORT void effect_setval(effect_t shader, eparam_t param, const void *val,
+		size_t size);
+EXPORT void effect_setdefault(effect_t effect, eparam_t param);
+
+/* ---------------------------------------------------
+ * texture render helper functions
+ * --------------------------------------------------- */
+
+EXPORT texrender_t texrender_create(enum gs_color_format format,
+		enum gs_zstencil_format zsformat);
+EXPORT void texrender_destroy(texrender_t texrender);
+EXPORT bool texrender_begin(texrender_t texrender, int cx, int cy);
+EXPORT void texrender_end(texrender_t texrender);
+EXPORT void texrender_reset(texrender_t texrender);
+EXPORT texture_t texrender_gettexture(texrender_t texrender);
+
+/* ---------------------------------------------------
+ * graphics subsystem
+ * --------------------------------------------------- */
+
+#define GS_BUILDMIPMAPS (1<<0)
+#define GS_DYNAMIC      (1<<1)
+#define GS_RENDERTARGET (1<<2)
+
+/* ---------------- */
+/* global functions */
+
+#define GS_SUCCESS              0
+#define GS_ERROR_MODULENOTFOUND -1
+#define GS_ERROR_FAIL           -2
+
+struct gs_init_data {
+#if defined(_WIN32)
+	void                    *hwnd;
+#elif defined(__APPLE__)
+	/* TODO */
+#elif defined(__posix__)
+	/* TODO */
+#endif
+	uint32_t                cx, cy;
+	uint32_t                num_backbuffers;
+	enum gs_color_format    format;
+	enum gs_zstencil_format zsformat;
+	uint32_t                adapter;
+};
+
+
+EXPORT int gs_create(graphics_t *graphics, const char *module,
+		struct gs_init_data *data);
+EXPORT void gs_destroy(graphics_t graphics);
+
+EXPORT void gs_setcontext(graphics_t graphics);
+EXPORT graphics_t gs_getcontext(void);
+
+EXPORT void gs_matrix_push(void);
+EXPORT void gs_matrix_pop(void);
+EXPORT void gs_matrix_identity(void);
+EXPORT void gs_matrix_transpose(void);
+EXPORT void gs_matrix_set(const struct matrix3 *matrix);
+EXPORT void gs_matrix_get(struct matrix3 *dst);
+EXPORT void gs_matrix_mul(const struct matrix3 *matrix);
+EXPORT void gs_matrix_rotquat(const struct quat *rot);
+EXPORT void gs_matrix_rotaa(const struct axisang *rot);
+EXPORT void gs_matrix_translate(const struct vec3 *pos);
+EXPORT void gs_matrix_scale(const struct vec3 *scale);
+EXPORT void gs_matrix_rotaa4f(float x, float y, float z, float angle);
+EXPORT void gs_matrix_translate3f(float x, float y, float z);
+EXPORT void gs_matrix_scale3f(float x, float y, float z);
+
+EXPORT void gs_renderstart(bool b_new);
+EXPORT void gs_renderstop(enum gs_draw_mode mode);
+EXPORT vertbuffer_t gs_rendersave(void);
+EXPORT void gs_vertex2f(float x, float y);
+EXPORT void gs_vertex3f(float x, float y, float z);
+EXPORT void gs_normal3f(float x, float y, float z);
+EXPORT void gs_color(uint32_t color);
+EXPORT void gs_texcoord(float x, float y, int unit);
+EXPORT void gs_vertex2v(const struct vec2 *v);
+EXPORT void gs_vertex3v(const struct vec3 *v);
+EXPORT void gs_normal3v(const struct vec3 *v);
+EXPORT void gs_color4v(const struct vec4 *v);
+EXPORT void gs_texcoord2v(const struct vec2 *v, int unit);
+
+EXPORT input_t gs_getinput(void);
+EXPORT effect_t gs_geteffect(void);
+
+EXPORT effect_t gs_create_effect_from_file(const char *file,
+		char **error_string);
+EXPORT effect_t gs_create_effect(const char *effect_string,
+		const char *filename, char **error_string);
+
+EXPORT shader_t gs_create_vertexshader_from_file(const char *file,
+		char **error_string);
+EXPORT shader_t gs_create_pixelshader_from_file(const char *file,
+		char **error_string);
+
+EXPORT texture_t gs_create_texture_from_file(const char *file,
+		uint32_t flags);
+EXPORT texture_t gs_create_cubetexture_from_file(const char *flie,
+		uint32_t flags);
+EXPORT texture_t gs_create_volumetexture_from_file(const char *flie,
+		uint32_t flags);
+
+EXPORT void gs_draw_sprite(texture_t tex);
+
+EXPORT void gs_draw_cube_backdrop(texture_t cubetex, const struct quat *rot,
+		float left, float right, float top, float bottom, float znear);
+
+/** sets the viewport to current swap chain size */
+EXPORT void gs_resetviewport(void);
+
+/** sets default screen-sized orthographich mode */
+EXPORT void gs_set2dmode(void);
+/** sets default screen-sized perspective mode */
+EXPORT void gs_set3dmode(double fovy, double znear, double zvar);
+
+EXPORT void gs_viewport_push(void);
+EXPORT void gs_viewport_pop(void);
+
+EXPORT void texture_setimage(texture_t tex, const void *data,
+		uint32_t byte_width);
+EXPORT void cubetexture_setimage(texture_t cubetex, uint32_t side,
+		const void *data, uint32_t byte_width);
+
+/* -------------------------- */
+/* library-specific functions */
+
+EXPORT swapchain_t gs_create_swapchain(struct gs_init_data *data);
+
+EXPORT void gs_resize(uint32_t x, uint32_t y);
+EXPORT void gs_getsize(uint32_t *x, uint32_t *y);
+EXPORT uint32_t gs_getwidth(void);
+EXPORT uint32_t gs_getheight(void);
+
+EXPORT texture_t gs_create_texture(uint32_t width, uint32_t height,
+		enum gs_color_format color_format, void *data, uint32_t flags);
+EXPORT texture_t gs_create_cubetexture(uint32_t size,
+		enum gs_color_format color_format, void *data[6],
+		uint32_t flags);
+EXPORT texture_t gs_create_volumetexture(uint32_t width, uint32_t height,
+		uint32_t depth, enum gs_color_format color_format, void *data,
+		uint32_t flags);
+
+EXPORT zstencil_t gs_create_zstencil(uint32_t width, uint32_t height,
+		enum gs_zstencil_format format);
+
+EXPORT stagesurf_t gs_create_stagesurface(uint32_t width, uint32_t height,
+		enum gs_color_format color_format);
+
+EXPORT samplerstate_t gs_create_samplerstate(struct gs_sampler_info *info);
+
+EXPORT shader_t gs_create_vertexshader(const char *shader,
+		const char *file, char **error_string);
+EXPORT shader_t gs_create_pixelshader(const char *shader,
+		const char *file, char **error_string);
+
+EXPORT vertbuffer_t gs_create_vertexbuffer(struct vb_data *data,
+		uint32_t flags);
+EXPORT indexbuffer_t gs_create_indexbuffer(enum gs_index_type type,
+		void *indices, size_t num, uint32_t flags);
+
+EXPORT enum gs_texture_type gs_gettexturetype(texture_t texture);
+
+EXPORT void gs_load_vertexbuffer(vertbuffer_t vertbuffer);
+EXPORT void gs_load_indexbuffer(indexbuffer_t indexbuffer);
+EXPORT void gs_load_texture(texture_t tex, int unit);
+EXPORT void gs_load_samplerstate(samplerstate_t samplerstate, int unit);
+EXPORT void gs_load_vertexshader(shader_t vertshader);
+EXPORT void gs_load_pixelshader(shader_t pixelshader);
+
+EXPORT void gs_load_defaultsamplerstate(bool b_3d, int unit);
+
+EXPORT shader_t gs_getvertexshader(void);
+EXPORT shader_t gs_getpixelshader(void);
+
+EXPORT texture_t  gs_getrendertarget(void);
+EXPORT zstencil_t gs_getzstenciltarget(void);
+
+EXPORT void gs_setrendertarget(texture_t tex, zstencil_t zstencil);
+EXPORT void gs_setcuberendertarget(texture_t cubetex, int side,
+		zstencil_t zstencil);
+
+EXPORT void gs_copy_texture(texture_t dst, texture_t src);
+EXPORT void gs_stage_texture(stagesurf_t dst, texture_t src);
+
+EXPORT void gs_beginscene(void);
+EXPORT void gs_draw(enum gs_draw_mode draw_mode, uint32_t start_vert,
+		uint32_t num_verts);
+EXPORT void gs_endscene(void);
+
+#define GS_CLEAR_COLOR   (1<<0)
+#define GS_CLEAR_DEPTH   (1<<1)
+#define GS_CLEAR_STENCIL (1<<2)
+
+EXPORT void gs_load_swapchain(swapchain_t swapchain);
+EXPORT void gs_clear(uint32_t clear_flags, struct vec4 *color,
+		float depth, uint8_t stencil);
+EXPORT void gs_present(void);
+
+EXPORT void gs_setcullmode(enum gs_cull_mode mode);
+EXPORT enum gs_cull_mode gs_getcullmode(void);
+
+EXPORT void gs_enable_blending(bool enable);
+EXPORT void gs_enable_depthtest(bool enable);
+EXPORT void gs_enable_stenciltest(bool enable);
+EXPORT void gs_enable_stencilwrite(bool enable);
+EXPORT void gs_enable_color(bool red, bool blue, bool green, bool alpha);
+
+EXPORT void gs_blendfunction(enum gs_blend_type src, enum gs_blend_type dest);
+EXPORT void gs_depthfunction(enum gs_depth_test test);
+
+EXPORT void gs_stencilfunction(enum gs_stencil_side side,
+		enum gs_depth_test test);
+EXPORT void gs_stencilop(enum gs_stencil_side side, enum gs_stencil_op fail,
+		enum gs_stencil_op zfail, enum gs_stencil_op zpass);
+
+EXPORT void gs_setclip(struct plane *p);
+
+EXPORT void gs_enable_fullscreen(bool enable);
+EXPORT int gs_fullscreen_enabled(void);
+EXPORT void gs_setdisplaymode(const struct gs_display_mode *mode);
+EXPORT void gs_getdisplaymode(struct gs_display_mode *mode);
+EXPORT void gs_setcolorramp(float gamma, float brightness, float contrast);
+
+EXPORT void gs_setviewport(int x, int y, int width, int height);
+EXPORT void gs_getviewport(struct gs_rect *rect);
+EXPORT void gs_setscissorrect(struct gs_rect *rect);
+
+EXPORT void gs_ortho(float left, float right, float top, float bottom,
+		float znear, float zfar);
+EXPORT void gs_frustum(float left, float right, float top, float bottom,
+		float znear, float zfar);
+EXPORT void gs_perspective(float fovy, float aspect, float znear,
+		float zfar);
+
+EXPORT void gs_projection_push(void);
+EXPORT void gs_projection_pop(void);
+
+EXPORT void     swapchain_destroy(swapchain_t swapchain);
+
+EXPORT void     texture_destroy(texture_t tex);
+EXPORT uint32_t texture_getwidth(texture_t tex);
+EXPORT uint32_t texture_getheight(texture_t tex);
+EXPORT enum gs_color_format texture_getcolorformat(texture_t tex);
+EXPORT bool     texture_map(texture_t tex, void **ptr, uint32_t *byte_width);
+EXPORT void     texture_unmap(texture_t tex);
+
+EXPORT void     cubetexture_destroy(texture_t cubetex);
+EXPORT uint32_t cubetexture_getsize(texture_t cubetex);
+EXPORT enum gs_color_format cubetexture_getcolorformat(texture_t cubetex);
+
+EXPORT void     volumetexture_destroy(texture_t voltex);
+EXPORT uint32_t volumetexture_getwidth(texture_t voltex);
+EXPORT uint32_t volumetexture_getheight(texture_t voltex);
+EXPORT uint32_t volumetexture_getdepth(texture_t voltex);
+EXPORT enum gs_color_format volumetexture_getcolorformat(texture_t voltex);
+
+EXPORT void     stagesurface_destroy(stagesurf_t stagesurf);
+EXPORT uint32_t stagesurface_getwidth(stagesurf_t stagesurf);
+EXPORT uint32_t stagesurface_getheight(stagesurf_t stagesurf);
+EXPORT enum gs_color_format stagesurface_getcolorformat(stagesurf_t stagesurf);
+EXPORT bool     stagesurface_map(stagesurf_t stagesurf, const void **data,
+		uint32_t *byte_width);
+EXPORT void     stagesurface_unmap(stagesurf_t stagesurf);
+
+EXPORT void     zstencil_destroy(zstencil_t zstencil);
+
+EXPORT void     samplerstate_destroy(samplerstate_t samplerstate);
+
+EXPORT void     vertexbuffer_destroy(vertbuffer_t vertbuffer);
+EXPORT void     vertexbuffer_flush(vertbuffer_t vertbuffer, bool rebuild);
+EXPORT struct vb_data *vertexbuffer_getdata(vertbuffer_t vertbuffer);
+
+EXPORT void     indexbuffer_destroy(indexbuffer_t indexbuffer);
+EXPORT void     indexbuffer_flush(indexbuffer_t indexbuffer);
+EXPORT void     *indexbuffer_getdata(indexbuffer_t indexbuffer);
+EXPORT size_t   indexbuffer_numindices(indexbuffer_t indexbuffer);
+EXPORT enum gs_index_type indexbuffer_gettype(indexbuffer_t indexbuffer);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 155 - 0
libobs/graphics/input.h

@@ -0,0 +1,155 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef INPUT_H
+#define INPUT_H
+
+/* TODO: incomplete/may not be necessary */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define KBC_ESCAPE          0x0
+#define KBC_1               0x1
+#define KBC_2               0x2
+#define KBC_3               0x3
+#define KBC_4               0x4
+#define KBC_5               0x5
+#define KBC_6               0x6
+#define KBC_7               0x7
+#define KBC_8               0x8
+#define KBC_9               0x9
+#define KBC_0               0xA
+#define KBC_MINUS           0xB
+#define KBC_EQUALS          0xC
+#define KBC_BACK            0xD
+#define KBC_TAB             0xE
+#define KBC_Q               0xF
+#define KBC_W               0x10
+#define KBC_E               0x11
+#define KBC_R               0x12
+#define KBC_T               0x13
+#define KBC_Y               0x14
+#define KBC_U               0x15
+#define KBC_I               0x16
+#define KBC_O               0x17
+#define KBC_P               0x18
+#define KBC_LBRACKET        0x19
+#define KBC_RBRACKET        0x1A
+#define KBC_RETURN          0x1B
+#define KBC_LCONTROL        0x1C
+#define KBC_A               0x1D
+#define KBC_S               0x1E
+#define KBC_D               0x1F
+#define KBC_F               0x20
+#define KBC_G               0x21
+#define KBC_H               0x22
+#define KBC_J               0x23
+#define KBC_K               0x24
+#define KBC_L               0x25
+#define KBC_SEMICOLON       0x26
+#define KBC_APOSTROPHE      0x27
+#define KBC_TILDE           0x28
+#define KBC_LSHIFT          0x29
+#define KBC_BACKSLASH       0x2A
+#define KBC_Z               0x2B
+#define KBC_X               0x2C
+#define KBC_C               0x2D
+#define KBC_V               0x2E
+#define KBC_B               0x2F
+#define KBC_N               0x30
+#define KBC_M               0x31
+#define KBC_COMMA           0x32
+#define KBC_PERIOD          0x33
+#define KBC_SLASH           0x34
+#define KBC_RSHIFT          0x35
+#define KBC_MULTIPLY        0x36
+#define KBC_LALT            0x37
+#define KBC_SPACE           0x38
+#define KBC_CAPSLOCK        0x39
+#define KBC_F1              0x3A
+#define KBC_F2              0x3B
+#define KBC_F3              0x3C
+#define KBC_F4              0x3D
+#define KBC_F5              0x3E
+#define KBC_F6              0x3F
+#define KBC_F7              0x40
+#define KBC_F8              0x41
+#define KBC_F9              0x42
+#define KBC_F10             0x43
+#define KBC_NUMLOCK         0x44
+#define KBC_SCROLLLOCK      0x45
+#define KBC_NUMPAD7         0x46
+#define KBC_NUMPAD8         0x47
+#define KBC_NUMPAD9         0x48
+#define KBC_SUBTRACT        0x49
+#define KBC_NUMPAD4         0x4A
+#define KBC_NUMPAD5         0x4B
+#define KBC_NUMPAD6         0x4C
+#define KBC_ADD             0x4D
+#define KBC_NUMPAD1         0x4E
+#define KBC_NUMPAD2         0x4F
+#define KBC_NUMPAD3         0x50
+#define KBC_NUMPAD0         0x51
+#define KBC_DECIMAL         0x52
+#define KBC_F11             0x53
+#define KBC_F12             0x54
+#define KBC_NUMPADENTER     0x55
+#define KBC_RCONTROL        0x56
+#define KBC_DIVIDE          0x57
+#define KBC_SYSRQ           0x58
+#define KBC_RALT            0x59
+#define KBC_PAUSE           0x5A
+#define KBC_HOME            0x5B
+#define KBC_UP              0x5C
+#define KBC_PAGEDOWN        0x5D
+#define KBC_LEFT            0x5E
+#define KBC_RIGHT           0x5F
+#define KBC_END             0x60
+#define KBC_DOWN            0x61
+#define KBC_PAGEUP          0x62
+#define KBC_INSERT          0x63
+#define KBC_DELETE          0x64
+
+#define MOUSE_LEFTBUTTON    0x65
+#define MOUSE_MIDDLEBUTTON  0x66
+#define MOUSE_RIGHTBUTTON   0x67
+#define MOUSE_WHEEL         0x68
+#define MOUSE_MOVE          0x69
+
+#define KBC_CONTROL         0xFFFFFFFE
+#define KBC_ALT             0xFFFFFFFD
+#define KBC_SHIFT           0xFFFFFFFC
+
+#define STATE_LBUTTONDOWN   (1<<0)
+#define STATE_RBUTTONDOWN   (1<<1)
+#define STATE_MBUTTONDOWN   (1<<2)
+#define STATE_X4BUTTONDOWN  (1<<3)
+#define STATE_X5BUTTONDOWN  (1<<4)
+
+/* wrapped opaque data types */
+struct input_subsystem;
+typedef struct input_subsystem* input_t;
+
+EXPORT int input_getbuttonstate(input_t input, uint32_t button);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 48 - 0
libobs/graphics/math-defs.h

@@ -0,0 +1,48 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef MATH_DEFS
+#define MATH_DEFS
+
+#include "../util/c99defs.h"
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef M_PI
+#define M_PI            3.1415926535897932384626433832795f
+#endif
+
+#define RAD(val)        ((val)*0.0174532925199432957692369076848f)
+#define DEG(val)        ((val)*57.295779513082320876798154814105f)
+#define LARGE_EPSILON   1e-2f
+#define EPSILON         1e-4f
+#define TINY_EPSILON    1e-5f
+#define M_INFINITE      3.4e38f
+
+static inline bool close_float(float f1, float f2, float precision)
+{
+	return fabsf(f1-f2) <= precision;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 132 - 0
libobs/graphics/math-extra.c

@@ -0,0 +1,132 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <stdlib.h>
+#include "vec2.h"
+#include "vec3.h"
+#include "math-extra.h"
+
+void polar_to_cart(struct vec3 *dst, const struct vec3 *v)
+{
+	struct vec3 cart;
+	float  sinx = cosf(v->x);
+	float  sinx_z = v->z * sinx;
+
+	cart.x = sinx_z * sinf(v->y);
+	cart.z = sinx_z * cosf(v->y);
+	cart.y = v->z   * sinf(v->x);
+
+	vec3_copy(dst, &cart);
+}
+
+void cart_to_polar(struct vec3 *dst, const struct vec3 *v)
+{
+	struct vec3 polar;
+	polar.z = vec3_len(v);
+
+	if (close_float(polar.z, 0.0f, EPSILON)) {
+		vec3_zero(&polar);
+	} else {
+		polar.x = asinf(v->y / polar.z);
+		polar.y = atan2f(v->x, v->z);
+	}
+
+	vec3_copy(dst, &polar);
+}
+
+void norm_to_polar(struct vec2 *dst, const struct vec3 *norm)
+{
+	dst->x = atan2f(norm->x, norm->z);
+	dst->y = asinf(norm->y);
+}
+
+void polar_to_norm(struct vec3 *dst, const struct vec2 *polar)
+{
+	float sinx = sinf(polar->x);
+
+	dst->x = sinx * cosf(polar->y);
+	dst->y = sinx * sinf(polar->y);
+	dst->z = cosf(polar->x);
+}
+
+float calc_torquef(float val1, float val2, float torque, float min_adjust,
+		float t)
+{
+	float out = val1;
+	float dist;
+	bool  over;
+
+	if (val1 == val2)
+		return val1;
+
+	dist = (val2-val1)*torque;
+	over = dist > 0.0f;
+
+	if (over) {
+		if (dist < min_adjust) /* prevents from going too slow */
+			dist = min_adjust;
+		out += dist*t;         /* add torque */
+		if (out > val2)        /* clamp if overshoot */
+			out = val2;
+	} else {
+		if (dist > -min_adjust)
+			dist = -min_adjust;
+		out += dist*t;
+		if (out < val2)
+			out = val2;
+	}
+
+	return out;
+}
+
+void calc_torque(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2, float torque, float min_adjust,
+		float t)
+{
+	struct vec3 line, dir;
+	float orig_dist, torque_dist, adjust_dist;
+
+	if (vec3_close(v1, v2, EPSILON)) {
+		vec3_copy(dst, v1);
+		return;
+	}
+
+	vec3_sub(&line, v2, v1);
+	orig_dist = vec3_len(&line);
+	vec3_mulf(&dir, &line, 1.0f/orig_dist);
+
+	torque_dist = orig_dist*torque; /* use distance to determine speed */
+	adjust_dist = torque_dist*t;
+
+	if (torque_dist < min_adjust)   /* prevent from going too slow */
+		torque_dist = min_adjust;
+
+	if (adjust_dist <= (orig_dist-LARGE_EPSILON)) {
+		vec3_mulf(dst, &dir, adjust_dist);
+		vec3_add(dst, dst, v1); /* add torque */
+	} else {
+		vec3_copy(dst, v2);     /* clamp if overshoot */ 
+	}
+}
+
+float rand_float(int positive_only)
+{
+	if (positive_only)
+		return (float)((double)rand()/(double)RAND_MAX);
+	else
+		return (float)(((double)rand()/(double)RAND_MAX*2.0)-1.0);
+}

+ 66 - 0
libobs/graphics/math-extra.h

@@ -0,0 +1,66 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef MATH_EXTRA_H
+#define MATH_EXTRA_H
+
+#include "../util/c99defs.h"
+
+/*
+ * A few general math functions that I couldn't really decide where to put.
+ *
+ *   Polar/Cart conversion, torque functions (for smooth movement), percentage,
+ * random floats.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct vec2;
+struct vec3;
+
+EXPORT void polar_to_cart(struct vec3 *dst, const struct vec3 *v);
+EXPORT void cart_to_polar(struct vec3 *dst, const struct vec3 *v);
+
+EXPORT void norm_to_polar(struct vec2 *dst, const struct vec3 *norm);
+EXPORT void polar_to_norm(struct vec3 *dst, const struct vec2 *polar);
+
+EXPORT float calc_torquef(float val1, float val2, float torque,
+		float min_adjust, float t);
+
+EXPORT void calc_torque(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2, float torque, float min_adjust,
+		float t);
+
+static inline float get_percentage(float start, float end, float mid)
+{
+	return (mid-start) / (end-start);
+}
+
+static inline float get_percentagei(int start, int end, int mid)
+{
+	return (float)(mid-start) / (float)(end-start);
+}
+
+EXPORT float rand_float(int positive_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 145 - 0
libobs/graphics/matrix3.c

@@ -0,0 +1,145 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <string.h>
+#include "matrix3.h"
+#include "matrix4.h"
+#include "plane.h"
+#include "quat.h"
+
+
+void matrix3_from_quat(struct matrix3 *dst, const struct quat *q)
+{
+	float norm = quat_dot(q, q);
+	float s = (norm > 0.0f) ? (2.0f/norm) : 0.0f;
+
+	float xx = q->x * q->x * s;
+	float yy = q->y * q->y * s;
+	float zz = q->z * q->z * s;
+	float xy = q->x * q->y * s;
+	float xz = q->x * q->z * s;
+	float yz = q->y * q->z * s;
+	float wx = q->w * q->x * s;
+	float wy = q->w * q->y * s;
+	float wz = q->w * q->z * s;
+
+	dst->x.x = 1.0f - (yy + zz);
+	dst->x.y = xy + wz;
+	dst->x.z = xz - wy;
+	dst->x.w = 0.0f;
+
+	dst->y.x = xy - wz;
+	dst->y.y = 1.0f - (xx + zz);
+	dst->y.z = yz + wx;
+	dst->y.w = 0.0f;
+
+	dst->z.x = xz + wy;
+	dst->z.y = yz - wx;
+	dst->z.z = 1.0f - (xx + yy);
+	dst->z.w = 0.0f;
+
+	vec3_zero(&dst->t);
+}
+
+void matrix3_from_axisang(struct matrix3 *dst, const struct axisang *aa)
+{
+	struct quat q;
+	quat_from_axisang(&q, aa);
+	matrix3_from_quat(dst, &q);
+}
+
+void matrix3_from_matrix4(struct matrix3 *dst, const struct matrix4 *m)
+{
+	dst->x.m = m->x.m;
+	dst->y.m = m->y.m;
+	dst->z.m = m->z.m;
+	dst->t.m = m->t.m;
+	dst->x.w = 0.0f;
+	dst->y.w = 0.0f;
+	dst->z.w = 0.0f;
+	dst->t.w = 0.0f;
+}
+
+void matrix3_mul(struct matrix3 *dst, const struct matrix3 *m1,
+		const struct matrix3 *m2)
+{
+	vec3_rotate(&dst->x, &m1->x, m2);
+	vec3_rotate(&dst->y, &m1->y, m2);
+	vec3_rotate(&dst->z, &m1->z, m2);
+	vec3_transform(&dst->t, &m1->t, m2);
+}
+
+void matrix3_rotate(struct matrix3 *dst, const struct matrix3 *m,
+		const struct quat *q)
+{
+	struct matrix3 temp;
+	matrix3_from_quat(&temp, q);
+	matrix3_mul(dst, m, &temp);
+}
+
+void matrix3_rotate_aa(struct matrix3 *dst, const struct matrix3 *m,
+		const struct axisang *aa)
+{
+	struct matrix3 temp;
+	matrix3_from_axisang(&temp, aa);
+	matrix3_mul(dst, m, &temp);
+}
+
+void matrix3_scale(struct matrix3 *dst, const struct matrix3 *m,
+		const struct vec3 *v)
+{
+	vec3_mul(&dst->x, &m->x, v);
+	vec3_mul(&dst->y, &m->y, v);
+	vec3_mul(&dst->z, &m->z, v);
+	vec3_mul(&dst->t, &m->t, v);
+}
+
+void matrix3_transpose(struct matrix3 *dst, const struct matrix3 *m)
+{
+	__m128 tmp1, tmp2;
+	vec3_transform(&dst->t, &m->t, m);
+	vec3_neg(&dst->t, &dst->t);
+
+        tmp1 = _mm_movelh_ps(m->x.m, m->y.m);
+        tmp2 = _mm_movehl_ps(m->y.m, m->x.m);
+        dst->x.m = _mm_shuffle_ps(tmp1, m->z.m, _MM_SHUFFLE(3, 0, 2, 0));
+        dst->y.m = _mm_shuffle_ps(tmp1, m->z.m, _MM_SHUFFLE(3, 1, 3, 1));
+        dst->z.m = _mm_shuffle_ps(tmp2, m->z.m, _MM_SHUFFLE(3, 2, 2, 0));
+}
+
+void matrix3_inv(struct matrix3 *dst, const struct matrix3 *m)
+{
+	matrix4_inv((struct matrix4*)dst, (struct matrix4*)m);
+}
+
+void matrix3_mirror(struct matrix3 *dst, const struct matrix3 *m,
+	const struct plane *p)
+{
+	vec3_mirrorv(&dst->x, &m->x, &p->dir);
+	vec3_mirrorv(&dst->y, &m->y, &p->dir);
+	vec3_mirrorv(&dst->z, &m->z, &p->dir);
+	vec3_mirror(&dst->t, &m->t, p);
+}
+
+void matrix3_mirrorv(struct matrix3 *dst, const struct matrix3 *m,
+       const struct vec3 *v)
+{
+	vec3_mirrorv(&dst->x, &m->x, v);
+	vec3_mirrorv(&dst->y, &m->y, v);
+	vec3_mirrorv(&dst->z, &m->z, v);
+	vec3_mirrorv(&dst->t, &m->t, v);
+}

+ 87 - 0
libobs/graphics/matrix3.h

@@ -0,0 +1,87 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef MATRIX_H
+#define MATRIX_H
+
+#include "vec3.h"
+
+/* 3x4 Matrix */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct axisang;
+struct matrix4;
+
+struct matrix3 {
+	struct vec3 x;
+	struct vec3 y;
+	struct vec3 z;
+	struct vec3 t;
+};
+
+static inline void matrix3_copy(struct matrix3 *dst, const struct matrix3 *m)
+{
+	vec3_copy(&dst->x, &m->x);
+	vec3_copy(&dst->y, &m->y);
+	vec3_copy(&dst->z, &m->z);
+	vec3_copy(&dst->t, &m->t);
+}
+
+static inline void matrix3_identity(struct matrix3 *dst)
+{
+	vec3_zero(&dst->x);
+	vec3_zero(&dst->y);
+	vec3_zero(&dst->z);
+	vec3_zero(&dst->t);
+	dst->x.x = dst->y.y = dst->z.z = 1.0f;
+}
+
+EXPORT void matrix3_from_quat(struct matrix3 *dst, const struct quat *q);
+EXPORT void matrix3_from_axisang(struct matrix3 *dst,
+		const struct axisang *aa);
+EXPORT void matrix3_from_matrix4(struct matrix3 *dst, const struct matrix4 *m);
+
+EXPORT void matrix3_mul(struct matrix3 *dst, const struct matrix3 *m1,
+		const struct matrix3 *m2);
+static inline void matrix3_translate(struct matrix3 *dst,
+		const struct matrix3 *m, const struct vec3 *v)
+{
+	vec3_sub(&dst->t, &m->t, v);
+}
+
+EXPORT void matrix3_rotate(struct matrix3 *dst, const struct matrix3 *m,
+		const struct quat *q);
+EXPORT void matrix3_rotate_aa(struct matrix3 *dst, const struct matrix3 *m,
+		const struct axisang *aa);
+EXPORT void matrix3_scale(struct matrix3 *dst, const struct matrix3 *m,
+		const struct vec3 *v);
+EXPORT void matrix3_transpose(struct matrix3 *dst, const struct matrix3 *m);
+EXPORT void matrix3_inv(struct matrix3 *dst, const struct matrix3 *m);
+
+EXPORT void matrix3_mirror(struct matrix3 *dst, const struct matrix3 *m,
+		const struct plane *p);
+EXPORT void matrix3_mirrorv(struct matrix3 *dst, const struct matrix3 *m,
+		const struct vec3 *v);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 209 - 0
libobs/graphics/matrix4.c

@@ -0,0 +1,209 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "math-defs.h"
+#include "matrix4.h"
+#include "matrix3.h"
+
+void matrix4_from_matrix3(struct matrix4 *dst, const struct matrix3 *m)
+{
+	dst->x.m = m->x.m;
+	dst->y.m = m->y.m;
+	dst->z.m = m->z.m;
+	dst->t.m = m->t.m;
+	dst->t.w = 1.0f;
+}
+
+void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1,
+		const struct matrix4 *m2)
+{
+	const struct vec4 *m1v = (const struct vec4*)m1;
+	const float *m2f = (const float*)m2;
+	struct vec4 out[4];
+	int i, j;
+
+	for (i = 0; i < 4; i++) {
+		for (j=0; j<4; j++) {
+			struct vec4 temp;
+			vec4_set(&temp, m2f[j], m2f[j+4], m2f[j+8], m2f[j+12]);
+			out[i].ptr[j] = vec4_dot(&m1v[i], &temp);
+		}
+	}
+
+	matrix4_copy(dst, (struct matrix4*)out);
+}
+
+static inline void get_3x3_submatrix(float *dst, const struct matrix4 *m,
+		int i, int j)
+{
+	const float *mf = (const float *)m;
+	int ti, tj, idst, jdst;
+
+	for (ti = 0; ti < 4; ti++) {
+		if (ti < i)
+			idst = ti;
+		else if (ti > i)
+			idst = ti-1;
+		else
+			continue;
+
+		for (tj = 0; tj < 4; tj++) {
+			if (tj < j)
+				jdst = tj;
+			else if (tj > j)
+				jdst = tj-1;
+			else
+				continue;
+
+			dst[(idst*3) + jdst] = mf[(ti*4) + tj];
+		}
+	}
+}
+
+static inline float get_3x3_determinant(const float *m)
+{
+	return (m[0] * ((m[4]*m[8]) - (m[7]*m[5]))) -
+	       (m[1] * ((m[3]*m[8]) - (m[6]*m[5]))) +
+	       (m[2] * ((m[3]*m[7]) - (m[6]*m[4])));
+}
+
+float matrix4_determinant(const struct matrix4 *m)
+{
+	const float *mf = (const float *)m;
+	float det, result = 0.0f, i = 1.0f;
+	float m3x3[9];
+	int n;
+
+	for (n = 0; n < 4; n++, i *= -1.0f) {
+		get_3x3_submatrix(m3x3, m, 0, n);
+
+		det     = get_3x3_determinant(m3x3);
+		result += mf[n] * det * i;
+	}
+
+	return result;
+}
+
+bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m)
+{
+	struct vec4 *dstv = (struct vec4 *)dst;
+	float det = matrix4_determinant(m);
+	float m3x3[9];
+	int   i, j, sign;
+
+	if (fabs(det) < 0.0005f)
+		return false;
+
+	for (i = 0; i < 4; i++) {
+		for (j = 0; j < 4; j++) {
+			sign = 1 - ((i+j) % 2) * 2;
+			get_3x3_submatrix(m3x3, m, i, j);
+			dstv[j].ptr[i] = get_3x3_determinant(m3x3) *
+			                  (float)sign / det;
+		}
+	}
+
+	return true;
+}
+
+void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m)
+{
+	struct matrix4 temp;
+
+	/* TODO: Use SSE */
+	temp.x.x = m->x.x;
+	temp.x.y = m->y.x;
+	temp.x.z = m->z.x;
+	temp.x.w = m->t.x;
+	temp.y.x = m->x.y;
+	temp.y.y = m->y.y;
+	temp.y.z = m->z.y;
+	temp.y.w = m->t.y;
+	temp.z.x = m->x.z;
+	temp.z.y = m->y.z;
+	temp.z.z = m->z.z;
+	temp.z.w = m->t.z;
+	temp.t.x = m->x.w;
+	temp.t.y = m->y.w;
+	temp.t.z = m->z.w;
+	temp.t.w = m->t.w;
+
+	matrix4_copy(dst, &temp);
+}
+
+void matrix4_ortho(struct matrix4 *dst, float left, float right,
+		float top, float bottom, float near, float far)
+{
+	float rml = right-left;
+	float bmt = bottom-top;
+	float fmn = far-near;
+
+	vec4_zero(&dst->x);
+	vec4_zero(&dst->y);
+	vec4_zero(&dst->z);
+	vec4_zero(&dst->t);
+
+	dst->x.x =         2.0f /  rml;
+	dst->t.x = (left+right) / -rml;
+
+	dst->y.y =         2.0f / -bmt;
+	dst->t.y = (bottom+top) /  bmt;
+
+	dst->z.z =         1.0f /  fmn;
+	dst->t.z =         near / -fmn;
+
+	dst->t.w = 1.0f;
+}
+
+void matrix4_frustum(struct matrix4 *dst, float left, float right,
+		float top, float bottom, float near, float far)
+{
+	float rml    = right-left;
+	float bmt    = bottom-top;
+	float fmn    = far-near;
+	float nearx2 = 2.0f*near;
+
+	vec4_zero(&dst->x);
+	vec4_zero(&dst->y);
+	vec4_zero(&dst->z);
+	vec4_zero(&dst->t);
+
+	dst->x.x =       nearx2 /  rml;
+	dst->z.x = (left+right) / -rml;
+
+	dst->y.y =       nearx2 / -bmt;
+	dst->z.y = (bottom+top) /  bmt;
+
+	dst->z.z =          far /  fmn;
+	dst->t.z =   (near*far) / -fmn;
+
+	dst->z.w = 1.0f;
+}
+
+void matrix4_perspective(struct matrix4 *dst, float angle,
+		float aspect, float near, float far)
+{
+	float xmin, xmax, ymin, ymax;
+
+	ymax = near * tanf(RAD(angle)*0.5f);
+	ymin = -ymax;
+
+	xmin = ymin * aspect;
+	xmax = ymax * aspect;
+
+	matrix4_frustum(dst, xmin, xmax, ymin, ymax, near, far);
+}

+ 76 - 0
libobs/graphics/matrix4.h

@@ -0,0 +1,76 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef MATRIX4_H
+#define MATRIX4_H
+
+#include "vec4.h"
+
+/* 4x4 Matrix */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct matrix3;
+
+struct matrix4 {
+	struct vec4 x, y, z, t;
+};
+
+static inline void matrix4_copy(struct matrix4 *dst, const struct matrix4 *m)
+{
+	dst->x.m = m->x.m;
+	dst->y.m = m->y.m;
+	dst->z.m = m->z.m;
+	dst->t.m = m->t.m;
+}
+
+static inline void matrix4_identity(struct matrix4 *dst)
+{
+	vec4_zero(&dst->x);
+	vec4_zero(&dst->y);
+	vec4_zero(&dst->z);
+	vec4_zero(&dst->t);
+	dst->x.x = 1.0f;
+	dst->y.y = 1.0f;
+	dst->z.z = 1.0f;
+	dst->t.w = 1.0f;
+}
+
+EXPORT void matrix4_from_matrix3(struct matrix4 *dst, const struct matrix3 *m);
+
+EXPORT void matrix4_mul(struct matrix4 *dst, const struct matrix4 *m1,
+		const struct matrix4 *m2);
+
+EXPORT float matrix4_determinant(const struct matrix4 *m);
+
+EXPORT bool matrix4_inv(struct matrix4 *dst, const struct matrix4 *m);
+EXPORT void matrix4_transpose(struct matrix4 *dst, const struct matrix4 *m);
+
+EXPORT void matrix4_ortho(struct matrix4 *dst, float left, float right,
+		float top, float bottom, float near, float far);
+EXPORT void matrix4_frustum(struct matrix4 *dst, float left, float right,
+		float top, float bottom, float near, float far);
+EXPORT void matrix4_perspective(struct matrix4 *dst, float angle,
+		float aspect, float near, float far);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 148 - 0
libobs/graphics/plane.c

@@ -0,0 +1,148 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "../util/c99defs.h"
+#include "matrix3.h"
+#include "plane.h"
+
+void plane_from_tri(struct plane *dst,
+                    const struct vec3 *v1,
+                    const struct vec3 *v2,
+                    const struct vec3 *v3)
+{
+	struct vec3 temp;
+
+	vec3_sub(&temp, v2, v1);
+	vec3_sub(&dst->dir, v3, v1);
+	vec3_cross(&dst->dir, &temp, &dst->dir);
+	vec3_norm(&dst->dir, &dst->dir);
+	dst->dist = vec3_dot(v1, &dst->dir);
+}
+
+void plane_transform(struct plane *dst, const struct plane *p,
+		const struct matrix3 *m)
+{
+	struct vec3 temp;
+
+	vec3_transform(&dst->dir, &p->dir, m);
+	vec3_norm(&dst->dir, &dst->dir);
+
+	vec3_transform(&temp, &m->t, m);
+	dst->dist = p->dist - vec3_dot(&dst->dir, &temp);
+}
+
+bool plane_intersection_ray(const struct plane *p, const struct vec3 *orig,
+		const struct vec3 *dir, float *t)
+{
+	float c = vec3_dot(&p->dir, dir);
+
+	if (fabsf(c) < EPSILON) {
+		*t = 0.0f;
+		return false;
+	} else {
+		*t = (p->dist - vec3_dot(&p->dir, orig)) / c;
+		return true;
+	}
+}
+
+bool plane_intersection_line(const struct plane *p, const struct vec3 *v1,
+		const struct vec3 *v2, float *t)
+{
+	float p1_dist, p2_dist, p1_abs_dist, dist2;
+	bool  p1_over, p2_over;
+
+	p1_dist = vec3_plane_dist(v1, p);
+	p2_dist = vec3_plane_dist(v2, p);
+
+	if (close_float(p1_dist, 0.0f, EPSILON)) {
+		if (close_float(p2_dist, 0.0f, EPSILON))
+			return false;
+
+		*t = 0.0f;
+		return true;
+	} else if (close_float(p2_dist, 0.0f, EPSILON)) {
+		*t = 1.0f;
+		return true;
+	}
+
+	p1_over = (p1_dist > 0.0f);
+	p2_over = (p2_dist > 0.0f);
+
+	if (p1_over == p2_over)
+		return false;
+
+	p1_abs_dist = fabsf(p1_dist);
+	dist2 = p1_abs_dist + fabsf(p2_dist);
+	if (dist2 < EPSILON)
+		return false;
+
+	*t = p1_abs_dist / dist2;
+	return true;
+}
+
+bool plane_tri_inside(const struct plane *p,
+                     const struct vec3 *v1,
+                     const struct vec3 *v2,
+                     const struct vec3 *v3,
+                     float precision)
+{
+	/* bit 1: part or all is behind the plane      */
+	/* bit 2: part or all is in front of the plane */
+	int sides = 0;
+	float d1 = vec3_plane_dist(v1, p);
+	float d2 = vec3_plane_dist(v2, p);
+	float d3 = vec3_plane_dist(v3, p);
+
+	if (d1 >= precision)
+		sides = 2;
+	else if (d1 <= -precision)
+		sides = 1;
+
+	if (d2 >= precision)
+		sides |= 2;
+	else if (d2 <= -precision)
+		sides |= 1;
+
+	if (d3 >= precision)
+		sides |= 2;
+	else if (d3 <= -precision)
+		sides |= 1;
+
+	return sides;
+}
+
+bool plane_line_inside(const struct plane *p, const struct vec3 *v1,
+		const struct vec3 *v2, float precision)
+{
+	/* bit 1: part or all is behind the plane      */
+	/* bit 2: part or all is in front of the plane */
+	int sides = 0;
+	float d1 = vec3_plane_dist(v1, p);
+	float d2 = vec3_plane_dist(v2, p);
+
+	if (d1 >= precision)
+		sides = 2;
+	else if (d1 <= -precision)
+		sides = 1;
+
+	if (d2 >= precision)
+		sides |= 2;
+	else if (d2 <= -precision)
+		sides |= 1;
+
+	return sides;
+}

+ 101 - 0
libobs/graphics/plane.h

@@ -0,0 +1,101 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef PLANE_H
+#define PLANE_H
+
+#include "math-defs.h"
+#include "vec3.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct matrix3;
+
+struct plane {
+	struct vec3 dir;
+	float       dist;
+};
+
+static inline void plane_copy(struct plane *dst, const struct plane *p)
+{
+	vec3_copy(&dst->dir, &p->dir);
+	dst->dist = p->dist;
+}
+
+static inline void plane_set(struct plane *dst, const struct vec3 *dir,
+		float dist)
+{
+	vec3_copy(&dst->dir, dir);
+	dst->dist = dist;
+}
+
+static inline void plane_setf(struct plane *dst, float a, float b, float c,
+		float d)
+{
+	vec3_set(&dst->dir, a, b, c);
+	dst->dist = d;
+}
+
+EXPORT void plane_from_tri(struct plane *dst,
+                           const struct vec3 *v1,
+                           const struct vec3 *v2,
+                           const struct vec3 *v3);
+
+EXPORT void plane_transform(struct plane *dst, const struct plane *p,
+		const struct matrix3 *m);
+
+EXPORT bool plane_intersection_ray(const struct plane *p,
+		const struct vec3 *orig, const struct vec3 *dir, float *t);
+EXPORT bool plane_intersection_line(const struct plane *p,
+		const struct vec3 *v1, const struct vec3 *v2, float *t);
+
+EXPORT bool plane_tri_inside(const struct plane *p,
+                            const struct vec3 *v1,
+                            const struct vec3 *v2,
+                            const struct vec3 *v3,
+                            float precision);
+
+EXPORT bool plane_line_inside(const struct plane *p, const struct vec3 *v1,
+		const struct vec3 *v2, float precision);
+
+static inline bool plane_close(const struct plane *p1, const struct plane *p2,
+		float precision)
+{
+	return vec3_close(&p1->dir, &p2->dir, precision) &&
+	       close_float(p1->dist, p2->dist, precision);
+}
+
+static inline bool plane_coplanar(const struct plane *p1,
+		const struct plane *p2, float precision)
+{
+	float cos_angle = vec3_dot(&p1->dir, &p2->dir);
+
+	if (close_float(cos_angle, 1.0f, precision))
+		return close_float(p1->dist, p2->dist, precision);
+	else if (close_float(cos_angle, -1.0f, precision))
+		return close_float(-p1->dist, p2->dist, precision);
+
+	return false;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 216 - 0
libobs/graphics/quat.c

@@ -0,0 +1,216 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "quat.h"
+#include "vec3.h"
+#include "matrix3.h"
+#include "axisang.h"
+
+static inline void quat_vec3(struct vec3 *v, const struct quat *q)
+{
+	v->m = q->m;
+	v->w = 0.0f;
+}
+
+void quat_mul(struct quat *dst, const struct quat *q1, const struct quat *q2)
+{
+	struct vec3 q1axis, q2axis;
+	struct vec3 temp1, temp2;
+
+	quat_vec3(&q1axis, q1);
+	quat_vec3(&q2axis, q2);
+
+	vec3_mulf(&temp1, &q2axis, q1->w);
+	vec3_mulf(&temp2, &q1axis, q2->w);
+	vec3_add(&temp1, &temp1, &temp2);
+	vec3_cross(&temp2, &q1axis, &q2axis);
+	vec3_add((struct vec3 *)dst, &temp1, &temp2);
+
+	dst->w = (q1->w * q2->w) - vec3_dot(&q1axis, &q2axis);
+}
+
+void quat_from_axisang(struct quat *dst, const struct axisang *aa)
+{
+	float halfa = aa->w * 0.5f;
+	float sine  = sinf(halfa);
+
+	dst->x = aa->x * sine;
+	dst->y = aa->y * sine;
+	dst->z = aa->z * sine;
+	dst->w  = cosf(halfa);
+}
+
+struct f4x4 {
+    float ptr[4][4];
+};
+
+void quat_from_matrix(struct quat *dst, const struct matrix3 *m)
+{
+	float tr = (m->x.x + m->y.y + m->z.z);
+	float inv_half;
+	float four_d;
+	int i,j,k;
+
+	if (tr > 0.0f) {
+		four_d = sqrtf(tr+1.0f);
+		dst->w = four_d*0.5f;
+
+		inv_half = 0.5f/four_d;
+		dst->x = (m->y.z - m->z.y)*inv_half;
+		dst->y = (m->z.x - m->x.z)*inv_half;
+		dst->z = (m->x.y - m->y.x)*inv_half;
+	} else {
+		struct f4x4 *val = (struct f4x4*)m;
+
+		i = (m->x.x > m->y.y) ? 0 : 1;
+
+		if (m->z.z > val->ptr[i][i])
+			i = 2;
+
+		j = (i+1)%3;
+		k = (i+2)%3;
+
+		/* ---------------------------------- */
+
+		four_d = sqrtf((val->ptr[i][i] - val->ptr[j][j] -
+					val->ptr[k][k]) + 1.0f);
+
+		dst->ptr[i] = four_d*0.5f;
+
+		inv_half = 0.5f/four_d;
+		dst->ptr[j]  = (val->ptr[i][j] + val->ptr[j][i])*inv_half;
+		dst->ptr[k]  = (val->ptr[i][k] + val->ptr[k][i])*inv_half;
+		dst->w =       (val->ptr[j][k] - val->ptr[k][j])*inv_half;
+	}
+}
+
+void quat_get_dir(struct vec3 *dst, const struct quat *q)
+{
+	struct matrix3 m;
+	matrix3_from_quat(&m, q);
+	vec3_copy(dst, &m.z);
+}
+
+void quat_set_look_dir(struct quat *dst, const struct vec3 *dir)
+{
+	struct vec3 new_dir;
+	struct quat xz_rot, yz_rot;
+	bool   xz_valid;
+	bool   yz_valid;
+	struct axisang aa;
+
+	vec3_norm(&new_dir, dir);
+	vec3_neg(&new_dir, &new_dir);
+
+	quat_identity(&xz_rot);
+	quat_identity(&yz_rot);
+
+	xz_valid = close_float(new_dir.x, 0.0f, EPSILON) ||
+		close_float(new_dir.z, 0.0f, EPSILON);
+	yz_valid = close_float(new_dir.y, 0.0f, EPSILON);
+
+	if (xz_valid) {
+		axisang_set(&aa, 0.0f, 1.0f, 0.0f,
+				atan2f(new_dir.x, new_dir.z));
+
+		quat_from_axisang(&xz_rot, &aa);
+	}
+	if (yz_valid) {
+		axisang_set(&aa, -1.0f, 0.0f, 0.0f, asinf(new_dir.y));
+		quat_from_axisang(&yz_rot, &aa);
+	}
+
+	if (!xz_valid)
+		quat_copy(dst, &yz_rot);
+	else if (!yz_valid)
+		quat_copy(dst, &xz_rot);
+	else
+		quat_mul(dst, &xz_rot, &yz_rot);
+}
+
+void quat_log(struct quat *dst, const struct quat *q)
+{
+	float angle = acosf(q->w);
+	float sine  = sinf(angle);
+	float w     = q->w;
+
+	quat_copy(dst, q);
+	dst->w = 0.0f;
+
+	if ((fabsf(w) < 1.0f) && (fabsf(sine) >= EPSILON)) {
+		sine = angle/sine;
+		quat_mulf(dst, dst, sine);
+	}
+}
+
+void quat_exp(struct quat *dst, const struct quat *q)
+{
+	float length = sqrtf(q->x*q->x + q->y*q->y + q->z*q->z);
+	float sine   = sinf(length);
+
+	quat_copy(dst, q);
+	sine = (length > EPSILON) ? (sine/length) : 1.0f;
+	quat_mulf(dst, dst, sine);
+	dst->w = cosf(length);
+}
+
+void quat_interpolate(struct quat *dst, const struct quat *q1,
+		const struct quat *q2, float t)
+{
+	float dot = quat_dot(q1, q2);
+	float anglef = acosf(dot);
+	float sine, sinei, sinet, sineti;
+	struct quat temp;
+
+	if (anglef >= EPSILON) {
+		sine   = sinf(anglef);
+		sinei  = 1/sine;
+		sinet  = sinf(anglef*t)*sinei;
+		sineti = sinf(anglef*(1.0f-t))*sinei;
+
+		quat_mulf(&temp, q1, sineti);
+		quat_mulf(dst, q2, sinet);
+		quat_add(dst, &temp, dst);
+	} else {
+		quat_sub(&temp, q2, q1);
+		quat_mulf(&temp, &temp, t);
+		quat_add(dst, &temp, q1);
+	}
+}
+
+void quat_get_tangent(struct quat *dst, const struct quat *prev,
+		const struct quat *q, const struct quat *next)
+{
+	struct quat temp;
+
+	quat_sub(&temp, q, prev);
+	quat_add(&temp, &temp, next);
+	quat_sub(&temp, &temp, q);
+	quat_mulf(dst, &temp, 0.5f);
+}
+
+void quat_interpolate_cubic(struct quat *dst,
+                            const struct quat *q1, const struct quat *q2,
+                            const struct quat *m1, const struct quat *m2,
+                            float t)
+{
+	struct quat temp1, temp2;
+
+	quat_interpolate(&temp1, q1, q2, t);
+	quat_interpolate(&temp2, m1, m2, t);
+	quat_interpolate(dst, &temp1, &temp2, 2.0f*(1.0f-t)*t);
+}

+ 185 - 0
libobs/graphics/quat.h

@@ -0,0 +1,185 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef QUAT_H
+#define QUAT_H
+
+#include "../util/c99defs.h"
+#include "math-defs.h"
+#include "vec3.h"
+#include <xmmintrin.h>
+
+/*
+ * Quaternion math
+ *
+ *   Generally used to represent rotational data more than anything.  Allows
+ * for efficient and correct rotational interpolation without suffering from
+ * things like gimbal lock.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct matrix3;
+struct axisang;
+
+struct quat {
+	union {
+		struct {float x, y, z, w;};
+		float ptr[4];
+		__m128 m;
+	};
+};
+
+static inline void quat_identity(struct quat *q)
+{
+	q->m = _mm_setzero_ps();
+	q->w = 1.0f;
+}
+
+static inline void quat_set(struct quat *dst, float x, float y, float z,
+		float w)
+{
+	dst->m = _mm_set_ps(x, y, z, w);
+}
+
+static inline void quat_copy(struct quat *dst, const struct quat *q)
+{
+	dst->m = q->m;
+}
+
+static inline void quat_add(struct quat *dst, const struct quat *q1,
+		const struct quat *q2)
+{
+	dst->m = _mm_add_ps(q1->m, q2->m);
+}
+
+static inline void quat_sub(struct quat *dst, const struct quat *q1,
+		const struct quat *q2)
+{
+	dst->m = _mm_sub_ps(q1->m, q2->m);
+}
+
+EXPORT void quat_mul(struct quat *dst, const struct quat *q1,
+		const struct quat *q2);
+
+static inline void quat_addf(struct quat *dst, const struct quat *q,
+		float f)
+{
+	dst->m = _mm_add_ps(q->m, _mm_set1_ps(f));
+}
+
+static inline void quat_subf(struct quat *dst, const struct quat *q,
+		float f)
+{
+	dst->m = _mm_sub_ps(q->m, _mm_set1_ps(f));
+}
+
+static inline void quat_mulf(struct quat *dst, const struct quat *q,
+		float f)
+{
+	dst->m = _mm_mul_ps(q->m, _mm_set1_ps(f));
+}
+
+static inline void quat_divf(struct quat *dst, const struct quat *q,
+		float f)
+{
+	dst->m = _mm_div_ps(q->m, _mm_set1_ps(f));
+}
+
+static inline float quat_dot(const struct quat *q1, const struct quat *q2)
+{
+	struct vec3 add;
+	__m128 mul = _mm_mul_ps(q1->m, q2->m);
+	add.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);
+	add.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);
+	return add.x;
+}
+
+static inline void quat_inv(struct quat *dst, const struct quat *q)
+{
+	dst->x = -q->x;
+	dst->y = -q->y;
+	dst->z = -q->z;
+}
+
+static inline void quat_neg(struct quat *dst, const struct quat *q)
+{
+	dst->x = -q->x;
+	dst->y = -q->y;
+	dst->z = -q->z;
+	dst->w = -q->w;
+}
+
+static inline float quat_len(const struct quat *q)
+{
+	float dot_val = quat_dot(q, q);
+	return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
+}
+
+static inline float quat_dist(const struct quat *q1, const struct quat *q2)
+{
+	struct quat temp;
+	float dot_val;
+
+	quat_sub(&temp, q1, q2);
+	dot_val = quat_dot(&temp, &temp);
+	return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
+}
+
+static inline void quat_norm(struct quat *dst, const struct quat *q)
+{
+	float dot_val = quat_dot(q, q);
+	dst->m = (dot_val > 0.0f) ?
+		_mm_mul_ps(q->m, _mm_set1_ps(1.0f/sqrtf(dot_val))) :
+		_mm_setzero_ps();
+}
+
+static inline bool quat_close(const struct quat *q1, const struct quat *q2,
+		float epsilon)
+{
+	struct quat test;
+	quat_sub(&test, q1, q2);
+	return test.x < epsilon &&
+	       test.y < epsilon &&
+	       test.z < epsilon &&
+	       test.w < epsilon;
+}
+
+EXPORT void quat_from_axisang(struct quat *dst, const struct axisang *aa);
+EXPORT void quat_from_matrix3(struct quat *dst, const struct matrix3 *m);
+
+EXPORT void quat_get_dir(struct vec3 *dst, const struct quat *q);
+EXPORT void quat_set_look_dir(struct quat *dst, const struct vec3 *dir);
+
+EXPORT void quat_log(struct quat *dst, const struct quat *q);
+EXPORT void quat_exp(struct quat *dst, const struct quat *q);
+
+EXPORT void quat_interpolate(struct quat *dst, const struct quat *q1,
+		const struct quat *q2, float t);
+EXPORT void quat_get_tangent(struct quat *dst, const struct quat *prev,
+		const struct quat *q, const struct quat *next);
+EXPORT void quat_interpolate_cubic(struct quat *dst, const struct quat *q1,
+		const struct quat *q2, const struct quat *m1,
+		const struct quat *m2, float t);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 677 - 0
libobs/graphics/shader-parser.c

@@ -0,0 +1,677 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "shader-parser.h"
+
+enum shader_param_type get_shader_param_type(const char *type)
+{
+	if (strcmp(type, "float") == 0)
+		return SHADER_PARAM_FLOAT;
+	else if (strcmp(type, "float2") == 0)
+		return SHADER_PARAM_VEC2;
+	else if (strcmp(type, "float3") == 0)
+		return SHADER_PARAM_VEC3;
+	else if (strcmp(type, "float4") == 0)
+		return SHADER_PARAM_VEC4;
+	else if (astrcmp_n(type, "texture", 7) == 0)
+		return SHADER_PARAM_TEXTURE;
+	else if (strcmp(type, "float3x3") == 0)
+		return SHADER_PARAM_MATRIX3X3;
+	else if (strcmp(type, "float4x4") == 0)
+		return SHADER_PARAM_MATRIX4X4;
+	else if (strcmp(type, "bool") == 0)
+		return SHADER_PARAM_BOOL;
+	else if (strcmp(type, "int") == 0)
+		return SHADER_PARAM_INT;
+	else if (strcmp(type, "string") == 0)
+		return SHADER_PARAM_STRING;
+
+	return SHADER_PARAM_UNKNOWN;
+}
+
+enum gs_sample_filter get_sample_filter(const char *filter)
+{
+	if (astrcmpi(filter, "Anisotropy") == 0)
+		return GS_FILTER_ANISOTROPIC;
+
+	else if (astrcmpi(filter, "Point")           == 0 ||
+	         strcmp(filter, "MIN_MAG_MIP_POINT") == 0)
+		return GS_FILTER_POINT;
+
+	else if (astrcmpi(filter, "Linear")           == 0 ||
+	         strcmp(filter, "MIN_MAG_MIP_LINEAR") == 0)
+		return GS_FILTER_LINEAR;
+
+	else if (strcmp(filter, "MIN_MAG_POINT_MIP_LINEAR") == 0)
+		return GS_FILTER_MIN_MAG_POINT_MIP_LINEAR;
+
+	else if (strcmp(filter, "MIN_POINT_MAG_LINEAR_MIP_POINT") == 0)
+		return GS_FILTER_MIN_POINT_MAG_LINEAR_MIP_POINT;
+
+	else if (strcmp(filter, "MIN_POINT_MAG_MIP_LINEAR") == 0)
+		return GS_FILTER_MIN_POINT_MAG_MIP_LINEAR;
+
+	else if (strcmp(filter, "MIN_LINEAR_MAG_MIP_POINT") == 0)
+		return GS_FILTER_MIN_LINEAR_MAG_MIP_POINT;
+
+	else if (strcmp(filter, "MIN_LINEAR_MAG_POINT_MIP_LINEAR") == 0)
+		return GS_FILTER_MIN_LINEAR_MAG_POINT_MIP_LINEAR;
+
+	else if (strcmp(filter, "MIN_MAG_LINEAR_MIP_POINT") == 0)
+		return GS_FILTER_MIN_MAG_LINEAR_MIP_POINT;
+
+	return GS_FILTER_LINEAR;
+}
+
+extern enum gs_address_mode get_address_mode(const char *mode)
+{
+	if (astrcmpi(mode, "Wrap") == 0 || astrcmpi(mode, "Repeat") == 0)
+		return GS_ADDRESS_WRAP;
+	else if (astrcmpi(mode, "Clamp") == 0 || astrcmpi(mode, "None") == 0)
+		return GS_ADDRESS_CLAMP;
+	else if (astrcmpi(mode, "Mirror") == 0)
+		return GS_ADDRESS_MIRROR;
+	else if (astrcmpi(mode, "Border") == 0)
+		return GS_ADDRESS_BORDER;
+	else if (astrcmpi(mode, "MirrorOnce") == 0)
+		return GS_ADDRESS_MIRRORONCE;
+
+	return GS_ADDRESS_CLAMP;
+}
+
+void shader_sampler_convert(struct shader_sampler *ss,
+		struct gs_sampler_info *info)
+{
+	size_t i;
+	memset(info, 0, sizeof(struct gs_sampler_info));
+
+	for (i = 0; i < ss->states.num; i++) {
+		const char *state = ss->states.array[i];
+		const char *value = ss->values.array[i];
+
+		if (astrcmpi(state, "Filter") == 0)
+			info->filter = get_sample_filter(value);
+		else if (astrcmpi(state, "AddressU") == 0)
+			info->address_u = get_address_mode(value);
+		else if (astrcmpi(state, "AddressV") == 0)
+			info->address_v = get_address_mode(value);
+		else if (astrcmpi(state, "AddressW") == 0)
+			info->address_w = get_address_mode(value);
+		else if (astrcmpi(state, "MaxAnisotropy") == 0)
+			info->max_anisotropy = strtol(value, NULL, 10);
+		/*else if (astrcmpi(state, "BorderColor") == 0)
+			// TODO */
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+
+static int sp_parse_sampler_state_item(struct shader_parser *sp,
+		struct shader_sampler *ss)
+{
+	int ret;
+	char *state = NULL, *value = NULL;
+
+	ret = next_name(&sp->cfp, &state, "state name", ";");
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	ret = next_token_should_be(&sp->cfp, "=", ";", NULL);
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	ret = next_name(&sp->cfp, &value, "value name", ";");
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	ret = next_token_should_be(&sp->cfp, ";", ";", NULL);
+	if (ret != PARSE_SUCCESS) goto fail;
+
+	da_push_back(ss->states, &state);
+	da_push_back(ss->values, &value);
+	return ret;
+
+fail:
+	bfree(state);
+	bfree(value);
+	return ret;
+}
+
+static void sp_parse_sampler_state(struct shader_parser *sp)
+{
+	struct shader_sampler ss;
+	struct cf_token peek;
+	shader_sampler_init(&ss);
+
+	if (next_name(&sp->cfp, &ss.name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&sp->cfp, "{", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+
+	if (!peek_valid_token(&sp->cfp, &peek))
+		goto error;
+
+	while (strref_cmp(&peek.str, "}") != 0) {
+		int ret = sp_parse_sampler_state_item(sp, &ss);
+		if (ret == PARSE_EOF)
+			goto error;
+
+		if (!peek_valid_token(&sp->cfp, &peek))
+			goto error;
+	}
+
+	if (next_token_should_be(&sp->cfp, "}", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&sp->cfp, ";", NULL, NULL) != PARSE_SUCCESS)
+		goto error;
+
+	da_push_back(sp->samplers, &ss);
+	return;
+
+error:
+	shader_sampler_free(&ss);
+}
+
+static inline int sp_parse_struct_var(struct shader_parser *sp,
+		struct shader_var *var)
+{
+	int errcode;
+
+	/* -------------------------------------- */
+	/* variable type */
+
+	if (!next_valid_token(&sp->cfp)) return PARSE_EOF;
+
+	if (token_is(&sp->cfp, ";")) return PARSE_CONTINUE;
+	if (token_is(&sp->cfp, "}")) return PARSE_BREAK;
+
+	errcode = token_is_type(&sp->cfp, CFTOKEN_NAME, "type name", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	copy_token(&sp->cfp, &var->type);
+
+	/* -------------------------------------- */
+	/* variable name */
+
+	if (!next_valid_token(&sp->cfp)) return PARSE_EOF;
+
+	if (token_is(&sp->cfp, ";")) return PARSE_UNEXPECTED_CONTINUE;
+	if (token_is(&sp->cfp, "}")) return PARSE_UNEXPECTED_BREAK;
+
+	errcode = token_is_type(&sp->cfp, CFTOKEN_NAME, "variable name", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	copy_token(&sp->cfp, &var->name);
+
+	/* -------------------------------------- */
+	/* variable mapping if any (POSITION, TEXCOORD, etc) */
+
+	if (!next_valid_token(&sp->cfp)) return PARSE_EOF;
+
+	if (token_is(&sp->cfp, ":")) {
+		if (!next_valid_token(&sp->cfp)) return PARSE_EOF;
+
+		if (token_is(&sp->cfp, ";")) return PARSE_UNEXPECTED_CONTINUE;
+		if (token_is(&sp->cfp, "}")) return PARSE_UNEXPECTED_BREAK;
+
+		errcode = token_is_type(&sp->cfp, CFTOKEN_NAME,
+				"mapping name", ";");
+		if (errcode != PARSE_SUCCESS)
+			return errcode;
+
+		copy_token(&sp->cfp, &var->mapping);
+
+		if (!next_valid_token(&sp->cfp)) return PARSE_EOF;
+	}
+
+	/* -------------------------------------- */
+
+	if (!token_is(&sp->cfp, ";")) {
+		if (!go_to_valid_token(&sp->cfp, ";", "}"))
+			return PARSE_EOF;
+		return PARSE_CONTINUE;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static void sp_parse_struct(struct shader_parser *sp)
+{
+	struct shader_struct ss;
+	shader_struct_init(&ss);
+
+	if (next_name(&sp->cfp, &ss.name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_token_should_be(&sp->cfp, "{", ";", NULL) != PARSE_SUCCESS)
+		goto error;
+
+	/* get structure variables */
+	while (true) {
+		bool do_break = false;
+		struct shader_var var;
+
+		shader_var_init(&var);
+
+		switch (sp_parse_struct_var(sp, &var)) {
+
+		case PARSE_UNEXPECTED_CONTINUE:
+			cf_adderror_syntax_error(&sp->cfp);
+		case PARSE_CONTINUE:
+			shader_var_free(&var);
+			continue;
+
+		case PARSE_UNEXPECTED_BREAK:
+			cf_adderror_syntax_error(&sp->cfp);
+		case PARSE_BREAK:
+			shader_var_free(&var);
+			do_break = true;
+			break;
+
+		case PARSE_EOF:
+			shader_var_free(&var);
+			goto error;
+		}
+
+		if (do_break)
+			break;
+
+		da_push_back(ss.vars, &var);
+	}
+
+	if (next_token_should_be(&sp->cfp, ";", NULL, NULL) != PARSE_SUCCESS)
+		goto error;
+
+	da_push_back(sp->structs, &ss);
+	return;
+
+error:
+	shader_struct_free(&ss);
+}
+
+static inline int sp_check_for_keyword(struct shader_parser *sp,
+		const char *keyword, bool *val)
+{
+	bool new_val;
+	new_val = token_is(&sp->cfp, keyword);
+	if (new_val) {
+		if (!next_valid_token(&sp->cfp))
+			return PARSE_EOF;
+
+		if (new_val && *val)
+			cf_adderror(&sp->cfp, "'$1' keyword already specified",
+					LEVEL_WARNING, keyword, NULL, NULL);
+		*val = new_val;
+
+		return PARSE_CONTINUE;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static inline int sp_parse_func_param(struct shader_parser *sp,
+		struct shader_func *func, struct shader_var *var)
+{
+	int errcode;
+	bool is_uniform;
+
+	if (!next_valid_token(&sp->cfp))
+		return PARSE_EOF;
+
+	errcode = sp_check_for_keyword(sp, "uniform", &is_uniform);
+	if (errcode == PARSE_EOF)
+		return PARSE_EOF;
+
+	var->var_type = is_uniform ? SHADER_VAR_UNIFORM : SHADER_VAR_NONE;
+
+	errcode = get_name(&sp->cfp, &var->type, "type", ")");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	errcode = next_name(&sp->cfp, &var->name, "name", ")");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	if (!next_valid_token(&sp->cfp))
+		return PARSE_EOF;
+
+	if (token_is(&sp->cfp, ":")) {
+		errcode = next_name(&sp->cfp, &var->mapping,
+				"mapping specifier", ")");
+		if (errcode != PARSE_SUCCESS)
+			return errcode;
+
+		if (!next_valid_token(&sp->cfp))
+			return PARSE_EOF;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static bool sp_parse_func_params(struct shader_parser *sp,
+		struct shader_func *func)
+{
+	struct cf_token peek;
+	int errcode;
+
+	cf_token_clear(&peek);
+
+	if (!peek_valid_token(&sp->cfp, &peek))
+		return false;
+
+	if (*peek.str.array == ')') {
+		next_token(&sp->cfp);
+		goto exit;
+	}
+
+	do {
+		struct shader_var var;
+		shader_var_init(&var);
+
+		if (!token_is(&sp->cfp, "(") && !token_is(&sp->cfp, ","))
+			cf_adderror_syntax_error(&sp->cfp);
+
+		errcode = sp_parse_func_param(sp, func, &var);
+		if (errcode != PARSE_SUCCESS) {
+			shader_var_free(&var);
+
+			if (errcode == PARSE_CONTINUE)
+				goto exit;
+			else if (errcode == PARSE_EOF)
+				return false;
+		}
+
+		da_push_back(func->params, &var);
+	} while (!token_is(&sp->cfp, ")"));
+
+exit:
+	return true;
+}
+
+static void sp_parse_function(struct shader_parser *sp,
+		char *type, char *name)
+{
+	struct shader_func func;
+
+	shader_func_init(&func, type, name);
+	if (!sp_parse_func_params(sp, &func))
+		goto error;
+
+	if (!next_valid_token(&sp->cfp))
+		goto error;
+
+	/* if function is mapped to something, for example COLOR */
+	if (token_is(&sp->cfp, ":")) {
+		if (!next_valid_token(&sp->cfp))
+			goto error;
+		if (!next_valid_token(&sp->cfp))
+			goto error;
+	}
+
+	if (!token_is(&sp->cfp, "{")) {
+		cf_adderror_expecting(&sp->cfp, "{");
+		goto error;
+	}
+
+	func.start = sp->cfp.cur_token;
+	if (!pass_pair(&sp->cfp, '{', '}'))
+		goto error;
+
+	/* it is established that the current token is '}' if we reach this */
+	next_token(&sp->cfp);
+
+	func.end = sp->cfp.cur_token;
+	da_push_back(sp->funcs, &func);
+	return;
+
+error:
+	shader_func_free(&func);
+}
+
+/* parses "array[count]" */
+static bool sp_parse_param_array(struct shader_parser *sp,
+		struct shader_var *param)
+{
+	if (!next_valid_token(&sp->cfp))
+		return false;
+
+	if (sp->cfp.cur_token->type != CFTOKEN_NUM ||
+	    !valid_int_str(sp->cfp.cur_token->str.array,
+		    sp->cfp.cur_token->str.len))
+		return false;
+
+	param->array_count = strtol(sp->cfp.cur_token->str.array, NULL, 10);
+
+	if (next_token_should_be(&sp->cfp, "]", ";", NULL) == PARSE_EOF)
+		return false;
+
+	if (!next_valid_token(&sp->cfp))
+		return false;
+
+	return true;
+}
+
+static inline int sp_parse_param_assign_intfloat(struct shader_parser *sp,
+		struct shader_var *param, bool is_float)
+{
+	int errcode;
+
+	if (!next_valid_token(&sp->cfp))
+		return PARSE_EOF;
+
+	errcode = token_is_type(&sp->cfp, CFTOKEN_NUM, "numeric value", ";");
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	if (is_float) {
+		float f = (float)strtod(sp->cfp.cur_token->str.array, NULL);
+		da_push_back_array(param->default_val, &f, sizeof(float));
+	} else {
+		long l = strtol(sp->cfp.cur_token->str.array, NULL, 10);
+		da_push_back_array(param->default_val, &l, sizeof(long));
+	}
+
+	return PARSE_SUCCESS;
+}
+
+/*
+ * parses assignment for float1, float2, float3, float4, and any combination
+ * for float3x3, float4x4, etc
+ */
+static inline int sp_parse_param_assign_float_array(struct shader_parser *sp,
+		struct shader_var *param)
+{
+	const char *float_type = param->type+5;
+	int float_count = 0, errcode, i;
+
+	/* -------------------------------------------- */
+
+	if (float_type[0] < '1' || float_type[0] > '4')
+		cf_adderror(&sp->cfp, "Invalid row count", LEVEL_ERROR,
+				NULL, NULL, NULL);
+
+	float_count = float_type[0]-'0';
+
+	if (float_type[1] == 'x') {
+		if (float_type[2] < '1' || float_type[2] > '4')
+			cf_adderror(&sp->cfp, "Invalid column count",
+					LEVEL_ERROR, NULL, NULL, NULL);
+
+		float_count *= float_type[2]-'0';
+	}
+
+	/* -------------------------------------------- */
+
+	errcode = next_token_should_be(&sp->cfp, "{", ";", NULL);
+	if (errcode != PARSE_SUCCESS) return errcode;
+
+	for (i = 0; i < float_count; i++) {
+		char *next = ((i+1) < float_count) ? "," : "}";
+
+		errcode = sp_parse_param_assign_intfloat(sp, param, true);
+		if (errcode != PARSE_SUCCESS) return errcode;
+
+		errcode = next_token_should_be(&sp->cfp, next, ";", NULL);
+		if (errcode != PARSE_SUCCESS) return errcode;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static int sp_parse_param_assignment_val(struct shader_parser *sp,
+		struct shader_var *param)
+{
+	if (strcmp(param->type, "int") == 0)
+		return sp_parse_param_assign_intfloat(sp, param, false);
+	else if (strcmp(param->type, "float") == 0)
+		return sp_parse_param_assign_intfloat(sp, param, true);
+	else if (astrcmp_n(param->type, "float", 5) == 0)
+		return sp_parse_param_assign_float_array(sp, param);
+
+	cf_adderror(&sp->cfp, "Invalid type '$1' used for assignment",
+			LEVEL_ERROR, param->type, NULL, NULL);
+
+	return PARSE_CONTINUE;
+}
+
+static inline bool sp_parse_param_assignment(struct shader_parser *sp,
+		struct shader_var *param)
+{
+	if (sp_parse_param_assignment_val(sp, param) != PARSE_SUCCESS)
+		return false;
+
+	if (!next_valid_token(&sp->cfp))
+		return false;
+
+	return true;
+}
+
+static void sp_parse_param(struct shader_parser *sp,
+		char *type, char *name, bool is_const, bool is_uniform)
+{
+	struct shader_var param;
+	shader_var_init_param(&param, type, name, is_uniform, is_const);
+
+	if (token_is(&sp->cfp, ";"))
+		goto complete;
+	if (token_is(&sp->cfp, "[") && !sp_parse_param_array(sp, &param))
+		goto error;
+	if (token_is(&sp->cfp, "=") && !sp_parse_param_assignment(sp, &param))
+		goto error;
+	if (!token_is(&sp->cfp, ";"))
+		goto error;
+
+complete:
+	da_push_back(sp->params, &param);
+	return;
+
+error:
+	shader_var_free(&param);
+}
+
+static bool sp_get_var_specifiers(struct shader_parser *sp,
+		bool *is_const, bool *is_uniform)
+{
+	while(true) {
+		int errcode;
+		errcode = sp_check_for_keyword(sp, "const", is_const);
+		if (errcode == PARSE_EOF)
+			return false;
+		else if (errcode == PARSE_CONTINUE)
+			continue;
+
+		errcode = sp_check_for_keyword(sp, "uniform", is_uniform);
+		if (errcode == PARSE_EOF)
+			return false;
+		else if (errcode == PARSE_CONTINUE)
+			continue;
+
+		break;
+	}
+
+	return true;
+}
+
+static inline void report_invalid_func_keyword(struct shader_parser *sp,
+		const char *name, bool val)
+{
+	if (val)
+		cf_adderror(&sp->cfp, "'$1' keyword cannot be used with a "
+		                      "function", LEVEL_ERROR,
+		                      name, NULL, NULL);
+}
+
+static void sp_parse_other(struct shader_parser *sp)
+{
+	bool is_const = false, is_uniform = false;
+	char *type = NULL, *name = NULL;
+
+	if (!sp_get_var_specifiers(sp, &is_const, &is_uniform))
+		goto error;
+
+	if (get_name(&sp->cfp, &type, "type", ";") != PARSE_SUCCESS)
+		goto error;
+	if (next_name(&sp->cfp, &name, "name", ";") != PARSE_SUCCESS)
+		goto error;
+
+	if (!next_valid_token(&sp->cfp))
+		goto error;
+
+	if (token_is(&sp->cfp, "(")) {
+		report_invalid_func_keyword(sp, "const",    is_const);
+		report_invalid_func_keyword(sp, "uniform",  is_uniform);
+
+		sp_parse_function(sp, type, name);
+		return;
+	} else {
+		sp_parse_param(sp, type, name, is_const, is_uniform);
+		return;
+	}
+
+error:
+	bfree(type);
+	bfree(name);
+}
+
+bool shader_parse(struct shader_parser *sp, const char *shader,
+		const char *file)
+{
+	if (!cf_parser_parse(&sp->cfp, shader, file))
+		return false;
+
+	while (sp->cfp.cur_token && sp->cfp.cur_token->type != CFTOKEN_NONE) {
+		if (token_is(&sp->cfp, ";") ||
+		    is_whitespace(*sp->cfp.cur_token->str.array)) {
+			sp->cfp.cur_token++;
+
+		} else if (token_is(&sp->cfp, "struct")) {
+			sp_parse_struct(sp);
+
+		} else if (token_is(&sp->cfp, "sampler_state")) {
+			sp_parse_sampler_state(sp);
+
+		} else if (token_is(&sp->cfp, "{")) {
+			cf_adderror(&sp->cfp, "Unexpected code segment",
+					LEVEL_ERROR, NULL, NULL, NULL);
+			pass_pair(&sp->cfp, '{', '}');
+
+		} else {
+			/* parameters and functions */
+			sp_parse_other(sp);
+		}
+	}
+
+	return !error_data_has_errors(&sp->cfp.error_list);
+}

+ 246 - 0
libobs/graphics/shader-parser.h

@@ -0,0 +1,246 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef SHADER_PARSER_H
+#define SHADER_PARSER_H
+
+#include "../util/cf-parser.h"
+#include "graphics.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EXPORT enum shader_param_type get_shader_param_type(const char *type);
+EXPORT enum gs_sample_filter get_sample_filter(const char *filter);
+EXPORT enum gs_address_mode get_address_mode(const char *address_mode);
+
+/*
+ * Shader Parser
+ *
+ *   Parses a shader and extracts data such as shader constants, samplers,
+ * and vertex input information.  Also allows the reformatting of shaders for
+ * different libraries.  This is usually used only by graphics libraries,
+ */
+
+enum shader_var_type {
+	SHADER_VAR_NONE,
+	SHADER_VAR_UNIFORM,
+	SHADER_VAR_CONST
+};
+
+struct shader_var {
+	char *type;
+	char *name;
+	char *mapping;
+	enum shader_var_type var_type;
+	int array_count;
+
+	DARRAY(uint8_t) default_val;
+};
+
+static inline void shader_var_init(struct shader_var *sv)
+{
+	memset(sv, 0, sizeof(struct shader_var));
+}
+
+static inline void shader_var_init_param(struct shader_var *sv,
+		char *type, char *name, bool is_uniform,
+		bool is_const)
+{
+	if (is_uniform)
+		sv->var_type = SHADER_VAR_UNIFORM;
+	else if (is_const)
+		sv->var_type = SHADER_VAR_CONST;
+	else
+		sv->var_type = SHADER_VAR_NONE;
+
+	sv->type        = type;
+	sv->name        = name;
+	sv->mapping     = NULL;
+	sv->array_count = 0;
+	da_init(sv->default_val);
+}
+
+static inline void shader_var_free(struct shader_var *sv)
+{
+	bfree(sv->type);
+	bfree(sv->name);
+	bfree(sv->mapping);
+	da_free(sv->default_val);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct shader_sampler {
+	char *name;
+	DARRAY(char*) states;
+	DARRAY(char*) values;
+};
+
+static inline void shader_sampler_init(struct shader_sampler *ss)
+{
+	memset(ss, 0, sizeof(struct shader_sampler));
+}
+
+static inline void shader_sampler_free(struct shader_sampler *ss)
+{
+	size_t i;
+	for (i = 0; i < ss->states.num; i++)
+		bfree(ss->states.array[i]);
+	for (i = 0; i < ss->values.num; i++)
+		bfree(ss->values.array[i]);
+
+	bfree(ss->name);
+	da_free(ss->states);
+	da_free(ss->values);
+}
+
+EXPORT void shader_sampler_convert(struct shader_sampler *ss,
+		struct gs_sampler_info *info);
+
+/* ------------------------------------------------------------------------- */
+
+struct shader_struct {
+	char *name;
+	DARRAY(struct shader_var) vars;
+};
+
+static inline void shader_struct_init(struct shader_struct *ss)
+{
+	memset(ss, 0, sizeof(struct shader_struct));
+}
+
+static inline void shader_struct_free(struct shader_struct *ss)
+{
+	size_t i;
+
+	for (i = 0; i < ss->vars.num; i++)
+		shader_var_free(ss->vars.array+i);
+
+	bfree(ss->name);
+	da_free(ss->vars);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct shader_func {
+	char *name;
+	char *return_type;
+	DARRAY(struct shader_var) params;
+
+	const struct cf_token *start, *end;
+};
+
+static inline void shader_func_init(struct shader_func *sf,
+		char *return_type, char *name)
+{
+	da_init(sf->params);
+
+	sf->return_type = return_type;
+	sf->name        = name;
+	sf->start       = NULL;
+	sf->end         = NULL;
+}
+
+static inline void shader_func_free(struct shader_func *sf)
+{
+	size_t i;
+
+	for (i = 0; i < sf->params.num; i++)
+		shader_var_free(sf->params.array+i);
+
+	bfree(sf->name);
+	bfree(sf->return_type);
+	da_free(sf->params);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct shader_parser {
+	struct cf_parser cfp;
+
+	DARRAY(struct shader_var)     params;
+	DARRAY(struct shader_struct)  structs;
+	DARRAY(struct shader_sampler) samplers;
+	DARRAY(struct shader_func)    funcs;
+};
+
+static inline void shader_parser_init(struct shader_parser *sp)
+{
+	cf_parser_init(&sp->cfp);
+
+	da_init(sp->params);
+	da_init(sp->structs);
+	da_init(sp->samplers);
+	da_init(sp->funcs);
+}
+
+static inline void shader_parser_free(struct shader_parser *sp)
+{
+	size_t i;
+
+	for (i = 0; i < sp->params.num; i++)
+		shader_var_free(sp->params.array+i);
+	for (i = 0; i < sp->structs.num; i++)
+		shader_struct_free(sp->structs.array+i);
+	for (i = 0; i < sp->samplers.num; i++)
+		shader_sampler_free(sp->samplers.array+i);
+	for (i = 0; i < sp->funcs.num; i++)
+		shader_func_free(sp->funcs.array+i);
+
+	cf_parser_free(&sp->cfp);
+	da_free(sp->params);
+	da_free(sp->structs);
+	da_free(sp->samplers);
+	da_free(sp->funcs);
+}
+
+EXPORT bool shader_parse(struct shader_parser *sp, const char *shader,
+		const char *file);
+
+static inline struct shader_func *shader_parser_getfunc(
+		struct shader_parser *sp, const char *func_name)
+{
+	size_t i;
+	for (i = 0; i < sp->funcs.num; i++) {
+		struct shader_func *func = sp->funcs.array+i;
+		if (strcmp(func->name, func_name) == 0)
+			return func;
+	}
+
+	return NULL;
+}
+
+static inline struct shader_struct *shader_parser_getstruct(
+		struct shader_parser *sp, const char *struct_name)
+{
+	size_t i;
+	for (i = 0; i < sp->structs.num; i++) {
+		struct shader_struct *st = sp->structs.array+i;
+		if (strcmp(st->name, struct_name) == 0)
+			return st;
+	}
+
+	return NULL;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 139 - 0
libobs/graphics/texture-render.c

@@ -0,0 +1,139 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+/*
+ *   This is a set of helper functions to more easily render to textures
+ * without having to duplicate too much code.
+ */
+
+#include <assert.h>
+#include "graphics.h"
+
+struct gs_texture_render {
+	texture_t  target, prev_target;
+	zstencil_t zs, prev_zs;
+
+	int cx, cy;
+
+	enum gs_color_format    format;
+	enum gs_zstencil_format zsformat;
+
+	bool rendered;
+};
+
+texrender_t texrender_create(enum gs_color_format format,
+		enum gs_zstencil_format zsformat)
+{
+	struct gs_texture_render *texrender;
+	texrender = bmalloc(sizeof(struct gs_texture_render));
+	memset(texrender, 0, sizeof(struct gs_texture_render));
+
+	texrender->format   = format;
+	texrender->zsformat = zsformat;
+
+	return texrender;
+}
+
+void texrender_destroy(texrender_t texrender)
+{
+	if (texrender) {
+		texture_destroy(texrender->target);
+		zstencil_destroy(texrender->zs);
+		bfree(texrender);
+	}
+}
+
+static bool texrender_resetbuffer(texrender_t texrender, int cx, int cy)
+{
+	texture_destroy(texrender->target);
+	zstencil_destroy(texrender->zs);
+
+	texrender->target = NULL;
+	texrender->zs     = NULL;
+	texrender->cx     = cx;
+	texrender->cy     = cy;
+
+	texrender->target = gs_create_texture(cx, cy, texrender->format,
+			NULL, GS_RENDERTARGET);
+	if (!texrender->target)
+		return false;
+
+	if (texrender->zsformat != GS_ZS_NONE) {
+		texrender->zs = gs_create_zstencil(cx, cy, texrender->zsformat);
+		if (!texrender->zs) {
+			texture_destroy(texrender->target);
+			texrender->target = NULL;
+
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool texrender_begin(texrender_t texrender, int cx, int cy)
+{
+	if (texrender->rendered)
+		return false;
+
+	if (cx == -1)
+		cx = gs_getwidth();
+	if (cy == -1)
+		cy = gs_getheight();
+
+	assert(cx && cy);
+	if (!cx || !cy)
+		return false;
+
+	if (texrender->cx != cx || texrender->cy != cy)
+		if (!texrender_resetbuffer(texrender, cx, cy))
+			return false;
+
+	gs_viewport_push();
+	gs_projection_push();
+	gs_matrix_push();
+	gs_matrix_identity();
+
+	texrender->prev_target = gs_getrendertarget();
+	texrender->prev_zs     = gs_getzstenciltarget();
+	gs_setrendertarget(texrender->target, texrender->zs);
+
+	gs_setviewport(0, 0, texrender->cx, texrender->cy);
+
+	return true;
+}
+
+void texrender_end(texrender_t texrender)
+{
+	gs_setrendertarget(texrender->prev_target, texrender->prev_zs);
+
+	gs_matrix_pop();
+	gs_projection_pop();
+	gs_viewport_pop();
+
+	texrender->rendered = true;
+}
+
+void texrender_reset(texrender_t texrender)
+{
+	texrender->rendered = false;
+}
+
+texture_t texrender_gettexture(texrender_t texrender)
+{
+	return texrender->target;
+}

+ 52 - 0
libobs/graphics/vec2.c

@@ -0,0 +1,52 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <math.h>
+#include "math-extra.h"
+#include "math-defs.h"
+#include "vec2.h"
+
+void vec2_abs(struct vec2 *dst, const struct vec2 *v)
+{
+	vec2_set(dst, fabsf(v->x), fabsf(v->y));
+}
+
+void vec2_floor(struct vec2 *dst, const struct vec2 *v)
+{
+	vec2_set(dst, floorf(v->x), floorf(v->y));
+}
+
+void vec2_ceil(struct vec2 *dst, const struct vec2 *v)
+{
+	vec2_set(dst, ceilf(v->x), ceilf(v->y));
+}
+
+int vec2_close(const struct vec2 *v1, const struct vec2 *v2, float epsilon)
+{
+	return close_float(v1->x, v2->x, epsilon) &&
+	       close_float(v1->y, v2->y, epsilon);
+}
+
+void vec2_norm(struct vec2 *dst, const struct vec2 *v)
+{
+	float len = vec2_len(v);
+
+	if (len > 0.0f) {
+		len = 1.0f / len;
+		vec2_mulf(dst, v, len);
+	}
+}

+ 171 - 0
libobs/graphics/vec2.h

@@ -0,0 +1,171 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef VECT2_H
+#define VECT2_H
+
+#include "../util/c99defs.h"
+#include <math.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct vec2 {
+	union {
+		struct {
+			float x, y;
+		};
+		float ptr[2];
+	};
+};
+
+static inline void vec2_zero(struct vec2 *dst)
+{
+	dst->x = 0.0f;
+	dst->y = 0.0f;
+}
+
+static inline void vec2_set(struct vec2 *dst, float x, float y)
+{
+	dst->x = x;
+	dst->y = y;
+}
+
+static inline void vec2_copy(struct vec2 *dst, const struct vec2 *v)
+{
+	dst->x = v->x;
+	dst->y = v->y;
+}
+
+static inline void vec2_add(struct vec2 *dst, const struct vec2 *v1,
+		const struct vec2 *v2)
+{
+	vec2_set(dst, v1->x+v2->x, v1->y+v2->y);	
+}
+
+static inline void vec2_sub(struct vec2 *dst, const struct vec2 *v1,
+		const struct vec2 *v2)
+{
+	vec2_set(dst, v1->x-v2->x, v1->y-v2->y);	
+}
+
+static inline void vec2_mul(struct vec2 *dst, const struct vec2 *v1,
+		const struct vec2 *v2)
+{
+	vec2_set(dst, v1->x*v2->x, v1->y*v2->y);	
+}
+
+static inline void vec2_div(struct vec2 *dst, const struct vec2 *v1,
+		const struct vec2 *v2)
+{
+	vec2_set(dst, v1->x/v2->x, v1->y/v2->y);	
+}
+
+static inline void vec2_addf(struct vec2 *dst, const struct vec2 *v,
+		float f)
+{
+	vec2_set(dst, v->x+f, v->y+f);	
+}
+
+static inline void vec2_subf(struct vec2 *dst, const struct vec2 *v,
+		float f)
+{
+	vec2_set(dst, v->x-f, v->y-f);	
+}
+
+static inline void vec2_mulf(struct vec2 *dst, const struct vec2 *v,
+		float f)
+{
+	vec2_set(dst, v->x*f, v->y*f);	
+}
+
+static inline void vec2_divf(struct vec2 *dst, const struct vec2 *v,
+		float f)
+{
+	vec2_set(dst, v->x/f, v->y/f);	
+}
+
+static inline void vec2_neg(struct vec2 *dst, const struct vec2 *v)
+{
+	vec2_set(dst, -v->x, -v->y);
+}
+
+static inline float vec2_dot(const struct vec2 *v1, const struct vec2 *v2)
+{
+	return (v1->x+v2->x) * (v1->y+v2->y);
+}
+
+static inline float vec2_len(const struct vec2 *v)
+{
+	return sqrtf(v->x*v->x + v->y*v->y);
+}
+
+static inline float vec2_dist(const struct vec2 *v1, const struct vec2 *v2)
+{
+	struct vec2 temp;
+	vec2_sub(&temp, v1, v2);
+	return vec2_len(&temp);
+}
+
+static inline void vec2_minf(struct vec2 *dst, const struct vec2 *v,
+		float val)
+{
+	if (v->x < val)
+		dst->x = val;
+	if (v->y < val)
+		dst->y = val;
+}
+
+static inline void vec2_min(struct vec2 *dst, const struct vec2 *v,
+		const struct vec2 *min_v)
+{
+	if (v->x < min_v->x)
+		dst->x = min_v->x;
+	if (v->y < min_v->y)
+		dst->y = min_v->y;
+}
+
+static inline void vec2_maxf(struct vec2 *dst, const struct vec2 *v,
+		float val)
+{
+	if (v->x > val)
+		dst->x = val;
+	if (v->y > val)
+		dst->y = val;
+}
+
+static inline void vec2_max(struct vec2 *dst, const struct vec2 *v,
+		const struct vec2 *max_v)
+{
+	if (v->x > max_v->x)
+		dst->x = max_v->x;
+	if (v->y > max_v->y)
+		dst->y = max_v->y;
+}
+
+EXPORT void vec2_abs(struct vec2 *dst, const struct vec2 *v);
+EXPORT void vec2_floor(struct vec2 *dst, const struct vec2 *v);
+EXPORT void vec2_ceil(struct vec2 *dst, const struct vec2 *v);
+EXPORT int vec2_close(const struct vec2 *v1, const struct vec2 *v2,
+		float epsilon);
+EXPORT void vec2_norm(struct vec2 *dst, const struct vec2 *v);
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 73 - 0
libobs/graphics/vec3.c

@@ -0,0 +1,73 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "vec3.h"
+#include "quat.h"
+#include "axisang.h"
+#include "plane.h"
+#include "matrix3.h"
+#include "math-extra.h"
+
+float vec3_plane_dist(const struct vec3 *v, const struct plane *p)
+{
+	return vec3_dot(v, &p->dir) - p->dist;
+}
+
+void vec3_rotate(struct vec3 *dst, const struct vec3 *v,
+		const struct matrix3 *m)
+{
+	struct vec3 temp;
+	vec3_copy(&temp, v);
+
+	dst->x = vec3_dot(&temp, &m->x);
+	dst->y = vec3_dot(&temp, &m->y);
+	dst->z = vec3_dot(&temp, &m->z);
+}
+
+void vec3_transform(struct vec3 *dst, const struct vec3 *v,
+		const struct matrix3 *m)
+{
+	struct vec3 temp;
+	vec3_sub(&temp, v, &m->t);
+
+	dst->x = vec3_dot(&temp, &m->x);
+	dst->y = vec3_dot(&temp, &m->y);
+	dst->z = vec3_dot(&temp, &m->z);
+}
+
+void vec3_mirror(struct vec3 *dst, const struct vec3 *v, const struct plane *p)
+{
+	struct vec3 temp;
+	vec3_mulf(&temp, &p->dir, vec3_plane_dist(v, p) * 2.0f);
+	vec3_sub(dst, v, &temp);
+}
+
+void vec3_mirrorv(struct vec3 *dst, const struct vec3 *v,
+		const struct vec3 *vec)
+{
+	struct vec3 temp;
+	vec3_mulf(&temp, vec, vec3_dot(v, vec) * 2.0f);
+	vec3_sub(dst, v, &temp);
+}
+
+void vec3_rand(struct vec3 *dst, int positive_only)
+{
+	dst->x = rand_float(positive_only);
+	dst->y = rand_float(positive_only);
+	dst->z = rand_float(positive_only);
+	dst->w = 0.0f;
+}

+ 236 - 0
libobs/graphics/vec3.h

@@ -0,0 +1,236 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef VECT_H
+#define VECT_H
+
+#include "math-defs.h"
+#include <xmmintrin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct plane;
+struct matrix3;
+struct quat;
+
+struct vec3 {
+	union {
+		struct {
+			float x, y, z, w;
+		};
+		float ptr[4];
+		__m128 m;
+	};
+};
+
+static inline void vec3_zero(struct vec3 *v)
+{
+	v->m = _mm_setzero_ps();
+}
+
+static inline void vec3_set(struct vec3 *dst, float x, float y, float z)
+{
+	dst->m = _mm_set_ps(0.0f, z, y, x);
+}
+
+static inline void vec3_copy(struct vec3 *dst, const struct vec3 *v)
+{
+	dst->m = v->m;
+}
+
+static inline void vec3_add(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	dst->m = _mm_add_ps(v1->m, v2->m);
+	dst->w = 0.0f;
+}
+
+static inline void vec3_sub(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	dst->m = _mm_sub_ps(v1->m, v2->m);
+	dst->w = 0.0f;
+}
+
+static inline void vec3_mul(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	dst->m = _mm_mul_ps(v1->m, v2->m);
+}
+
+static inline void vec3_div(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	dst->m = _mm_div_ps(v1->m, v2->m);
+	dst->w = 0.0f;
+}
+
+static inline void vec3_addf(struct vec3 *dst, const struct vec3 *v,
+		float f)
+{
+	dst->m = _mm_add_ps(v->m, _mm_set1_ps(f));
+	dst->w = 0.0f;
+}
+
+static inline void vec3_subf(struct vec3 *dst, const struct vec3 *v,
+		float f)
+{
+	dst->m = _mm_sub_ps(v->m, _mm_set1_ps(f));
+	dst->w = 0.0f;
+}
+
+static inline void vec3_mulf(struct vec3 *dst, const struct vec3 *v,
+		float f)
+{
+	dst->m = _mm_mul_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline void vec3_divf(struct vec3 *dst, const struct vec3 *v,
+		float f)
+{
+	dst->m = _mm_div_ps(v->m, _mm_set1_ps(f));
+	dst->w = 0.0f;
+}
+
+static inline float vec3_dot(const struct vec3 *v1, const struct vec3 *v2)
+{
+	struct vec3 add;
+	__m128 mul = _mm_mul_ps(v1->m, v2->m);
+	add.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);
+	add.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);
+	return add.x;
+}
+
+static inline void vec3_cross(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	__m128 s1v1 = _mm_shuffle_ps(v1->m, v1->m, _MM_SHUFFLE(3, 0, 2, 1));
+	__m128 s1v2 = _mm_shuffle_ps(v2->m, v2->m, _MM_SHUFFLE(3, 1, 0, 2));
+	__m128 s2v1 = _mm_shuffle_ps(v1->m, v1->m, _MM_SHUFFLE(3, 1, 0, 2));
+	__m128 s2v2 = _mm_shuffle_ps(v2->m, v2->m, _MM_SHUFFLE(3, 0, 2, 1));
+	dst->m = _mm_sub_ps(_mm_mul_ps(s1v1, s1v2), _mm_mul_ps(s2v1, s2v2));
+}
+
+static inline void vec3_neg(struct vec3 *dst, const struct vec3 *v)
+{
+	dst->x = -dst->x;
+	dst->y = -dst->y;
+	dst->z = -dst->z;
+}
+
+static inline float vec3_len(const struct vec3 *v)
+{
+	float dot_val = vec3_dot(v, v);
+	return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
+}
+
+static inline float vec3_dist(const struct vec3 *v1, const struct vec3 *v2)
+{
+	struct vec3 temp;
+	float dot_val;
+
+	vec3_sub(&temp, v1, v2);
+	dot_val = vec3_dot(&temp, &temp);
+	return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
+}
+
+static inline void vec3_norm(struct vec3 *dst, const struct vec3 *v)
+{
+	float dot_val = vec3_dot(v, v);
+	dst->m = (dot_val > 0.0f) ?
+		_mm_mul_ps(v->m, _mm_set1_ps(1.0f/sqrtf(dot_val))) :
+		_mm_setzero_ps();
+}
+
+static inline bool vec3_close(const struct vec3 *v1, const struct vec3 *v2,
+		float epsilon)
+{
+	struct vec3 test;
+	vec3_sub(&test, v1, v2);
+	return test.x < epsilon && test.y < epsilon && test.z < epsilon;
+}
+
+static inline void vec3_min(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	dst->m = _mm_min_ps(v1->m, v2->m);
+	dst->w = 0.0f;
+}
+
+static inline void vec3_minf(struct vec3 *dst, const struct vec3 *v,
+		float f)
+{
+	dst->m = _mm_min_ps(v->m, _mm_set1_ps(f));
+	dst->w = 0.0f;
+}
+
+static inline void vec3_max(struct vec3 *dst, const struct vec3 *v1,
+		const struct vec3 *v2)
+{
+	dst->m = _mm_max_ps(v1->m, v2->m);
+	dst->w = 0.0f;
+}
+
+static inline void vec3_maxf(struct vec3 *dst, const struct vec3 *v,
+		float f)
+{
+	dst->m = _mm_max_ps(v->m, _mm_set1_ps(f));
+	dst->w = 0.0f;
+}
+
+static inline void vec3_abs(struct vec3 *dst, const struct vec3 *v)
+{
+	dst->x = fabsf(v->x);
+	dst->y = fabsf(v->y);
+	dst->z = fabsf(v->z);
+}
+
+static inline void vec3_floor(struct vec3 *dst, const struct vec3 *v)
+{
+	dst->x = floorf(v->x);
+	dst->y = floorf(v->y);
+	dst->z = floorf(v->z);
+}
+
+static inline void vec3_ceil(struct vec3 *dst, const struct vec3 *v)
+{
+	dst->x = ceilf(v->x);
+	dst->y = ceilf(v->y);
+	dst->z = ceilf(v->z);
+}
+
+EXPORT float vec3_plane_dist(const struct vec3 *v, const struct plane *p);
+
+EXPORT void vec3_rotate(struct vec3 *dst, const struct vec3 *v,
+		const struct matrix3 *m);
+EXPORT void vec3_transform(struct vec3 *dst, const struct vec3 *v,
+		const struct matrix3 *m);
+
+EXPORT void vec3_mirror(struct vec3 *dst, const struct vec3 *v,
+		const struct plane *p);
+EXPORT void vec3_mirrorv(struct vec3 *dst, const struct vec3 *v,
+		const struct vec3 *vec);
+
+EXPORT void vec3_rand(struct vec3 *dst, int positive_only);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 32 - 0
libobs/graphics/vec4.c

@@ -0,0 +1,32 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "vec4.h"
+#include "matrix4.h"
+
+void vec4_transform(struct vec4 *dst, const struct vec4 *v,
+		const struct matrix4 *m)
+{
+	struct vec4 temp;
+
+	temp.x = vec4_dot(&m->x, v);
+	temp.y = vec4_dot(&m->y, v);
+	temp.z = vec4_dot(&m->z, v);
+	temp.w = vec4_dot(&m->t, v);
+
+	vec4_copy(dst, &temp);
+}

+ 253 - 0
libobs/graphics/vec4.h

@@ -0,0 +1,253 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef VECT4_H
+#define VECT4_H
+
+#include "math-defs.h"
+#include <xmmintrin.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct matrix4;
+
+struct vec4 {
+	union {
+		struct {
+			float x, y, z, w;
+		};
+		float ptr[4];
+		__m128 m;
+	};
+};
+
+static inline void vec4_zero(struct vec4 *v)
+{
+	v->m = _mm_setzero_ps();
+}
+
+static inline void vec4_set(struct vec4 *dst, float x, float y, float z,
+		float w)
+{
+	dst->m = _mm_set_ps(w, z, y, x);
+}
+
+static inline void vec4_copy(struct vec4 *dst, const struct vec4 *v)
+{
+	dst->m = v->m;
+}
+
+static inline void vec4_add(struct vec4 *dst, const struct vec4 *v1,
+		const struct vec4 *v2)
+{
+	dst->m = _mm_add_ps(v1->m, v2->m);
+}
+
+static inline void vec4_sub(struct vec4 *dst, const struct vec4 *v1,
+		const struct vec4 *v2)
+{
+	dst->m = _mm_sub_ps(v1->m, v2->m);
+}
+
+static inline void vec4_mul(struct vec4 *dst, const struct vec4 *v1,
+		const struct vec4 *v2)
+{
+	dst->m = _mm_mul_ps(v1->m, v2->m);
+}
+
+static inline void vec4_div(struct vec4 *dst, const struct vec4 *v1,
+		const struct vec4 *v2)
+{
+	dst->m = _mm_div_ps(v1->m, v2->m);
+}
+
+static inline void vec4_addf(struct vec4 *dst, const struct vec4 *v,
+		float f)
+{
+	dst->m = _mm_add_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline void vec4_subf(struct vec4 *dst, const struct vec4 *v,
+		float f)
+{
+	dst->m = _mm_sub_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline void vec4_mulf(struct vec4 *dst, const struct vec4 *v,
+		float f)
+{
+	dst->m = _mm_mul_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline void vec4_divf(struct vec4 *dst, const struct vec4 *v,
+		float f)
+{
+	dst->m = _mm_div_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline float vec4_dot(const struct vec4 *v1, const struct vec4 *v2)
+{
+	struct vec4 add;
+	__m128 mul = _mm_mul_ps(v1->m, v2->m);
+	add.m = _mm_add_ps(_mm_movehl_ps(mul, mul), mul);
+	add.m = _mm_add_ps(_mm_shuffle_ps(add.m, add.m, 0x55), add.m);
+	return add.x;
+}
+
+static inline void vec4_neg(struct vec4 *dst, const struct vec4 *v)
+{
+	dst->x = -dst->x;
+	dst->y = -dst->y;
+	dst->z = -dst->z;
+	dst->w = -dst->w;
+}
+
+static inline float vec4_len(const struct vec4 *v)
+{
+	float dot_val = vec4_dot(v, v);
+	return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
+}
+
+static inline float vec4_dist(const struct vec4 *v1, const struct vec4 *v2)
+{
+	struct vec4 temp;
+	float dot_val;
+
+	vec4_sub(&temp, v1, v2);
+	dot_val = vec4_dot(&temp, &temp);
+	return (dot_val > 0.0f) ? sqrtf(dot_val) : 0.0f;
+}
+
+static inline void vec4_norm(struct vec4 *dst, const struct vec4 *v)
+{
+	float dot_val = vec4_dot(v, v);
+	dst->m = (dot_val > 0.0f) ?
+		_mm_mul_ps(v->m, _mm_set1_ps(1.0f/sqrtf(dot_val))) :
+		_mm_setzero_ps();
+}
+
+static inline int vec4_close(const struct vec4 *v1, const struct vec4 *v2,
+		float epsilon)
+{
+	struct vec4 test;
+	vec4_sub(&test, v1, v2);
+	return test.x < epsilon &&
+	       test.y < epsilon &&
+	       test.z < epsilon &&
+	       test.w < epsilon;
+}
+
+static inline void vec4_min(struct vec4 *dst, const struct vec4 *v1,
+		const struct vec4 *v2)
+{
+	dst->m = _mm_min_ps(v1->m, v2->m);
+}
+
+static inline void vec4_minf(struct vec4 *dst, const struct vec4 *v,
+		float f)
+{
+	dst->m = _mm_min_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline void vec4_max(struct vec4 *dst, const struct vec4 *v1,
+		const struct vec4 *v2)
+{
+	dst->m = _mm_max_ps(v1->m, v2->m);
+}
+
+static inline void vec4_maxf(struct vec4 *dst, const struct vec4 *v,
+		float f)
+{
+	dst->m = _mm_max_ps(v->m, _mm_set1_ps(f));
+}
+
+static inline void vec4_abs(struct vec4 *dst, const struct vec4 *v)
+{
+	dst->x = fabsf(v->x);
+	dst->y = fabsf(v->y);
+	dst->z = fabsf(v->z);
+	dst->w = fabsf(v->w);
+}
+
+static inline void vec4_floor(struct vec4 *dst, const struct vec4 *v)
+{
+	dst->x = floorf(v->x);
+	dst->y = floorf(v->y);
+	dst->z = floorf(v->z);
+	dst->w = floorf(v->w);
+}
+
+static inline void vec4_ceil(struct vec4 *dst, const struct vec4 *v)
+{
+	dst->x = ceilf(v->x);
+	dst->y = ceilf(v->y);
+	dst->z = ceilf(v->z);
+	dst->w = ceilf(v->w);
+}
+
+static inline uint32_t vec4_to_rgba(const struct vec4 *src)
+{
+	uint32_t val;
+	val  = (uint32_t)((double)src->x * 255.0);
+	val |= (uint32_t)((double)src->y * 255.0);
+	val |= (uint32_t)((double)src->z * 255.0);
+	val |= (uint32_t)((double)src->w * 255.0);
+	return val;
+}
+
+static inline uint32_t vec4_to_bgra(const struct vec4 *src)
+{
+	uint32_t val;
+	val  = (uint32_t)((double)src->z * 255.0);
+	val |= (uint32_t)((double)src->y * 255.0);
+	val |= (uint32_t)((double)src->x * 255.0);
+	val |= (uint32_t)((double)src->w * 255.0);
+	return val;
+}
+
+static inline void vec4_from_rgba(struct vec4 *dst, uint32_t rgba)
+{
+	dst->x = (float)((double)(rgba&0xFF) * (1.0/255.0));
+	rgba >>= 8;
+	dst->y = (float)((double)(rgba&0xFF) * (1.0/255.0));
+	rgba >>= 8;
+	dst->z = (float)((double)(rgba&0xFF) * (1.0/255.0));
+	rgba >>= 8;
+	dst->w = (float)((double)(rgba&0xFF) * (1.0/255.0));
+}
+
+static inline void vec4_from_bgra(struct vec4 *dst, uint32_t bgra)
+{
+	dst->z = (float)((double)(bgra&0xFF) * (1.0/255.0));
+	bgra >>= 8;
+	dst->y = (float)((double)(bgra&0xFF) * (1.0/255.0));
+	bgra >>= 8;
+	dst->x = (float)((double)(bgra&0xFF) * (1.0/255.0));
+	bgra >>= 8;
+	dst->w = (float)((double)(bgra&0xFF) * (1.0/255.0));
+}
+
+EXPORT void vec4_transform(struct vec4 *dst, const struct vec4 *v,
+		const struct matrix4 *m);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 67 - 0
libobs/makefile

@@ -0,0 +1,67 @@
+include ../config.mak
+
+.PHONY: all default clean
+
+all: default
+
+SRCFILES=util/bmem.c \
+	 util/base.c \
+	 util/dstr.c \
+	 util/lexer.c \
+	 util/utf8.c \
+	 util/text-lookup.c \
+	 util/platform.c \
+	 util/platform-windows.c \
+	 util/config-file.c \
+	 util/cf-lexer.c \
+	 util/cf-parser.c \
+	 graphics/axisang.c \
+	 graphics/bounds.c \
+	 graphics/effect.c \
+	 graphics/effect-parser.c \
+	 graphics/graphics.c \
+	 graphics/graphics-imports.c \
+	 graphics/math-extra.c \
+	 graphics/matrix3.c \
+	 graphics/matrix4.c \
+	 graphics/plane.c \
+	 graphics/quat.c \
+	 graphics/shader-parser.c \
+	 graphics/texture-render.c \
+	 graphics/vec2.c \
+	 graphics/vec3.c \
+	 graphics/vec4.c \
+	 media-io/video-io.c \
+	 media-io/audio-io.c \
+	 media-io/media-io.c \
+	 obs-module.c \
+	 obs-output.c \
+	 obs-source.c \
+	 obs-scene.c \
+	 obs-display.c \
+	 obs-video.c \
+	 obs.c
+
+SONAME=../build/libobs.$(SOEXT)
+
+OBJS += $(SRCFILES:%.c=%.$(OBJ))
+LDFLAGS += -lpthread
+
+default: $(SONAME)
+
+.depend:
+	@rm -f .depend
+	@$(foreach SRC, $(addprefix $(SRCPATH)/, $(SRCFILES)), $(CCDEP) \
+	       $(CPPFLAGS) $(SRC) \
+	       -MT $(SRC:$(SRCPATH)/%.c=%.$(OBJ)) -MM 1>> .depend;)
+
+$(SONAME): .depend $(OBJS)
+	$(LD)$@ $(LDFLAGS) $(OBJS)
+
+depend: .depend
+ifneq ($(wildcard .depend),)
+include .depend
+endif
+
+clean:
+	rm -f $(OBJS) $(SONAME) *.a *.lib *.exp *.pdb .depend

+ 134 - 0
libobs/media-io/audio-io.c

@@ -0,0 +1,134 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "../util/threading.h"
+#include "../util/darray.h"
+#include "../util/platform.h"
+
+#include "audio-io.h"
+
+/* TODO: Incomplete */
+
+struct audio_output {
+	struct audio_info info;
+	media_t           media;
+	media_output_t    output;
+
+	pthread_t         thread;
+	pthread_mutex_t   data_mutex;
+	event_t           stop_event;
+
+	struct darray     pending_frames;
+
+	bool              initialized;
+};
+
+/* ------------------------------------------------------------------------- */
+
+static void *audio_thread(void *param)
+{
+	struct audio_output *audio = param;
+
+	while (event_try(&audio->stop_event) == EAGAIN) {
+		os_sleep_ms(5);
+		/* TODO */
+	}
+
+	return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline bool valid_audio_params(struct audio_info *info)
+{
+	return info->channels > 0 && info->format && info->name &&
+		info->samples_per_sec > 0 && info->speakers > 0;
+}
+
+static inline bool ao_add_to_media(audio_t audio)
+{
+	struct media_output_info oi;
+	oi.format  = audio->info.format;
+	oi.obj     = audio;
+	oi.connect = NULL;
+
+	audio->output = media_output_create(&oi);
+	if (!audio->output)
+		return false;
+
+	media_add_output(audio->media, audio->output);
+	return true;
+}
+
+int audio_output_open(audio_t *audio, media_t media, struct audio_info *info)
+{
+	struct audio_output *out;
+
+	if (!valid_audio_params(info))
+		return AUDIO_OUTPUT_INVALIDPARAM;
+
+	out = bmalloc(sizeof(struct audio_output));
+	memset(out, 0, sizeof(struct audio_output));
+
+	memcpy(&out->info, info, sizeof(struct audio_info));
+	out->media = media;
+
+	if (pthread_mutex_init(&out->data_mutex, NULL) != 0)
+		goto fail;
+
+	if (event_init(&out->stop_event, true) != 0)
+		goto fail;
+
+	if (!ao_add_to_media(out))
+		goto fail;
+
+	if (pthread_create(&out->thread, NULL, audio_thread, out) != 0)
+		goto fail;
+
+	out->initialized = true;
+	*audio = out;
+	return AUDIO_OUTPUT_SUCCESS;
+
+fail:
+	audio_output_close(out);
+	return AUDIO_OUTPUT_FAIL;
+}
+
+void audio_output_data(audio_t audio, struct audio_data *data)
+{
+	pthread_mutex_lock(&audio->data_mutex);
+	/* TODO */
+	pthread_mutex_unlock(&audio->data_mutex);
+}
+
+void audio_output_close(audio_t audio)
+{
+	void *thread_ret;
+
+	if (!audio)
+		return;
+
+	if (audio->initialized) {
+		event_signal(&audio->stop_event);
+		pthread_join(audio->thread, &thread_ret);
+	}
+
+	media_remove_output(audio->media, audio->output);
+	event_destroy(&audio->stop_event);
+	pthread_mutex_destroy(&audio->data_mutex);
+	bfree(audio);
+}

+ 91 - 0
libobs/media-io/audio-io.h

@@ -0,0 +1,91 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef AUDIO_IO_H
+#define AUDIO_IO_H
+
+#include "../util/c99defs.h"
+#include "media-io.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Base audio output component.  Use this to create an audio output track
+ * for the media.
+ */
+
+struct audio_output;
+typedef struct audio_output *audio_t;
+
+enum audio_type {
+	AUDIO_FORMAT_UNKNOWN,
+	AUDIO_FORMAT_8BIT,
+	AUDIO_FORMAT_16BIT,
+	AUDIO_FORMAT_24BIT,
+	AUDIO_FORMAT_32BIT,
+	AUDIO_FORMAT_FLOAT,
+};
+
+enum speaker_setup {
+	SPEAKERS_UNKNOWN,
+	SPEAKERS_MONO,
+	SPEAKERS_STEREO,
+	SPEAKERS_2POINT1,
+	SPEAKERS_QUAD,
+	SPEAKERS_4POINT1,
+	SPEAKERS_5POINT1,
+	SPEAKERS_5POINT1_SURROUND,
+	SPEAKERS_7POINT1,
+	SPEAKERS_7POINT1_SURROUND,
+	SPEAKERS_SURROUND,
+};
+
+struct audio_data {
+	void            *data;
+	uint32_t        frames;
+	uint32_t        speakers;
+	uint32_t        samples_per_sec;
+	enum audio_type type;
+	uint64_t        timestamp;
+};
+
+struct audio_info {
+	const char         *name;
+	const char         *format;
+
+	uint32_t           channels;
+	uint32_t           samples_per_sec;
+	enum audio_type    type;
+	enum speaker_setup speakers;
+};
+
+#define AUDIO_OUTPUT_SUCCESS       0
+#define AUDIO_OUTPUT_INVALIDPARAM -1
+#define AUDIO_OUTPUT_FAIL         -2
+
+EXPORT int audio_output_open(audio_t *audio, media_t media,
+		struct audio_info *info);
+EXPORT void audio_output_data(audio_t audio, struct audio_data *data);
+EXPORT void audio_output_close(audio_t audio);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 0 - 0
libobs/media-io/audio-mixer.h


+ 158 - 0
libobs/media-io/media-io.c

@@ -0,0 +1,158 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "../util/threading.h"
+#include "../util/darray.h"
+#include "../util/bmem.h"
+
+#include "media-io.h"
+
+/* TODO: Incomplete */
+
+struct media_input {
+	struct media_input_info info;
+	struct media_output *connection;
+};
+
+struct media_output {
+	struct media_output_info info;
+	DARRAY(media_input_t) connections;
+	pthread_mutex_t mutex;
+};
+
+struct media_data {
+	DARRAY(media_output_t) outputs;
+};
+
+/* ------------------------------------------------------------------------- */
+
+media_input_t media_input_create(struct media_input_info *info)
+{
+	struct media_input *input;
+
+	if (!info || !info->format || !info->on_input)
+		return NULL;
+       
+	input = bmalloc(sizeof(struct media_input));
+	input->connection = NULL;
+	memcpy(&input->info, info, sizeof(struct media_input_info));
+
+	return input;
+}
+
+void media_input_destroy(media_input_t input)
+{
+	if (input)
+		bfree(input);
+}
+
+/* ------------------------------------------------------------------------- */
+
+media_output_t media_output_create(struct media_output_info *info)
+{
+	struct media_output *output;
+
+	if (!info || !info->format)
+		return NULL;
+
+	output = bmalloc(sizeof(struct media_output));
+	da_init(output->connections);
+	memcpy(&output->info, info, sizeof(struct media_output_info));
+
+	if (pthread_mutex_init(&output->mutex, NULL) != 0) {
+		bfree(output);
+		return NULL;
+	}
+
+	return output;
+}
+
+void media_output_data(media_output_t output, const void *data)
+{
+	size_t i;
+
+	pthread_mutex_lock(&output->mutex);
+
+	for (i = 0; i < output->connections.num; i++) {
+		media_input_t input = output->connections.array[i];
+		input->info.on_input(input->info.obj, data);
+	}
+
+	pthread_mutex_unlock(&output->mutex);
+}
+
+void media_output_destroy(media_output_t output)
+{
+	if (output) {
+		da_free(output->connections);
+		pthread_mutex_destroy(&output->mutex);
+		bfree(output);
+	}
+}
+
+/* ------------------------------------------------------------------------- */
+
+media_t media_open(void)
+{
+	struct media_data *media = bmalloc(sizeof(struct media_data));
+	da_init(media->outputs);
+
+	return media;
+}
+
+bool media_add_input(media_t media, media_input_t input)
+{
+	media_output_t *outputs = media->outputs.array;
+	size_t i;
+
+	for (i = 0; i < media->outputs.num; i++) {
+		media_output_t output = outputs[i];
+
+		if (strcmp(output->info.format, input->info.format) == 0) {
+			da_push_back(output->connections, input);
+			return true;
+		}
+	}
+
+	return false;
+}
+
+void media_add_output(media_t media, media_output_t output)
+{
+	da_push_back(media->outputs, output);
+}
+
+void media_remove_input(media_t media, media_input_t input)
+{
+	if (!input->connection)
+		return;
+
+	da_erase_item(input->connection->connections, input);
+}
+
+void media_remove_output(media_t media, media_output_t output)
+{
+	da_erase_item(media->outputs, output);
+}
+
+void media_close(media_t media)
+{
+	if (media) {
+		da_free(media->outputs);
+		bfree(media);
+	}
+}

+ 77 - 0
libobs/media-io/media-io.h

@@ -0,0 +1,77 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef MEDIA_IO_H
+#define MEDIA_IO_H
+
+/*
+ * Media input/output components used for connecting media outputs/inputs
+ * together.  An input requests a connection to an output and the output
+ * sends frames to it through the callbacks in media_data_in structure.
+ *
+ * The id member should indicate the format/parameters used in text form.
+ */
+
+/* opaque data types */
+struct media_input;
+struct media_output;
+struct media_data;
+typedef struct media_input  *media_input_t;
+typedef struct media_output *media_output_t;
+typedef struct media_data   *media_t;
+
+#include "../util/c99defs.h"
+#include "video-io.h"
+#include "audio-io.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct media_input_info {
+	const char *format;
+
+	void *obj;
+	void (*on_input)(void *obj, const void *data);
+};
+
+struct media_output_info {
+	const char *format;
+
+	void *obj;
+	bool (*connect)(void *obj, media_input_t input);
+};
+
+EXPORT media_input_t media_input_create(struct media_input_info *info);
+EXPORT void          media_input_destroy(media_input_t input);
+
+EXPORT media_output_t media_output_create(struct media_output_info *info);
+EXPORT void           media_output_data(media_output_t out, const void *data);
+EXPORT void           media_output_destroy(media_output_t output);
+
+EXPORT media_t media_open(void);
+EXPORT bool    media_add_input(media_t media, media_input_t input);
+EXPORT void    media_add_output(media_t media, media_output_t output);
+EXPORT void    media_remove_input(media_t media, media_input_t input);
+EXPORT void    media_remove_output(media_t media, media_output_t output);
+EXPORT void    media_close(media_t media);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 194 - 0
libobs/media-io/video-io.c

@@ -0,0 +1,194 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include <assert.h>
+#include "../util/bmem.h"
+#include "../util/platform.h"
+#include "../util/threading.h"
+
+#include "video-io.h"
+
+struct video_output {
+	struct video_info  info;
+	media_t            media;
+	media_output_t     output;
+
+	pthread_t          thread;
+	pthread_mutex_t    data_mutex;
+	event_t            stop_event;
+
+	struct video_frame *cur_frame;
+	struct video_frame *next_frame;
+	event_t            update_event;
+	uint64_t           frame_time;
+	volatile uint64_t  cur_video_time;
+
+	bool               initialized;
+};
+
+/* ------------------------------------------------------------------------- */
+
+static inline void video_swapframes(struct video_output *video)
+{
+	pthread_mutex_lock(&video->data_mutex);
+
+	if (video->next_frame) {
+		video->cur_frame = video->next_frame;
+		video->next_frame = NULL;
+	}
+
+	pthread_mutex_unlock(&video->data_mutex);
+}
+
+static void *video_thread(void *param)
+{
+	struct video_output *video = param;
+	uint64_t cur_time = os_gettime_ns();
+
+	while (event_try(&video->stop_event) == EAGAIN) {
+		/* wait half a frame, update frame */
+		os_sleepto_ns(cur_time += (video->frame_time/2));
+		video->cur_video_time = cur_time;
+		event_signal(&video->update_event);
+
+		/* wait another half a frame, swap and output frames */
+		os_sleepto_ns(cur_time += (video->frame_time/2));
+		video_swapframes(video);
+		if (video->cur_frame)
+			media_output_data(video->output, video->cur_frame);
+	}
+
+	return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline bool valid_video_params(struct video_info *info)
+{
+	return info->height != 0 && info->width != 0 && info->fps_den != 0 &&
+	       info->fps_num != 0 && info->format != NULL;
+}
+
+static inline bool vo_add_to_media(video_t video)
+{
+	struct media_output_info oi;
+	oi.format  = video->info.format;
+	oi.obj     = video;
+	oi.connect = NULL;
+
+	video->output = media_output_create(&oi);
+	if (!video->output)
+		return false;
+
+	media_add_output(video->media, video->output);
+	return true;
+}
+
+int video_output_open(video_t *video, media_t media, struct video_info *info)
+{
+	struct video_output *out;
+
+	if (!valid_video_params(info))
+		return VIDEO_OUTPUT_INVALIDPARAM;
+
+	out = bmalloc(sizeof(struct video_output));
+	memset(out, 0, sizeof(struct video_output));
+
+	memcpy(&out->info, info, sizeof(struct video_info));
+	out->frame_time = (uint64_t)(1000000000.0 * (double)info->fps_den /
+		(double)info->fps_num);
+	out->media = media;
+	out->initialized = false;
+
+	if (pthread_mutex_init(&out->data_mutex, NULL) != 0)
+		goto fail;
+
+	if (event_init(&out->stop_event, true) != 0)
+		goto fail;
+
+	if (event_init(&out->update_event, false) != 0)
+		goto fail;
+
+	if (!vo_add_to_media(out))
+		goto fail;
+
+	if (pthread_create(&out->thread, NULL, video_thread, out) != 0)
+		goto fail;
+
+	out->initialized = true;
+	*video = out;
+	return VIDEO_OUTPUT_SUCCESS;
+
+fail:
+	video_output_close(out);
+	return VIDEO_OUTPUT_FAIL;
+}
+
+void video_output_frame(video_t video, struct video_frame *frame)
+{
+	pthread_mutex_lock(&video->data_mutex);
+	video->next_frame = frame;
+	pthread_mutex_unlock(&video->data_mutex);
+}
+
+bool video_output_wait(video_t video)
+{
+	event_wait(&video->update_event);
+	return event_try(&video->stop_event) == EAGAIN;
+}
+
+uint64_t video_getframetime(video_t video)
+{
+	return video->frame_time;
+}
+
+uint64_t video_gettime(video_t video)
+{
+	return video->cur_video_time;
+}
+
+void video_output_stop(video_t video)
+{
+	void *thread_ret;
+
+	if (!video)
+		return;
+
+	if (video->initialized) {
+		event_signal(&video->stop_event);
+		pthread_join(video->thread, &thread_ret);
+		event_signal(&video->update_event);
+	}
+}
+
+void video_output_close(video_t video)
+{
+	if (!video)
+		return;
+
+	video_output_stop(video);
+
+	if (video->output) {
+		media_remove_output(video->media, video->output);
+		media_output_destroy(video->output);
+	}
+
+	event_destroy(&video->update_event);
+	event_destroy(&video->stop_event);
+	pthread_mutex_destroy(&video->data_mutex);
+	bfree(video);
+}

+ 69 - 0
libobs/media-io/video-io.h

@@ -0,0 +1,69 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef VIDEO_IO_H
+#define VIDEO_IO_H
+
+#include "../util/c99defs.h"
+#include "media-io.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Base video output component.  Use this to create an video output track
+ * for the media.
+ */
+
+struct video_output;
+typedef struct video_output *video_t;
+
+struct video_frame {
+	const void *data;
+	uint32_t   row_size;
+	uint64_t   timestamp;
+};
+
+struct video_info {
+	const char *name;
+	const char *format;
+
+	uint32_t   fps_num; /* numerator */
+	uint32_t   fps_den; /* denominator */
+	uint32_t   width;
+	uint32_t   height;
+};
+
+#define VIDEO_OUTPUT_SUCCESS       0
+#define VIDEO_OUTPUT_INVALIDPARAM -1
+#define VIDEO_OUTPUT_FAIL         -2
+
+EXPORT int      video_output_open(video_t *video, media_t media,
+		struct video_info *info);
+EXPORT void     video_output_frame(video_t video, struct video_frame *frame);
+EXPORT bool     video_output_wait(video_t video);
+EXPORT uint64_t video_getframetime(video_t video);
+EXPORT uint64_t video_gettime(video_t video);
+EXPORT void     video_output_stop(video_t video);
+EXPORT void     video_output_close(video_t video);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 83 - 0
libobs/obs-data.h

@@ -0,0 +1,83 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef OBS_DATA_H
+#define OBS_DATA_H
+
+#include "util/darray.h"
+#include "util/threading.h"
+
+#include "graphics/graphics.h"
+
+#include "media-io/media-io.h"
+#include "media-io/video-io.h"
+#include "media-io/audio-io.h"
+
+#include "obs.h"
+#include "obs-module.h"
+#include "obs-source.h"
+#include "obs-output.h"
+/*#include "obs-service.h"*/
+
+#define NUM_TEXTURES 2
+
+struct obs_display {
+	swapchain_t swap; /* can be NULL if just sound */
+	source_t    source;
+	/* TODO: sound output target */
+};
+
+struct obs_data {
+	DARRAY(struct obs_module)       modules;
+
+	DARRAY(struct source_info)      input_types;
+	DARRAY(struct source_info)      filter_types;
+	DARRAY(struct source_info)      transition_types;
+	DARRAY(struct output_info)      output_types;
+	/*DARRAY(struct service_info)     service_types;*/
+
+	DARRAY(struct obs_display*)     displays;
+	DARRAY(struct obs_source*)      sources;
+
+	/* graphics */
+	graphics_t  graphics;
+	stagesurf_t copy_surfaces[NUM_TEXTURES];
+	bool        textures_copied[NUM_TEXTURES];
+	bool        copy_mapped;
+	int         cur_texture;
+
+	/* TODO: sound output stuff */
+
+	/* media */
+	media_t    media;
+	video_t    video;
+	audio_t    audio;
+
+	uint32_t output_width;
+	uint32_t output_height;
+
+	/* threading */
+	pthread_t       video_thread;
+	pthread_mutex_t source_mutex;
+	bool            thread_initialized;
+
+	source_t primary_source;
+};
+
+extern void *obs_video_thread(void *param);
+
+#endif

+ 30 - 0
libobs/obs-defs.h

@@ -0,0 +1,30 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef OBS_DEFS_H
+#define OBS_DEFS_H
+
+#define MODULE_SUCCESS           0
+#define MODULE_ERROR            -1
+#define MODULE_FILENOTFOUND     -2
+#define MODULE_FUNCTIONNOTFOUND -3
+
+#define SOURCE_VIDEO (1<<0)
+#define SOURCE_AUDIO (1<<1)
+#define SOURCE_ASYNC (1<<2)
+
+#endif

+ 53 - 0
libobs/obs-display.c

@@ -0,0 +1,53 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "obs.h"
+#include "obs-data.h"
+
+display_t display_create(obs_t obs, struct gs_init_data *graphics_data)
+{
+	struct obs_display *display = bmalloc(sizeof(struct obs_display));
+	memset(display, 0, sizeof(struct obs_display));
+
+	if (graphics_data) {
+		display->swap = gs_create_swapchain(graphics_data);
+		if (!display->swap) {
+			display_destroy(display);
+			return NULL;
+		}
+	}
+
+	return display;
+}
+
+void display_destroy(display_t display)
+{
+	if (display) {
+		swapchain_destroy(display->swap);
+		bfree(display);
+	}
+}
+
+source_t display_getsource(display_t display)
+{
+	return display->source;
+}
+
+void display_setsource(display_t display, source_t source)
+{
+	display->source = source;
+}

+ 127 - 0
libobs/obs-module.c

@@ -0,0 +1,127 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "util/platform.h"
+#include "util/dstr.h"
+
+#include "obs-defs.h"
+#include "obs-data.h"
+#include "obs-module.h"
+
+void *load_module_subfunc(void *module, const char *module_name,
+		const char *name, const char *func, bool required)
+{
+	struct dstr func_name;
+	void *func_addr = NULL;
+
+	dstr_init_copy(&func_name, name);
+	dstr_cat(&func_name, "_");
+	dstr_cat(&func_name, func);
+
+	func_addr = os_dlsym(module, func_name.array);
+	if (required && !func_addr)
+		blog(LOG_ERROR, "Could not load function '%s' from module '%s'",
+				func_name.array, module_name);
+
+	dstr_free(&func_name);
+	return func_addr;
+}
+
+static void module_load_exports(struct obs_data *obs, struct obs_module *mod,
+		struct darray *output_array, const char *type,
+		const size_t data_size, void *callback_ptr)
+{
+	bool (*enum_func)(size_t idx, const char **name);
+	bool (*callback)(void*, const char*, const char*, void*);
+	struct dstr enum_name;
+	const char *name;
+	size_t i = 0;
+
+	callback = callback_ptr;
+
+	dstr_init_copy(&enum_name, "enum_");
+	dstr_cat(&enum_name, type);
+
+	enum_func = os_dlsym(mod->module, enum_name.array);
+	if (!enum_func)
+		goto complete;
+
+	while (enum_func(i++, &name)) {
+		void *info = bmalloc(data_size);
+		if (!callback(mod->module, mod->name, name, info))
+			blog(LOG_ERROR, "Couldn't load '%s' because it "
+			                "was missing required functions",
+			                name);
+		else
+			darray_push_back(data_size, output_array, info);
+
+		bfree(info);
+	}
+
+complete:
+	dstr_free(&enum_name);
+}
+
+int obs_load_module(struct obs_data *obs, const char *path)
+{
+	struct obs_module mod;
+	bool (*module_load)(void) = NULL;
+
+	mod.module = os_dlopen(path);
+	if (!mod.module)
+		return MODULE_FILENOTFOUND;
+
+	module_load = os_dlsym(mod.module, "module_load");
+	if (module_load) {
+		if (!module_load()) {
+			os_dlclose(mod.module);
+			return MODULE_ERROR;
+		}
+	}
+
+	mod.name = bstrdup(path);
+	module_load_exports(obs, &mod, &obs->input_types.da, "inputs",
+			sizeof(struct source_info), get_source_info);
+	module_load_exports(obs, &mod, &obs->filter_types.da, "filters",
+			sizeof(struct source_info), get_source_info);
+	module_load_exports(obs, &mod, &obs->transition_types.da, "transitions",
+			sizeof(struct source_info), get_source_info);
+	module_load_exports(obs, &mod, &obs->output_types.da, "outputs",
+			sizeof(struct output_info), get_output_info);
+
+	da_push_back(obs->modules, &mod);
+	return MODULE_SUCCESS;
+}
+
+void free_module(struct obs_module *mod)
+{
+	if (!mod)
+		return;
+
+	if (mod->module) {
+		void (*module_unload)(void);
+
+		module_unload = os_dlsym(mod->module,
+				"module_unload");
+		if (module_unload)
+			module_unload();
+
+		os_dlclose(mod->module);
+	}
+
+	bfree(mod->name);
+}

+ 32 - 0
libobs/obs-module.h

@@ -0,0 +1,32 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef OBS_MODULE_H
+#define OBS_MODULE_H
+
+#include "util/darray.h"
+
+struct obs_module {
+	char *name;
+	void *module;
+};
+
+extern void *load_module_subfunc(void *module, const char *module_name,
+		const char *name, const char *func, bool required);
+extern void free_module(struct obs_module *mod);
+
+#endif

+ 121 - 0
libobs/obs-output.c

@@ -0,0 +1,121 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "obs.h"
+#include "obs-data.h"
+
+bool get_output_info(void *module, const char *module_name,
+		const char *output_name, struct output_info *info)
+{
+	info->create = load_module_subfunc(module, module_name,
+			output_name, "create", true);
+	info->destroy = load_module_subfunc(module, module_name,
+			output_name, "destroy", true);
+	info->start = load_module_subfunc(module, module_name,
+			output_name, "start", true);
+	info->stop = load_module_subfunc(module, module_name,
+			output_name, "stop", true);
+
+	if (!info->create || !info->destroy || !info->start || !info->stop)
+		return false;
+
+	info->config = load_module_subfunc(module, module_name,
+			output_name, "config", false);
+	info->pause = load_module_subfunc(module, module_name,
+			output_name, "pause", false);
+
+	info->name = output_name;
+	return true;
+}
+
+static inline const struct output_info *find_output(obs_t obs, const char *type)
+{
+	size_t i;
+	for (i = 0; i < obs->output_types.num; i++)
+		if (strcmp(obs->output_types.array[i].name, type) == 0)
+			return obs->output_types.array+i;
+
+	return NULL;
+}
+
+output_t output_create(obs_t obs, const char *type, const char *settings)
+{
+	const struct output_info *info = find_output(obs, type);
+	struct obs_output *output;
+
+	if (!info) {
+		blog(LOG_WARNING, "Output type '%s' not found", type);
+		return NULL;
+	}
+
+	output = bmalloc(sizeof(struct obs_output));
+	output->data = info->create(settings, output);
+	if (!output->data) {
+		bfree(output);
+		return NULL;
+	}
+
+	dstr_init_copy(&output->settings, settings);
+	memcpy(&output->callbacks, info, sizeof(struct output_info));
+	return output;
+}
+
+void output_destroy(output_t output)
+{
+	if (output) {
+		output->callbacks.destroy(output->data);
+		dstr_free(&output->settings);
+		bfree(output);
+	}
+}
+
+void output_start(output_t output)
+{
+	output->callbacks.start(output->data);
+}
+
+void output_stop(output_t output)
+{
+	output->callbacks.stop(output->data);
+}
+
+bool output_canconfig(output_t output)
+{
+	return output->callbacks.config != NULL;
+}
+
+void output_config(output_t output, void *parent)
+{
+	if (output->callbacks.config)
+		output->callbacks.config(output->data, parent);
+}
+
+bool output_canpause(output_t output)
+{
+	return output->callbacks.pause != NULL;
+}
+
+void output_pause(output_t output)
+{
+	if (output->callbacks.pause)
+		output->callbacks.pause(output->data);
+}
+
+void output_save_settings(output_t output, const char *settings)
+{
+	dstr_copy(&output->settings, settings);
+}

+ 119 - 0
libobs/obs-output.h

@@ -0,0 +1,119 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef OBS_OUTPUT_H
+#define OBS_OUTPUT_H
+
+#include "util/c99defs.h"
+#include "util/dstr.h"
+
+/*
+ * ===========================================
+ *  Outputs 
+ * ===========================================
+ *
+ *   An output takes raw audio and/or video and processes and/or outputs it
+ * to a destination, whether that destination be a file, network, or other.
+ *
+ *   A module with outputs needs to export these functions:
+ *       + enum_outputss
+ *
+ *   Each individual output is then exported by it's name.  For example, an
+ * output named "myoutput" would have the following exports:
+ *       + myoutput_create
+ *       + myoutput_destroy
+ *       + myoutput_start
+ *       + myoutput_stop
+ *
+ *       [and optionally]
+ *       + myoutput_config
+ *       + myoutput_pause
+ *
+ * ===========================================
+ *   Primary Exports
+ * ===========================================
+ *   const char *enum_outputs(size_t idx);
+ *       idx: index of the output.
+ *       Return value: Output identifier name.  NULL when no more available.
+ *
+ * ===========================================
+ *   Output Exports
+ * ===========================================
+ *   void *[name]_create(const char *settings, output_t output);
+ *       Creates an output.
+ *
+ *       settings: Settings of the output.
+ *       output: pointer to main output
+ *       Return value: Internal output pointer, or NULL if failed.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_destroy(void *data);
+ *       Destroys the output.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_update(void *data, const char *settings)
+ *       Updates the output's settings
+ *
+ *       settings: New settings of the output
+ *
+ * ---------------------------------------------------------
+ *   void [name]_start(void *data)
+ *       Starts output
+ *
+ * ---------------------------------------------------------
+ *   void [name]_stop(void *data)
+ *       Stops output
+ *
+ * ===========================================
+ *   Optional Output Exports
+ * ===========================================
+ *   void [name]_config(void *data, void *parent);
+ *       Called to configure the output.
+ *
+ *       parent: Parent window pointer
+ *
+ * ---------------------------------------------------------
+ *   void [name]_pause(void *data)
+ *       Pauses output.  Typically only usable for local recordings.
+ */
+
+struct obs_output;
+
+struct output_info {
+	const char *name;
+
+	void *(*create)(const char *settings, struct obs_output *output);
+	void (*destroy)(void *data);
+
+	void (*start)(void *data);
+	void (*stop)(void *data);
+
+	/* optional */
+	void (*config)(void *data, void *parent);
+	void (*pause)(void *data);
+};
+
+struct obs_output {
+	void *data;
+	struct output_info callbacks;
+	struct dstr settings;
+};
+
+extern bool get_output_info(void *module, const char *module_name,
+		const char *output_name, struct output_info *info);
+
+#endif

+ 222 - 0
libobs/obs-scene.c

@@ -0,0 +1,222 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "graphics/math-defs.h"
+#include "obs-scene.h"
+
+static void *obs_scene_create(const char *settings, struct obs_source *source)
+{
+	struct obs_scene *scene = bmalloc(sizeof(struct obs_scene));
+	scene->source = source;
+	da_init(scene->items);
+
+	return scene;
+}
+
+static void obs_scene_destroy(void *data)
+{
+	struct obs_scene *scene = data;
+	size_t i;
+
+	for (i = 0; i < scene->items.num; i++)
+		bfree(scene->items.array[i]);
+
+	da_free(scene->items);
+	bfree(scene);
+}
+
+static uint32_t obs_scene_get_output_flags(void *data)
+{
+	return SOURCE_VIDEO | SOURCE_AUDIO;
+}
+
+static void obs_scene_video_render(void *data)
+{
+	struct obs_scene *scene = data;
+	size_t i;
+
+	for (i = scene->items.num; i > 0; i--) {
+		struct obs_scene_item *item = scene->items.array[i-1];
+
+		gs_matrix_push();
+		gs_matrix_translate3f(item->origin.x, item->origin.y, 0.0f);
+		gs_matrix_scale3f(item->scale.x, item->scale.y, 1.0f);
+		gs_matrix_rotaa4f(0.0f, 0.0f, 1.0f, RAD(-item->rot));
+		gs_matrix_translate3f(-item->pos.x, -item->pos.y, 0.0f);
+
+		source_video_render(item->source);
+
+		gs_matrix_pop();
+	}
+}
+
+static int obs_scene_getsize(void *data)
+{
+	return -1;
+}
+
+static bool obs_scene_enum_children(void *data, size_t idx, source_t *child)
+{
+	struct obs_scene *scene = data;
+	if (idx >= scene->items.num)
+		return false;
+
+	*child = scene->items.array[idx]->source;
+	return true;
+}
+
+/* thanks for being completely worthless, microsoft. */
+#if 1
+static const struct source_info scene_info =
+{
+	"scene",
+	obs_scene_create,
+	obs_scene_destroy,
+	obs_scene_get_output_flags, NULL, NULL, NULL, NULL,
+	obs_scene_video_render,
+	obs_scene_getsize,
+	obs_scene_getsize, NULL, NULL,
+	obs_scene_enum_children, NULL, NULL
+};
+#else
+static const struct source_info scene_info =
+{
+	.name             = "scene",
+	.create           = obs_scene_create,
+	.destroy          = obs_scene_destroy,
+	.get_output_flags = obs_scene_get_output_flags,
+	.video_render     = obs_scene_video_render,
+	.getwidth         = obs_scene_getsize,
+	.getheight        = obs_scene_getsize,
+	.enum_children    = obs_scene_enum_children
+};
+#endif
+
+scene_t scene_create(obs_t obs)
+{
+	struct obs_source *source = bmalloc(sizeof(struct obs_source));
+	struct obs_scene  *scene  = obs_scene_create(NULL, source);
+
+	source->data = scene;
+	if (!source->data) {
+		bfree(source);
+		return NULL;
+	}
+
+	scene->source = source;
+	source_init(obs, source);
+	memcpy(&source->callbacks, &scene_info, sizeof(struct source_info));
+	return scene;
+}
+
+void scene_destroy(scene_t scene)
+{
+	if (scene)
+		source_destroy(scene->source);
+}
+
+source_t scene_source(scene_t scene)
+{
+	return scene->source;
+}
+
+sceneitem_t scene_add(scene_t scene, source_t source)
+{
+	struct obs_scene_item *item = bmalloc(sizeof(struct obs_scene_item));
+	memset(item, 0, sizeof(struct obs_scene_item));
+	item->source  = source;
+	item->visible = true;
+	item->parent  = scene;
+	vec2_set(&item->scale, 1.0f, 1.0f);
+
+	da_push_back(scene->items, &item);
+	return item;
+}
+
+void sceneitem_remove(sceneitem_t item)
+{
+	if (item) {
+		da_erase_item(item->parent->items, item);
+		bfree(item);
+	}
+}
+
+void sceneitem_setpos(sceneitem_t item, const struct vec2 *pos)
+{
+	vec2_copy(&item->pos, pos);
+}
+
+void sceneitem_setrot(sceneitem_t item, float rot)
+{
+	item->rot = rot;
+}
+
+void sceneitem_setorigin(sceneitem_t item, const struct vec2 *origin)
+{
+	vec2_copy(&item->origin, origin);
+}
+
+void sceneitem_setscale(sceneitem_t item, const struct vec2 *scale)
+{
+	vec2_copy(&item->scale, scale);
+}
+
+void sceneitem_setorder(sceneitem_t item, enum order_movement movement)
+{
+	struct obs_scene *scene = item->parent;
+
+	if (movement == ORDER_MOVE_UP) {
+		size_t idx = da_find(scene->items, &item, 0);
+		if (idx > 0)
+			da_move_item(scene->items, idx, idx-1);
+
+	} else if (movement == ORDER_MOVE_DOWN) {
+		size_t idx = da_find(scene->items, &item, 0);
+		if (idx < (scene->items.num-1))
+			da_move_item(scene->items, idx, idx+1);
+
+	} else if (movement == ORDER_MOVE_TOP) {
+		size_t idx = da_find(scene->items, &item, 0);
+		if (idx > 0)
+			da_move_item(scene->items, idx, 0);
+
+	} else if (movement == ORDER_MOVE_TOP) {
+		size_t idx = da_find(scene->items, &item, 0);
+		if (idx < (scene->items.num-1))
+			da_move_item(scene->items, idx, scene->items.num-1);
+	}
+}
+
+void sceneitem_getpos(sceneitem_t item, struct vec2 *pos)
+{
+	vec2_copy(pos, &item->pos);
+}
+
+float sceneitem_getrot(sceneitem_t item)
+{
+	return item->rot;
+}
+
+void sceneitem_getorigin(sceneitem_t item, struct vec2 *origin)
+{
+	vec2_copy(origin, &item->origin);
+}
+
+void sceneitem_getscale(sceneitem_t item, struct vec2 *scale)
+{
+	vec2_copy(scale, &item->scale);
+}

+ 42 - 0
libobs/obs-scene.h

@@ -0,0 +1,42 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef OBS_SCENE_H
+#define OBS_SCENE_H
+
+#include "obs.h"
+#include "obs-source.h"
+
+/* how obs scene! */
+
+struct obs_scene_item {
+	scene_t     parent;
+	source_t    source;
+	bool        visible;
+
+	struct vec2 origin;
+	struct vec2 pos;
+	struct vec2 scale;
+	float       rot;
+};
+
+struct obs_scene {
+	source_t source;
+	DARRAY(struct obs_scene_item*) items;
+};
+
+#endif

+ 33 - 0
libobs/obs-service.h

@@ -0,0 +1,33 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef OBS_SERVICE_H
+#define OBS_SERVICE_H
+
+struct service_data;
+
+struct service_info {
+	void *(*create)(const char *settings, struct service_data *service);
+	void (*destroy)(void *data);
+	void (*config)(void *data, const char *settings);
+	
+	/* get stream url/key */
+	/* get (viewers/etc) */
+	/* send (current game/title/activate commercial/etc) */
+};
+
+#endif

+ 313 - 0
libobs/obs-source.c

@@ -0,0 +1,313 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "obs.h"
+#include "obs-data.h"
+
+bool get_source_info(void *module, const char *module_name,
+		const char *source_name, struct source_info *info)
+{
+	info->create = load_module_subfunc(module, module_name,
+			source_name,"create", true);
+	info->destroy = load_module_subfunc(module, module_name,
+			source_name, "destroy", true);
+	info->get_output_flags = load_module_subfunc(module, module_name,
+			source_name, "get_output_flags", true);
+
+	if (!info->create || !info->destroy || !info->get_output_flags)
+		return false;
+
+	info->config = load_module_subfunc(module, module_name,
+			source_name, "config", false);
+	info->activate = load_module_subfunc(module, module_name,
+			source_name, "activate", false);
+	info->deactivate = load_module_subfunc(module, module_name,
+			source_name, "deactivate", false);
+	info->video_tick = load_module_subfunc(module, module_name,
+			source_name, "video_tick", false);
+	info->video_render = load_module_subfunc(module, module_name,
+			source_name, "video_render", false);
+	info->getwidth = load_module_subfunc(module, module_name,
+			source_name, "getwidth", false);
+	info->getheight = load_module_subfunc(module, module_name,
+			source_name, "getheight", false);
+
+	info->getparam = load_module_subfunc(module, module_name,
+			source_name, "getparam", false);
+	info->setparam = load_module_subfunc(module, module_name,
+			source_name, "setparam", false);
+	info->enum_children = load_module_subfunc(module, module_name,
+			source_name, "enum_children", false);
+
+	info->filter_video = load_module_subfunc(module, module_name,
+			source_name, "filter_video", false);
+	info->filter_audio = load_module_subfunc(module, module_name,
+			source_name, "filter_audio", false);
+
+	info->name = source_name;
+	return true;
+}
+
+static inline const struct source_info *find_source(obs_t obs,
+		struct darray *list, const char *name)
+{
+	size_t i;
+	struct source_info *array = list->array;
+
+	for (i = 0; i < list->num; i++) {
+		struct source_info *info = array+i;
+		if (strcmp(info->name, name) == 0)
+			return info;
+	}
+
+	return NULL;
+}
+
+void source_init(obs_t obs, struct obs_source *source)
+{
+	source->obs              = obs;
+	source->filter_target    = NULL;
+	source->rendering_filter = false;
+
+	dstr_init(&source->settings);
+	da_init(source->filters);
+	da_push_back(obs->sources, &source);
+}
+
+source_t source_create(obs_t obs, enum source_type type, const char *name,
+		const char *settings)
+{
+	const struct source_info *info = NULL;
+	struct darray *list = NULL;
+	struct obs_source *source;
+
+	switch (type) {
+	case SOURCE_INPUT:      list = &obs->input_types.da; break;
+	case SOURCE_FILTER:     list = &obs->filter_types.da; break;
+	case SOURCE_TRANSITION: list = &obs->transition_types.da; break;
+	default:
+		return NULL;
+	}
+
+	info = find_source(obs, list, name);
+	if (!info) {
+		blog(LOG_WARNING, "Source '%s' not found", type);
+		return NULL;
+	}
+
+	source = bmalloc(sizeof(struct obs_source));
+	source->data = info->create(settings, source);
+	if (!source->data) {
+		bfree(source);
+		return NULL;
+	}
+
+	source_init(obs, source);
+	dstr_copy(&source->settings, settings);
+	memcpy(&source->callbacks, info, sizeof(struct source_info));
+	return source;
+}
+
+void source_destroy(source_t source)
+{
+	if (source) {
+		da_free(source->filters);
+		da_erase_item(source->obs->sources, &source);
+
+		source->callbacks.destroy(source->data);
+		dstr_free(&source->settings);
+		bfree(source);
+	}
+}
+
+uint32_t source_get_output_flags(source_t source)
+{
+	return source->callbacks.get_output_flags(source->data);
+}
+
+bool source_hasconfig(source_t source)
+{
+	return source->callbacks.config != NULL;
+}
+
+void source_config(source_t source, void *parent)
+{
+	if (source->callbacks.config)
+		source->callbacks.config(source->data, parent);
+}
+
+void source_activate(source_t source)
+{
+	if (source->callbacks.activate)
+		source->callbacks.activate(source->data);
+}
+
+void source_deactivate(source_t source)
+{
+	if (source->callbacks.deactivate)
+		source->callbacks.deactivate(source->data);
+}
+
+void source_video_tick(source_t source, float seconds)
+{
+	if (source->callbacks.video_tick)
+		source->callbacks.video_tick(source->data, seconds);
+}
+
+void source_video_render(source_t source)
+{
+	if (source->callbacks.video_render) {
+		if (source->filters.num && !source->rendering_filter) {
+			source->rendering_filter = true;
+			source_video_render(source->filters.array[0]);
+			source->rendering_filter = false;
+		} else {
+			source->callbacks.video_render(source->data);
+		}
+	}
+}
+
+int source_getwidth(source_t source)
+{
+	if (source->callbacks.getwidth)
+		return source->callbacks.getwidth(source->data);
+	return 0;
+}
+
+int source_getheight(source_t source)
+{
+	if (source->callbacks.getheight)
+		return source->callbacks.getheight(source->data);
+	return 0;
+}
+
+size_t source_getparam(source_t source, const char *param, void *buf,
+		size_t buf_size)
+{
+	if (source->callbacks.getparam)
+		return source->callbacks.getparam(source->data, param, buf,
+				buf_size);
+	return 0;
+}
+
+void source_setparam(source_t source, const char *param, const void *data,
+		size_t size)
+{
+	if (source->callbacks.setparam)
+		source->callbacks.setparam(source->data, param, data, size);
+}
+
+bool source_enum_children(source_t source, size_t idx, source_t *child)
+{
+	if (source->callbacks.enum_children)
+		return source->callbacks.enum_children(source, idx, child);
+	return false;
+}
+
+source_t filter_gettarget(source_t filter)
+{
+	return filter->filter_target;
+}
+
+void source_filter_add(source_t source, source_t filter)
+{
+	if (da_find(source->filters, &filter, 0) != -1) {
+		blog(LOG_WARNING, "Tried to add a filter that was already "
+		                  "present on the source");
+		return;
+	}
+
+	if (source->filters.num) {
+		source_t *back = da_end(source->filters);
+		(*back)->filter_target = filter;
+	}
+
+	da_push_back(source->filters, &filter);
+	filter->filter_target = source;
+}
+
+void source_filter_remove(source_t source, source_t filter)
+{
+	size_t idx = da_find(source->filters, &filter, 0);
+	if (idx == -1)
+		return;
+
+	if (idx > 0) {
+		source_t prev = source->filters.array[idx-1];
+		prev->filter_target = filter->filter_target;
+	}
+
+	da_erase(source->filters, idx);
+	filter->filter_target = NULL;
+}
+
+void source_filter_setorder(source_t source, source_t filter,
+		enum order_movement movement)
+{
+	size_t idx = da_find(source->filters, &filter, 0);
+	size_t i;
+	if (idx == -1)
+		return;
+
+	if (movement == ORDER_MOVE_UP) {
+		if (idx == source->filters.num-1)
+			return;
+		da_move_item(source->filters, idx, idx+1);
+
+	} else if (movement == ORDER_MOVE_DOWN) {
+		if (idx == 0)
+			return;
+		da_move_item(source->filters, idx, idx-1);
+
+	} else if (movement == ORDER_MOVE_TOP) {
+		if (idx == source->filters.num-1)
+			return;
+		da_move_item(source->filters, idx, source->filters.num-1);
+
+	} else if (movement == ORDER_MOVE_BOTTOM) {
+		if (idx == 0)
+			return;
+		da_move_item(source->filters, idx, 0);
+	}
+
+	/* reorder filter targets */
+	for (i = 0; i < source->filters.num; i++) {
+		source_t next_filter = (i == source->filters.num-1) ?
+			source : source->filters.array[idx+1];
+		source->filters.array[i]->filter_target = next_filter;
+	}
+}
+
+const char *source_get_settings(source_t source)
+{
+	return source->settings.array;
+}
+
+void source_save_settings(source_t source, const char *settings)
+{
+	dstr_copy(&source->settings, settings);
+}
+
+void source_output_video(source_t source, struct video_frame *frame)
+{
+	/* TODO */
+}
+
+void source_output_audio(source_t source, struct audio_data *audio)
+{
+	/* TODO */
+}

+ 214 - 0
libobs/obs-source.h

@@ -0,0 +1,214 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef SOURCE_H
+#define SOURCE_H 
+
+#include "util/c99defs.h"
+#include "util/darray.h"
+#include "util/dstr.h"
+#include "media-io/media-io.h"
+
+/*
+ * ===========================================
+ *   Sources
+ * ===========================================
+ *
+ *   A source is literally a "source" of audio and/or video.
+ *
+ *   A module with sources needs to export these functions:
+ *       + enum_[type]
+ *
+ *   Each individual source is then exported by it's name.  For example, a
+ * source named "mysource" would have the following exports:
+ *       + mysource_create
+ *       + mysource_destroy
+ *       + mysource_getflags
+ *
+ *       [and optionally]
+ *       + mysource_update
+ *       + mysource_config
+ *       + mysource_video_tick
+ *       + mysource_video_render
+ *       + mysource_getparam
+ *       + mysource_setparam
+ *
+ * ===========================================
+ *   Primary Exports
+ * ===========================================
+ *   const bool enum_[type](size_t idx, const char **name);
+ *       idx: index of the source.
+ *       type: pointer to variable that receives the type of the source
+ *       Return value: false when no more available.
+ *
+ * ===========================================
+ *   Source Exports
+ * ===========================================
+ *   void *[name]_create(const char *settings, source_t source);
+ *       Creates a source.
+ *
+ *       settings: Settings of the source.
+ *       source: pointer to main source
+ *       Return value: Internal source pointer, or NULL if failed.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_destroy(void *data);
+ *       Destroys the source.
+ *
+ * ---------------------------------------------------------
+ *   uint32_t [name]_getflags(void *data);
+ *       Returns a combination of one of the following values:
+ *           + SOURCE_VIDEO: source has video
+ *           + SOURCE_AUDIO: source has audio
+ *           + SOURCE_ASYNC: video is sent asynchronously via RAM
+ *
+ * ---------------------------------------------------------
+ *   int [name]_getwidth(void *data);
+ *       Returns the width of a source, or -1 for maximum width.  If you render
+ *       video, this is required.
+ *
+ * ---------------------------------------------------------
+ *   int [name]_getheight(void *data);
+ *       Returns the height of a source, or -1 for maximum height.  If you
+ *       render video, this is required.
+ *
+ * ===========================================
+ *   Optional Source Exports
+ * ===========================================
+ *   void [name]_update(void *data, const char *settings)
+ *       Updates the source's settings
+ *
+ *       settings: New settings of the source
+ *
+ * ---------------------------------------------------------
+ *   void [name]_config(void *data, void *parent);
+ *       Called to configure the source.
+ *
+ *       parent: Parent window pointer
+ *
+ * ---------------------------------------------------------
+ *   void [name]_video_activate(void *data);
+ *       Called when the source is being displayed.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_video_deactivate(void *data);
+ *       Called when the source is no longer being displayed.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_video_tick(void *data, float seconds);
+ *       Called each video frame with the time taken between the last and
+ *       current frame, in seconds.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_video_render(void *data);
+ *       Called to render the source.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_getparam(void *data, const char *param, void *buf,
+ *                        size_t buf_size);
+ *       Gets a source parameter value.  
+ *
+ *       param: Name of parameter.
+ *       Return value: Value of parameter.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_setparam(void *data, const char *param, const void *buf,
+ *                        size_t size);
+ *       Sets a source parameter value.
+ *
+ *       param: Name of parameter.
+ *       val: Value of parameter to set.
+ *
+ * ---------------------------------------------------------
+ *   bool [name]_enum_children(void *data, size_t idx, source_t *child);
+ *       Enumerates child sources, if any.
+ *
+ *       idx: Child source index.
+ *       child: Pointer to variable that receives child source. 
+ *       Return value: true if sources remaining, otherwise false.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_filter_video(void *data, struct video_frame *frame);
+ *       Filters audio data.  Used with audio filters.
+ *
+ *       frame: Video frame data.
+ *
+ * ---------------------------------------------------------
+ *   void [name]_filter_audio(void *data, struct audio_data *audio);
+ *       Filters video data.  Used with async video data.
+ *
+ *       audio: Audio data.
+ */
+
+struct obs_source;
+
+struct source_info {
+	const char *name;
+
+	/* ----------------------------------------------------------------- */
+	/* required implementations */
+
+	void *(*create)(const char *settings, struct obs_source *source);
+	void (*destroy)(void *data);
+
+	uint32_t (*get_output_flags)(void *data);
+
+	/* ----------------------------------------------------------------- */
+	/* optional implementations */
+
+	bool (*config)(void *data, void *parent);
+
+	void (*activate)(void *data);
+	void (*deactivate)(void *data);
+
+	void (*video_tick)(void *data, float seconds);
+	void (*video_render)(void *data);
+	int (*getwidth)(void *data);
+	int (*getheight)(void *data);
+
+	size_t (*getparam)(void *data, const char *param, void *data_out,
+			size_t buf_size);
+	void (*setparam)(void *data, const char *param, const void *data_in,
+			size_t size);
+
+	bool (*enum_children)(void *data, size_t idx, source_t *child);
+
+	void (*filter_video)(void *data, struct video_frame *frame);
+	void (*filter_audio)(void *data, struct audio_data *audio);
+};
+
+struct obs_source {
+	struct obs_data    *obs;
+	void               *data;
+	struct source_info callbacks;
+	struct dstr        settings;
+	bool               rendering_filter;
+
+	struct obs_source *filter_target;
+	DARRAY(struct obs_source*) filters;
+};
+
+extern bool get_source_info(void *module, const char *module_name,
+		const char *source_name, struct source_info *info);
+
+extern void source_init(obs_t obs, struct obs_source *source);
+
+EXPORT void source_activate(source_t source);
+EXPORT void source_deactivate(source_t source);
+EXPORT void source_video_tick(source_t source, float seconds);
+
+#endif

+ 126 - 0
libobs/obs-video.c

@@ -0,0 +1,126 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "obs.h"
+#include "obs-data.h"
+#include "graphics/vec4.h"
+
+static void tick_sources(obs_t obs, uint64_t cur_time, uint64_t *last_time)
+{
+	size_t i;
+	uint64_t delta_time;
+	float seconds;
+
+	if (!last_time)
+		*last_time = cur_time - video_getframetime(obs->video);
+	delta_time = cur_time - *last_time;
+	seconds = (float)((double)delta_time / 1000000000.0);
+
+	for (i = 0; i < obs->sources.num; i++)
+		source_video_tick(obs->sources.array[i], seconds);
+
+	*last_time = cur_time;
+}
+
+static inline void render_displays(obs_t obs)
+{
+	size_t i;
+	struct vec4 clear_color;
+	vec4_set(&clear_color, 0.3f, 0.0f, 0.0f, 1.0f);
+
+	//gs_enable_depthtest(false);
+
+	gs_ortho(0.0f, (float)obs->output_width,
+	         0.0f, (float)obs->output_height,
+	         -100.0f, 100.0f);
+
+	for (i = 0; i < obs->displays.num; i++) {
+		display_t display = obs->displays.array[i];
+
+		gs_load_swapchain(display->swap);
+
+		gs_beginscene();
+		gs_setviewport(0, 0, gs_getwidth(), gs_getheight());
+
+		if (display->source)
+			source_video_render(display->source);
+
+		gs_endscene();
+		gs_present();
+	}
+
+	gs_load_swapchain(NULL);
+
+	gs_beginscene();
+	gs_clear(GS_CLEAR_COLOR|GS_CLEAR_DEPTH|GS_CLEAR_STENCIL,
+			&clear_color, 1.0f, 0);
+
+	gs_setviewport(0, 0, gs_getwidth(), gs_getheight());
+
+	if (obs->primary_source)
+		source_video_render(obs->primary_source);
+
+	gs_endscene();
+	gs_present();
+}
+
+static bool swap_frame(obs_t obs, uint64_t timestamp)
+{
+	stagesurf_t last_surface = obs->copy_surfaces[obs->cur_texture];
+	stagesurf_t surface;
+	struct video_frame frame;
+
+	if (obs->copy_mapped) {
+		stagesurface_unmap(last_surface);
+		obs->copy_mapped = false;
+	}
+
+	obs->textures_copied[obs->cur_texture] = true;
+	gs_stage_texture(last_surface, NULL);
+
+	if (++obs->cur_texture == NUM_TEXTURES)
+		obs->cur_texture = 0;
+
+	if (obs->textures_copied[obs->cur_texture]) {
+		surface = obs->copy_surfaces[obs->cur_texture];
+		obs->copy_mapped = stagesurface_map(surface, &frame.data,
+				&frame.row_size);
+
+		if (obs->copy_mapped) {
+			frame.timestamp = timestamp;
+			video_output_frame(obs->video, &frame);
+		}
+	}
+
+	return obs->copy_mapped;
+}
+
+void *obs_video_thread(void *param)
+{
+	struct obs_data *obs = param;
+	uint64_t last_time = 0;
+
+	while (video_output_wait(obs->video)) {
+		uint64_t cur_time = video_gettime(obs->video);
+
+		tick_sources(obs, cur_time, &last_time);
+		render_displays(obs);
+		swap_frame(obs, cur_time);
+	}
+
+	return NULL;
+}

+ 242 - 0
libobs/obs.c

@@ -0,0 +1,242 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#include "obs.h"
+#include "obs-data.h"
+#include "obs-module.h"
+
+static bool obs_init_graphics(struct obs_data *obs, const char *graphics_module,
+		struct gs_init_data *graphics_data, struct video_info *vi)
+{
+	int errorcode;
+	size_t i;
+
+	errorcode = gs_create(&obs->graphics, graphics_module, graphics_data);
+	if (errorcode != GS_SUCCESS) {
+		if (errorcode == GS_ERROR_MODULENOTFOUND)
+			blog(LOG_ERROR, "Could not find graphics module '%s'",
+					graphics_module);
+		return false;
+	}
+
+	gs_setcontext(obs->graphics);
+
+	for (i = 0; i < NUM_TEXTURES; i++) {
+		obs->copy_surfaces[i] = gs_create_stagesurface(vi->width,
+				vi->height, graphics_data->format);
+		if (!obs->copy_surfaces[i])
+			return false;
+	}
+
+	return true;
+}
+
+static bool obs_init_media(struct obs_data *obs,
+		struct video_info *vi, struct audio_info *ai)
+{
+	obs->media = media_open();
+	if (!obs->media)
+		return false;
+
+	if (!obs_reset_video(obs, vi))
+		return false;
+	if (!obs_reset_audio(obs, ai))
+		return false;
+
+	return true;
+}
+
+static bool obs_init_threading(struct obs_data *obs)
+{
+	if (pthread_mutex_init(&obs->source_mutex, NULL) != 0)
+		return false;
+	if (pthread_create(&obs->video_thread, NULL, obs_video_thread,
+				obs) != 0)
+		return false;
+
+	obs->thread_initialized = true;
+	return true;
+}
+
+static pthread_mutex_t pthread_init_val = PTHREAD_MUTEX_INITIALIZER;
+
+obs_t obs_create(const char *graphics_module,
+		struct gs_init_data *graphics_data,
+		struct video_info *vi, struct audio_info *ai)
+{
+	struct obs_data *obs = bmalloc(sizeof(struct obs_data));
+
+	memset(obs, 0, sizeof(struct obs_data));
+	obs->source_mutex = pthread_init_val;
+
+	if (!obs_init_graphics(obs, graphics_module, graphics_data, vi))
+		goto error;
+	if (!obs_init_media(obs, vi, ai))
+		goto error;
+	if (!obs_init_threading(obs))
+		goto error;
+
+	return obs;
+
+error:
+	obs_destroy(obs);
+	return NULL;
+}
+
+static inline void obs_free_graphics(obs_t obs)
+{
+	size_t i;
+	if (!obs->graphics)
+		return;
+
+	if (obs->copy_mapped)
+		stagesurface_unmap(obs->copy_surfaces[obs->cur_texture]);
+
+	for (i = 0; i < NUM_TEXTURES; i++)
+		stagesurface_destroy(obs->copy_surfaces[i]);
+
+	gs_setcontext(NULL);
+	gs_destroy(obs->graphics);
+}
+
+static inline void obs_free_media(obs_t obs)
+{
+	video_output_close(obs->video);
+	audio_output_close(obs->audio);
+	media_close(obs->media);
+}
+
+static inline void obs_free_threading(obs_t obs)
+{
+	void *thread_ret;
+	video_output_stop(obs->video);
+	if (obs->thread_initialized)
+		pthread_join(obs->video_thread, &thread_ret);
+	pthread_mutex_destroy(&obs->source_mutex);
+}
+
+void obs_destroy(obs_t obs)
+{
+	size_t i;
+
+	if (!obs)
+		return;
+
+	for (i = 0; i < obs->displays.num; i++)
+		display_destroy(obs->displays.array[i]);
+
+	da_free(obs->input_types);
+	da_free(obs->filter_types);
+	da_free(obs->transition_types);
+	da_free(obs->output_types);
+	/*da_free(obs->services);*/
+
+	da_free(obs->displays);
+	da_free(obs->sources);
+
+	obs_free_threading(obs);
+	obs_free_media(obs);
+	obs_free_graphics(obs);
+
+	for (i = 0; i < obs->modules.num; i++)
+		free_module(obs->modules.array+i);
+	da_free(obs->modules);
+
+	bfree(obs);
+}
+
+bool obs_reset_video(obs_t obs, struct video_info *vi)
+{
+	int errorcode;
+
+	if (obs->video) {
+		video_output_close(obs->video);
+		obs->video = NULL;
+	}
+
+	obs->output_width  = vi->width;
+	obs->output_height = vi->height;
+
+	errorcode = video_output_open(&obs->video, obs->media, vi);
+	if (errorcode == VIDEO_OUTPUT_SUCCESS)
+		return true;
+	else if (errorcode == VIDEO_OUTPUT_INVALIDPARAM)
+		blog(LOG_ERROR, "Invalid video parameters specified");
+	else
+		blog(LOG_ERROR, "Could not open video output");
+
+	return false;
+}
+
+bool obs_reset_audio(obs_t obs, struct audio_info *ai)
+{
+	return true;
+}
+
+
+bool obs_enum_inputs(obs_t obs, size_t idx, const char **name)
+{
+	if (idx >= obs->input_types.num)
+		return false;
+	*name = obs->input_types.array[idx].name;
+	return true;
+}
+
+bool obs_enum_filters(obs_t obs, size_t idx, const char **name)
+{
+	if (idx >= obs->filter_types.num)
+		return false;
+	*name = obs->filter_types.array[idx].name;
+	return true;
+}
+
+bool obs_enum_transitions(obs_t obs, size_t idx, const char **name)
+{
+	if (idx >= obs->transition_types.num)
+		return false;
+	*name = obs->transition_types.array[idx].name;
+	return true;
+}
+
+bool obs_enum_outputs(obs_t obs, size_t idx, const char **name)
+{
+	if (idx >= obs->output_types.num)
+		return false;
+	*name = obs->output_types.array[idx].name;
+	return true;
+}
+
+
+graphics_t obs_graphics(obs_t obs)
+{
+	return obs->graphics;
+}
+
+media_t obs_media(obs_t obs)
+{
+	return obs->media;
+}
+
+source_t obs_get_primary_source(obs_t obs)
+{
+	return obs->primary_source;
+}
+
+void obs_set_primary_source(obs_t obs, source_t source)
+{
+	obs->primary_source = source;
+}

+ 304 - 0
libobs/obs.h

@@ -0,0 +1,304 @@
+/******************************************************************************
+    Copyright (C) 2013 by Hugh Bailey <[email protected]>
+
+    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 3 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, see <http://www.gnu.org/licenses/>.
+******************************************************************************/
+
+#ifndef LIBOBS_H
+#define LIBOBS_H
+
+#include "util/c99defs.h"
+#include "graphics/graphics.h"
+#include "graphics/vec2.h"
+#include "media-io/media-io.h"
+
+#include "obs-defs.h"
+
+/*
+ * Main libobs header used by applications.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum source_type {
+	SOURCE_INPUT,
+	SOURCE_FILTER,
+	SOURCE_TRANSITION,
+	SOURCE_SCENE
+};
+
+enum order_movement {
+	ORDER_MOVE_UP,
+	ORDER_MOVE_DOWN,
+	ORDER_MOVE_TOP,
+	ORDER_MOVE_BOTTOM
+};
+
+/* opaque types */
+struct obs_data;
+struct obs_display;
+struct obs_source;
+struct obs_scene;
+struct obs_scene_item;
+struct obs_output;
+
+typedef struct obs_data       *obs_t;
+typedef struct obs_display    *display_t;
+typedef struct obs_source     *source_t;
+typedef struct obs_scene      *scene_t;
+typedef struct obs_scene_item *sceneitem_t;
+typedef struct obs_output     *output_t;
+
+/* ------------------------------------------------------------------------- */
+/* OBS context */
+
+/**
+ * Creates the OBS context.
+ *
+ *   Using the graphics module specified, creates an OBS context and sets the
+ * primary video/audio output information.
+ */
+EXPORT obs_t      obs_create(const char *graphics_module,
+                             struct gs_init_data *graphics_data,
+                             struct video_info *vi, struct audio_info *ai);
+EXPORT void       obs_destroy(obs_t obs);
+
+/**
+ * Sets base video ouput base resolution/fps/format
+ *
+ *   NOTE: Cannot reset base video if currently streaming/recording.
+ */
+EXPORT bool       obs_reset_video(obs_t obs, struct video_info *vi);
+/**
+ * Sets base audio output format/channels/samples/etc
+ *
+ *   NOTE: Cannot reset base audio if currently streaming/recording.
+ */
+EXPORT bool       obs_reset_audio(obs_t obs, struct audio_info *ai);
+
+/**
+ * Loads a plugin module
+ *
+ *   A plugin module contains exports for inputs/fitlers/transitions/outputs.
+ * See obs-source.h and obs-output.h for more information on the exports to
+ * use.
+ */
+EXPORT int        obs_load_module(obs_t obs, const char *path);
+
+/**
+ * Enumerates all available inputs.
+ *
+ *   Inputs are general source inputs (such as capture sources, device sources,
+ * etc).
+ */
+EXPORT bool       obs_enum_inputs(obs_t obs, size_t idx, const char **name);
+/**
+ * Enumerates all available filters.
+ *
+ *   Filters are sources that are used to modify the video/audio output of
+ * other sources.
+ */
+EXPORT bool       obs_enum_filters(obs_t obs, size_t idx, const char **name);
+/**
+ * Enumerates all available transitions.
+ *
+ *   Transitions are sources used to transition between two or more other
+ * sources.
+ */
+EXPORT bool       obs_enum_transitions(obs_t obs, size_t idx,
+                                       const char **name);
+/**
+ * Enumerates all available ouputs.
+ *
+ *   Outputs handle color space conversion, encoding, and output to file or
+ * streams.
+ */
+EXPORT bool       obs_enum_outputs(obs_t obs, size_t idx, const char **name);
+
+/** Gets the graphics context for this OBS context */
+EXPORT graphics_t obs_graphics(obs_t obs);
+/** Get the media context for this OBS context (used for outputs) */
+EXPORT media_t    obs_media(obs_t obs);
+
+/**
+ * Sets/gets the primary output source.
+ *
+ *   The primary source is the source that's broadcasted/saved.
+ */
+EXPORT void       obs_set_primary_source(obs_t obs, source_t source);
+EXPORT source_t   obs_get_primary_source(obs_t obs);
+
+
+/* ------------------------------------------------------------------------- */
+/* Display context */
+
+/**
+ * Creates an extra display context.
+ *
+ *   An extra display can be used for things like separate previews,
+ * viewing sources independently, and other things.  Creates a new swap chain
+ * linked to a specific window to display a source.
+ */
+EXPORT display_t  display_create(obs_t obs, struct gs_init_data *graphics_data);
+EXPORT void       display_destroy(display_t display);
+/** Sets the source to be used for a display context. */
+EXPORT void       display_setsource(display_t display, source_t source);
+EXPORT source_t   display_getsource(display_t display);
+
+
+/* ------------------------------------------------------------------------- */
+/* Sources */
+
+/**
+ * Creates a source of the specified type with the specified settings.
+ *
+ *   The "source" context is used for anything related to presenting
+ * or modifying video/audio.
+ */
+EXPORT source_t   source_create(obs_t obs, enum source_type type,
+                                const char *name, const char *settings);
+EXPORT void       source_destroy(source_t source);
+/**
+ * Retrieves flags that specify what type of data the source presents/modifies.
+ *
+ *   SOURCE_VIDEO if it presents/modifies video_frame
+ *   SOURCE_ASYNC if the video is asynchronous.
+ *   SOURCE_AUDIO if it presents/modifies audio (always async)
+ */
+EXPORT uint32_t   source_get_output_flags(source_t source);
+/** Specifies whether the source can be configured */
+EXPORT bool       source_hasconfig(source_t source);
+/** Opens a configuration panel and attaches it to the parent window */
+EXPORT void       source_config(source_t source, void *parent);
+/** Renders a video source. */
+EXPORT void       source_video_render(source_t source);
+/** Gets the width of a source (if it has video) */
+EXPORT int        source_getwidth(source_t source);
+/** Gets the height of a source (if it has video) */
+EXPORT int        source_getheight(source_t source);
+/**
+ * Gets a custom parameter from the source.
+ *
+ *   Used for efficiently modifying source properties in real time.
+ */
+EXPORT size_t     source_getparam(source_t source, const char *param, void *buf,
+                                  size_t buf_size);
+/**
+ * Sets a custom parameter for the source.
+ *
+ *   Used for efficiently modifying source properties in real time.
+ */
+EXPORT void       source_setparam(source_t source, const char *param,
+                                  const void *data, size_t size);
+/**
+ * Enumerates child sources this source has
+ */
+EXPORT bool       source_enum_children(source_t source, size_t idx,
+                                       source_t *child);
+
+/** If the source is a filter, returns the target source of the filter */
+EXPORT source_t   filter_gettarget(source_t filter);
+/** Adds a filter to the source (which is used whenever the source is used) */
+EXPORT void       source_filter_add(source_t source, source_t filter);
+/** Removes a filter from the source */
+EXPORT void       source_filter_remove(source_t source, source_t filter);
+/** Modifies the order of a specific filter */
+EXPORT void       source_filter_setorder(source_t source, source_t filter,
+                                         enum order_movement movement);
+
+/** Gets the settings string for a source */
+EXPORT const char *source_get_settings(source_t source);
+
+/* ------------------------------------------------------------------------- */
+/* Functions used by sources */
+
+/** Saves the settings string for a source */
+EXPORT void source_save_settings(source_t source, const char *settings);
+/** Outputs asynchronous video data */
+EXPORT void source_output_video(source_t source, struct video_frame *frame);
+/** Outputs audio data (always asynchronous) */
+EXPORT void source_output_audio(source_t source, struct audio_data *audio);
+
+
+/* ------------------------------------------------------------------------- */
+/* Scenes */
+
+/**
+ * Creates a scene.
+ *
+ *   A scene is a source which is a container of other sources with specific
+ * display oriantations.  Scenes can also be used like any other source.
+ */
+EXPORT scene_t     scene_create(obs_t obs);
+EXPORT void        scene_destroy(scene_t scene);
+/** Gets the scene's source context */
+EXPORT source_t    scene_source(scene_t scene);
+
+/** Adds/creates a new scene item for a source */
+EXPORT sceneitem_t scene_add(scene_t scene, source_t source);
+
+/** Removes/destroys a scene item */
+EXPORT void  sceneitem_destroy(sceneitem_t item);
+
+/* Functions for gettings/setting specific oriantation of a scene item */
+EXPORT void  sceneitem_setpos(sceneitem_t item, const struct vec2 *pos);
+EXPORT void  sceneitem_setrot(sceneitem_t item, float rot);
+EXPORT void  sceneitem_setorigin(sceneitem_t item, const struct vec2 *origin);
+EXPORT void  sceneitem_setscale(sceneitem_t item, const struct vec2 *scale);
+EXPORT void  sceneitem_setorder(sceneitem_t item, enum order_movement movement);
+
+EXPORT void  sceneitem_getpos(sceneitem_t item, struct vec2 *pos);
+EXPORT float sceneitem_getrot(sceneitem_t item);
+EXPORT void  sceneitem_getorigin(sceneitem_t item, struct vec2 *center);
+EXPORT void  sceneitem_getscale(sceneitem_t item, struct vec2 *scale);
+
+
+/* ------------------------------------------------------------------------- */
+/* Outputs */
+
+/**
+ * Creates an output.
+ *
+ *   Outputs allow outputting to file, outputting to network, outputting to
+ * directshow, or other custom outputs.
+ */
+EXPORT output_t   output_create(obs_t obs, const char *name,
+                                const char *settings);
+EXPORT void       output_destroy(output_t output);
+/** Starts the output. */
+EXPORT void       output_start(output_t output);
+/** Stops the output. */
+EXPORT void       output_stop(output_t output);
+/** Specifies whether the output can be configured */
+EXPORT bool       output_canconfig(output_t output);
+/** Opens a configuration panel with the specified parent window */
+EXPORT void       output_config(output_t output, void *parent);
+/** Specifies whether the output can be paused */
+EXPORT bool       output_canpause(output_t output);
+/** Pauses the output (if the functionality is allowed by the output */
+EXPORT void       output_pause(output_t output);
+
+/* Gets the current output settings string */
+EXPORT const char *output_get_settings(output_t output);
+
+/* Saves the output settings string, typically used only by outputs */
+EXPORT void       output_save_settings(output_t output, const char *settings);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 104 - 0
libobs/util/base.c

@@ -0,0 +1,104 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "c99defs.h"
+#include "base.h"
+
+static enum log_type log_output_level = LOG_WARNING;
+static int  crashing = 0;
+
+static void def_log_handler(enum log_type type, const char *format,
+		va_list args)
+{
+	char out[4096];
+	vsnprintf(out, sizeof(out), format, args);
+
+	if (type >= log_output_level) {
+		switch (type) {
+		case LOG_DEBUG:
+			printf("debug: %s\n", out);
+			break;
+
+		case LOG_INFO:
+			printf("info: %s\n", out);
+			break;
+
+		case LOG_WARNING:
+			fprintf(stderr, "warning: %s\n", out);
+			break;
+
+		case LOG_ERROR:
+			fprintf(stderr, "error: %s\n", out);
+		}
+	}
+}
+
+static void def_crash_handler(const char *format, va_list args)
+{
+	vfprintf(stderr, format, args);
+	exit(0);
+}
+
+static void (*log_handler)(enum log_type, const char *, va_list) =
+		def_log_handler;
+static void (*crash_handler)(const char *, va_list) = def_crash_handler;
+
+void base_set_log_handler(
+	void (*handler)(enum log_type, const char *, va_list))
+{
+	log_handler = handler;
+}
+
+void base_set_crash_handler(void (*handler)(const char *, va_list))
+{
+	crash_handler = handler;
+}
+
+void bcrash(const char *format, ...)
+{
+	va_list args;
+
+	if (crashing) {
+		fputs("Crashed in the crash handler", stderr);
+		exit(2);
+	}
+
+	crashing = 1;
+	va_start(args, format);
+	crash_handler(format, args);
+	va_end(args);
+}
+
+void blog(enum log_type type, const char *format, ...)
+{
+	va_list args;
+
+	va_start(args, format);
+	log_handler(type, format, args);
+	va_end(args);
+}

+ 58 - 0
libobs/util/base.h

@@ -0,0 +1,58 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef BASE_H
+#define BASE_H
+
+#include <wctype.h>
+#include <stdarg.h>
+
+#include "c99defs.h"
+
+/*
+ * Just contains logging/crash related stuff
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum log_type {
+	LOG_DEBUG,
+	LOG_INFO,
+	LOG_WARNING,
+	LOG_ERROR
+};
+
+EXPORT void base_set_log_handler(
+		void (*handler)(enum log_type, const char *, va_list));
+EXPORT void base_set_crash_handler(void (*handler)(const char *, va_list));
+
+EXPORT void blog(enum log_type type, const char *format, ...);
+EXPORT void bcrash(const char *format, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 131 - 0
libobs/util/bmem.c

@@ -0,0 +1,131 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <string.h>
+#include "base.h"
+#include "bmem.h"
+
+static void *a_malloc(size_t size, size_t align)
+{
+#if defined(_WIN32)
+	return _aligned_malloc(size, align);
+#elif defined (__posix__)
+	void *ptr;
+	if (posix_memalign(&ptr, align, size) != 0)
+		return NULL;
+
+	return ptr;
+#else
+#error unknown OS
+#endif
+}
+
+static void a_free(void *ptr)
+{
+#if defined(_WIN32)
+	_aligned_free(ptr);
+#elif defined (__posix__)
+	free(ptr);
+#else
+#error unknown OS
+#endif
+}
+
+static struct base_allocator alloc = {malloc, realloc, free, a_malloc, a_free};
+static size_t num_allocs = 0;
+
+void base_set_allocator(struct base_allocator *defs)
+{
+	memcpy(&alloc, defs, sizeof(struct base_allocator));
+}
+
+void *bmalloc(size_t size)
+{
+	void *ptr = alloc.malloc(size);
+	if (!ptr && !size)
+		ptr = alloc.malloc(1);
+	if (!ptr)
+		bcrash("Out of memory while trying to allocate %lu bytes",
+				(unsigned long)size);
+
+	num_allocs++;
+	return ptr;
+}
+
+void *brealloc(void *ptr, size_t size)
+{
+	if (!ptr)
+		num_allocs++;
+
+	ptr = alloc.realloc(ptr, size);
+	if (!ptr && !size)
+		ptr = alloc.realloc(ptr, 1);
+	if (!ptr)
+		bcrash("Out of memory while trying to allocate %lu bytes",
+				(unsigned long)size);
+
+	return ptr;
+}
+
+void bfree(void *ptr)
+{
+	if (ptr)
+		num_allocs--;
+	alloc.free(ptr);
+}
+
+void *baligned_malloc(size_t size, size_t align)
+{
+	void *ptr = alloc.aligned_malloc(size, align);
+	if (!ptr && !size)
+		ptr = alloc.aligned_malloc(1, align);
+	if (!ptr)
+		bcrash("Out of memory while trying to allocate %lu bytes",
+				(unsigned long)size);
+
+	num_allocs++;
+	return ptr;
+}
+
+void baligned_free(void *ptr)
+{
+	if (ptr)
+		num_allocs--;
+	alloc.aligned_free(ptr);
+}
+
+size_t bnum_allocs(void)
+{
+	return num_allocs;
+}
+
+void *bmemdup(const void *ptr, size_t size)
+{
+	void *out = bmalloc(size);
+	if (size)
+		memcpy(out, ptr, size);
+
+	return out;
+}

+ 102 - 0
libobs/util/bmem.h

@@ -0,0 +1,102 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef BALLOC_H
+#define BALLOC_H
+
+#include "c99defs.h"
+#include "base.h"
+#include <wchar.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct base_allocator {
+	void *(*malloc)(size_t);
+	void *(*realloc)(void *, size_t);
+	void (*free)(void *);
+
+	void *(*aligned_malloc)(size_t, size_t);
+	void (*aligned_free)(void *);
+};
+
+EXPORT void base_set_allocator(struct base_allocator *defs);
+
+EXPORT void *bmalloc(size_t size);
+EXPORT void *brealloc(void *ptr, size_t size);
+EXPORT void bfree(void *ptr);
+
+EXPORT void *baligned_malloc(size_t size, size_t align);
+EXPORT void baligned_free(void *ptr);
+
+EXPORT size_t bnum_allocs(void);
+
+EXPORT void *bmemdup(const void *ptr, size_t size);
+
+static inline char *bstrdup_n(const char *str, size_t n)
+{
+	char *dup;
+	if (!str || !*str)
+		return NULL;
+
+	dup = (char*)bmemdup(str, n+1);
+	dup[n] = 0;
+
+	return dup;
+}
+
+static inline wchar_t *bwstrdup_n(const wchar_t *str, size_t n)
+{
+	wchar_t *dup;
+	if (!str || !*str)
+		return NULL;
+
+	dup = (wchar_t*)bmemdup(str, (n+1) * sizeof(wchar_t));
+	dup[n] = 0;
+
+	return dup;
+}
+
+static inline char *bstrdup(const char *str)
+{
+	if (!str)
+		return NULL;
+
+	return bstrdup_n(str, strlen(str));
+}
+
+static inline wchar_t *bwstrdup(const wchar_t *str)
+{
+	if (!str)
+		return NULL;
+
+	return bwstrdup_n(str, wcslen(str));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 75 - 0
libobs/util/c99defs.h

@@ -0,0 +1,75 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef C99DEFS_H
+#define C99DEFS_H
+
+/*
+ * Contains hacks for getting some C99 stuff working in VC, things like
+ * bool, inline, stdint
+ *
+ * TODO: Check for VC2013, because it actually supports this C99 stuff.
+ */
+
+#ifdef _MSC_VER
+#define EXPORT extern __declspec(dllexport)
+#else
+#define EXPORT extern 
+#endif
+
+#ifdef _MSC_VER
+
+#pragma warning (disable : 4996)
+
+#ifndef __cplusplus
+#define inline __inline
+#endif
+
+#include "vc/stdint.h"
+#include "vc/stdbool.h"
+
+#ifndef __off_t_defined
+#define __off_t_defined
+#if _FILE_OFFSET_BITS == 64
+typedef long long off_t;
+#else
+typedef long off_t;
+#endif
+typedef int64_t off64_t;
+#endif /* __off_t_defined */
+
+#ifdef _WIN64
+typedef long long ssize_t;
+#else
+typedef long ssize_t;
+#endif
+
+#else
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#endif /* _MSC_VER */
+
+#endif

+ 1311 - 0
libobs/util/cf-lexer.c

@@ -0,0 +1,1311 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <ctype.h>
+#include <stdio.h>
+#include "platform.h"
+#include "cf-lexer.h"
+
+static inline void cf_convert_from_escape_literal(char **p_dst,
+		const char **p_src)
+{
+	char *dst = *p_dst;
+	const char *src = *p_src;
+
+	switch (*(src++)) {
+		case '\'': *(dst++) = '\''; break;
+		case '\"': *(dst++) = '\"'; break; 
+		case '\?': *(dst++) = '\?'; break;
+		case '\\': *(dst++) = '\\'; break;
+		case '0':  *(dst++) = '\0'; break;
+		case 'a':  *(dst++) = '\a'; break;
+		case 'b':  *(dst++) = '\b'; break;
+		case 'f':  *(dst++) = '\f'; break;
+		case 'n':  *(dst++) = '\n'; break;
+		case 'r':  *(dst++) = '\r'; break;
+		case 't':  *(dst++) = '\t'; break;
+		case 'v':  *(dst++) = '\v'; break;
+
+		/* hex */
+		case 'X':
+		case 'x':
+			  *(dst++) = (char)strtoul(src, NULL, 16);
+			  src += 2;
+			  break;
+
+		/* oct */
+		default:
+			  if (isdigit(*src)) {
+				  *(dst++) = (char)strtoul(src, NULL, 8);
+				  src += 3;
+			  }
+
+		/* case 'u':
+		case 'U': */
+	}
+
+	*p_dst = dst;
+	*p_src = src;
+}
+
+char *cf_literal_to_str(const char *literal, size_t count)
+{
+	const char *temp_src;
+	char *str, *temp_dst;
+
+	if (!count)
+		count = strlen(literal);
+
+	if (count < 2)
+		return NULL;
+	if (literal[0] != literal[count-1])
+		return NULL;
+	if (literal[0] != '\"' && literal[0] != '\'')
+		return NULL;
+
+	str = bmalloc(count - 1);
+	temp_src = literal;
+	temp_dst = str;
+
+	while (*temp_src) {
+		if (*temp_src == '\\') {
+			temp_src++;
+			cf_convert_from_escape_literal(&temp_dst, &temp_src);
+		} else {
+			*(temp_dst++) = *(temp_src++);
+		}
+	}
+
+	*temp_dst = 0;
+	return str;
+}
+
+static bool cf_is_token_break(struct base_token *start_token,
+		const struct base_token *token)
+{
+	switch (start_token->type) {
+	case BASETOKEN_ALPHA:
+		if (token->type == BASETOKEN_OTHER ||
+		    token->type == BASETOKEN_WHITESPACE)
+			return true;
+		break;
+
+	case BASETOKEN_DIGIT:
+		if (token->type == BASETOKEN_WHITESPACE
+		    || (token->type == BASETOKEN_OTHER
+		        && *token->text.array != '.'))
+			return true;
+		break;
+
+	case BASETOKEN_WHITESPACE:
+		/* lump all non-newline whitespace together when possible */
+		if (is_space_or_tab(*start_token->text.array) &&
+		    is_space_or_tab(*token->text.array))
+			break;
+		return true;
+
+	case BASETOKEN_OTHER:
+		if (*start_token->text.array == '.' &&
+		    token->type == BASETOKEN_DIGIT) {
+			start_token->type = BASETOKEN_DIGIT;
+			break;
+		}
+
+	default:
+		return true;
+	}
+
+	return false;
+}
+
+static inline bool cf_is_splice(const char *array)
+{
+	return (*array == '\\' && is_newline(array[1]));
+}
+
+static inline void cf_pass_any_splices(const char **parray)
+{
+	while (cf_is_splice(*parray))
+		*parray += 1 + newline_size((*parray)+1);
+}
+
+static inline bool cf_is_comment(const char *array)
+{
+	const char *offset = array;
+
+	if (*offset++ == '/') {
+		cf_pass_any_splices(&offset);
+		return (*offset == '*' || *offset == '/');
+	}
+
+	return false;
+}
+
+static bool cf_lexer_process_comment(struct cf_lexer *lex,
+		struct cf_token *out_token)
+{
+	const char *offset;
+
+	if (!cf_is_comment(out_token->unmerged_str.array))
+		return false;
+
+	offset = lex->base_lexer.offset;
+	cf_pass_any_splices(&offset);
+
+	strcpy(lex->write_offset++, " ");
+	out_token->str.len = 1;
+
+	if (*offset == '/') {
+		while (*++offset && !is_newline(*offset))
+			cf_pass_any_splices(&offset);
+
+	} else if (*offset == '*') {
+		bool was_star = false;
+		lex->unexpected_eof = true;
+
+		while (*++offset) {
+			cf_pass_any_splices(&offset);
+
+			if (was_star && *offset == '/') {
+				offset++;
+				lex->unexpected_eof = false;
+				break;
+			} else {
+				was_star = (*offset == '*');
+			}
+		}
+	}
+
+	out_token->unmerged_str.len += offset - out_token->unmerged_str.array;
+	out_token->type = CFTOKEN_SPACETAB;
+	lex->base_lexer.offset = offset;
+
+	return true;
+}
+
+static inline void cf_lexer_write_strref(struct cf_lexer *lex,
+		const struct strref *ref)
+{
+	strncpy(lex->write_offset, ref->array, ref->len);
+	lex->write_offset[ref->len] = 0;
+	lex->write_offset += ref->len;
+}
+
+static bool cf_lexer_is_include(struct cf_lexer *lex)
+{
+	bool found_include_import = false;
+	bool found_preprocessor = false;
+	size_t i;
+
+	for (i = lex->tokens.num; i > 0; i--) {
+		struct cf_token *token = lex->tokens.array+(i-1);
+
+		if (is_space_or_tab(*token->str.array))
+			continue;
+
+		if (!found_include_import) {
+			if (strref_cmp(&token->str, "include") != 0 &&
+			    strref_cmp(&token->str, "import")  != 0)
+				break;
+
+			found_include_import = true;
+
+		} else if (!found_preprocessor) {
+			if (*token->str.array != '#')
+				break;
+
+			found_preprocessor = true;
+
+		} else {
+			return is_newline(*token->str.array);
+		}
+	}
+
+	/* if starting line */
+	return found_preprocessor && found_include_import;
+}
+
+static void cf_lexer_getstrtoken(struct cf_lexer *lex,
+		struct cf_token *out_token, char delimiter,
+		bool allow_escaped_delimiters)
+{
+	const char *offset = lex->base_lexer.offset;
+	bool escaped = false;
+
+	out_token->unmerged_str.len++;
+	out_token->str.len++;
+	cf_lexer_write_strref(lex, &out_token->unmerged_str);
+
+	while (*offset) {
+		cf_pass_any_splices(&offset);
+		if (*offset == delimiter) {
+			if (!escaped) {
+				*lex->write_offset++ = *offset;
+				out_token->str.len++;
+				offset++;
+				break;
+			}
+		} else if (is_newline(*offset)) {
+			break;
+		}
+
+		*lex->write_offset++ = *offset;
+		out_token->str.len++;
+
+		escaped = (allow_escaped_delimiters && *offset == '\\');
+		offset++;
+	}
+
+	*lex->write_offset = 0;
+	out_token->unmerged_str.len += offset - out_token->unmerged_str.array;
+	out_token->type = CFTOKEN_STRING;
+	lex->base_lexer.offset = offset;
+}
+
+static bool cf_lexer_process_string(struct cf_lexer *lex,
+		struct cf_token *out_token)
+{
+	char ch = *out_token->unmerged_str.array;
+
+	if (ch == '<' && cf_lexer_is_include(lex)) {
+		cf_lexer_getstrtoken(lex, out_token, '>', false);
+		return true;
+
+	} else if (ch == '"' || ch == '\'') {
+		cf_lexer_getstrtoken(lex, out_token, ch,
+				!cf_lexer_is_include(lex));
+		return true;
+	}
+
+	return false;
+}
+
+static inline enum cf_token_type cf_get_token_type(const struct cf_token *token,
+		const struct base_token *start_token)
+{
+	switch (start_token->type) {
+	case BASETOKEN_ALPHA:
+		return CFTOKEN_NAME;
+
+	case BASETOKEN_DIGIT:
+		return CFTOKEN_NUM;
+
+	case BASETOKEN_WHITESPACE:
+		if (is_newline(*token->str.array))
+			return CFTOKEN_NEWLINE;
+		else
+			return CFTOKEN_SPACETAB;
+
+	default:
+		break;
+	}
+
+	return CFTOKEN_OTHER;
+}
+
+static bool cf_lexer_nexttoken(struct cf_lexer *lex, struct cf_token *out_token)
+{
+	struct base_token token, start_token;
+	bool wrote_data = false;
+
+	base_token_clear(&token);
+	base_token_clear(&start_token);
+	cf_token_clear(out_token);
+
+	while (lexer_getbasetoken(&lex->base_lexer, &token, false)) {
+		/* reclassify underscore as alpha for alnum tokens */
+		if (*token.text.array == '_')
+			token.type = BASETOKEN_ALPHA;
+
+		/* ignore escaped newlines to merge spliced lines */
+		if (cf_is_splice(token.text.array)) {
+			lex->base_lexer.offset +=
+				newline_size(token.text.array+1);
+			continue;
+		}
+
+		if (!wrote_data) {
+			out_token->unmerged_str.array = token.text.array;
+			out_token->str.array          = lex->write_offset;
+
+			/* if comment then output a space */
+			if (cf_lexer_process_comment(lex, out_token))
+				return true;
+
+			/* process string tokens if any */
+			if (cf_lexer_process_string(lex, out_token))
+				return true;
+
+			base_token_copy(&start_token, &token);
+			wrote_data = true;
+
+		} else if (cf_is_token_break(&start_token, &token)) {
+			lex->base_lexer.offset -= token.text.len;
+			break;
+		}
+
+		/* write token to CF lexer to account for splicing/comments */
+		cf_lexer_write_strref(lex, &token.text);
+		out_token->str.len += token.text.len;
+	}
+
+	if (wrote_data) {
+		out_token->unmerged_str.len = lex->base_lexer.offset -
+		                              out_token->unmerged_str.array;
+		out_token->type = cf_get_token_type(out_token, &start_token);
+	}
+
+	return wrote_data;
+}
+
+void cf_lexer_init(struct cf_lexer *lex)
+{
+	lexer_init(&lex->base_lexer);
+	da_init(lex->tokens);
+
+	lex->file           = NULL;
+	lex->reformatted    = NULL;
+	lex->write_offset   = NULL;
+	lex->unexpected_eof = false;
+}
+
+void cf_lexer_free(struct cf_lexer *lex)
+{
+	bfree(lex->file);
+	bfree(lex->reformatted);
+	lexer_free(&lex->base_lexer);
+	da_free(lex->tokens);
+
+	lex->file           = NULL;
+	lex->reformatted    = NULL;
+	lex->write_offset   = NULL;
+	lex->unexpected_eof = false;
+}
+
+bool cf_lexer_lex(struct cf_lexer *lex, const char *str, const char *file)
+{
+	struct cf_token token;
+	struct cf_token *last_token = NULL;
+
+	cf_lexer_free(lex);
+	if (!str || !*str)
+		return false;
+
+	if (file)
+		lex->file = bstrdup(file);
+
+	lexer_start(&lex->base_lexer, str);
+	cf_token_clear(&token);
+
+	lex->reformatted = bmalloc(strlen(str) + 1);
+	lex->reformatted[0] = 0;
+	lex->write_offset = lex->reformatted;
+
+	while (cf_lexer_nexttoken(lex, &token)) {
+		if (last_token &&
+		    is_space_or_tab(*last_token->str.array) &&
+		    is_space_or_tab(*token.str.array)) {
+			cf_token_add(last_token, &token);
+			continue;
+		}
+
+		token.lex = lex;
+		last_token = da_push_back_new(lex->tokens);
+		memcpy(last_token, &token, sizeof(struct cf_token));
+	}
+
+	cf_token_clear(&token);
+
+	token.str.array = lex->write_offset;
+	token.unmerged_str.array = lex->base_lexer.offset;
+	token.lex = lex;
+	da_push_back(lex->tokens, &token);
+
+	return !lex->unexpected_eof;
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct macro_param {
+	struct cf_token name;
+	DARRAY(struct cf_token) tokens;
+};
+
+static inline void macro_param_init(struct macro_param *param)
+{
+	cf_token_clear(&param->name);
+	da_init(param->tokens);
+}
+
+static inline void macro_param_free(struct macro_param *param)
+{
+	cf_token_clear(&param->name);
+	da_free(param->tokens);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct macro_params {
+	DARRAY(struct macro_param) params;
+};
+
+static inline void macro_params_init(struct macro_params *params)
+{
+	da_init(params->params);
+}
+
+static inline void macro_params_free(struct macro_params *params)
+{
+	size_t i;
+	for (i = 0; i < params->params.num; i++)
+		macro_param_free(params->params.array+i);
+	da_free(params->params);
+}
+
+static inline struct macro_param *get_macro_param(
+		const struct macro_params *params,
+		const struct strref *name)
+{
+	size_t i;
+	if (!params)
+		return NULL;
+
+	for (i = 0; i < params->params.num; i++) {
+		struct macro_param *param = params->params.array+i;
+		if (strref_cmp_strref(&param->name.str, name) == 0)
+			return param;
+	}
+
+	return NULL;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static bool cf_preprocessor(struct cf_preprocessor *pp,
+		bool if_block, struct cf_token **p_cur_token);
+static void cf_preprocess_tokens(struct cf_preprocessor *pp,
+		bool if_block, struct cf_token **p_cur_token);
+
+static inline bool go_to_newline(struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	while (cur_token->type != CFTOKEN_NEWLINE &&
+	       cur_token->type != CFTOKEN_NONE)
+		cur_token++;
+
+	*p_cur_token = cur_token;
+
+	return cur_token->type != CFTOKEN_NONE;
+}
+
+static inline bool next_token(struct cf_token **p_cur_token, bool preprocessor)
+{
+	struct cf_token *cur_token = *p_cur_token;
+
+	if (cur_token->type != CFTOKEN_NONE)
+		cur_token++;
+
+	/* if preprocessor, stop at newline */
+	while (cur_token->type == CFTOKEN_SPACETAB && 
+	       (preprocessor || cur_token->type == CFTOKEN_NEWLINE))
+		cur_token++;
+
+	*p_cur_token = cur_token;
+	return cur_token->type != CFTOKEN_NONE;
+}
+
+static inline void cf_gettokenoffset(struct cf_preprocessor *pp,
+		const struct cf_token *token, uint32_t *row, uint32_t *col)
+{
+	lexer_getstroffset(&pp->lex->base_lexer,
+			token->unmerged_str.array, row, col);
+}
+
+static void cf_addew(struct cf_preprocessor *pp, const struct cf_token *token,
+		const char *message, int error_level,
+		const char *val1, const char *val2, const char *val3)
+{
+	uint32_t row, col;
+	cf_gettokenoffset(pp, token, &row, &col);
+
+	if (!val1 && !val2 && !val3) {
+		error_data_add(pp->ed, token->lex->file, row, col,
+				message, error_level);
+	} else {
+		struct dstr formatted;
+		dstr_init(&formatted);
+		dstr_safe_printf(&formatted, message, val1, val2, val3, NULL);
+
+		error_data_add(pp->ed, token->lex->file, row, col,
+				formatted.array, error_level);
+		dstr_free(&formatted);
+	}
+}
+
+static inline void cf_adderror(struct cf_preprocessor *pp,
+		const struct cf_token *token, const char *error,
+		const char *val1, const char *val2, const char *val3)
+{
+	cf_addew(pp, token, error, LEVEL_ERROR, val1, val2, val3);
+}
+
+static inline void cf_addwarning(struct cf_preprocessor *pp,
+		const struct cf_token *token, const char *warning,
+		const char *val1, const char *val2, const char *val3)
+{
+	cf_addew(pp, token, warning, LEVEL_WARNING, val1, val2, val3);
+}
+
+static inline void cf_adderror_expecting(struct cf_preprocessor *pp,
+		const struct cf_token *token, const char *expecting)
+{
+	cf_adderror(pp, token, "Expected $1", expecting,
+			NULL, NULL);
+}
+
+static inline void cf_adderror_expected_newline(struct cf_preprocessor *pp,
+		const struct cf_token *token)
+{
+	cf_adderror(pp, token,
+			"Unexpected token after preprocessor, expected "
+			"newline",
+			NULL, NULL, NULL);
+}
+
+static inline void cf_adderror_unexpected_endif_eof(struct cf_preprocessor *pp,
+		const struct cf_token *token)
+{
+	cf_adderror(pp, token, "Unexpected end of file before #endif",
+			NULL, NULL, NULL);
+}
+
+static inline void cf_adderror_unexpected_eof(struct cf_preprocessor *pp,
+		const struct cf_token *token)
+{
+	cf_adderror(pp, token, "Unexpected end of file",
+			NULL, NULL, NULL);
+}
+
+static void cf_include_file(struct cf_preprocessor *pp,
+		const struct cf_token *file_token)
+{
+	struct cf_lexer new_lex;
+	struct dstr str_file;
+	FILE *file;
+	char *file_data;
+	struct cf_token *tokens;
+	size_t i;
+
+	dstr_init(&str_file);
+	dstr_copy_strref(&str_file, &file_token->str);
+	dstr_mid(&str_file, &str_file, 1, str_file.len-2);
+
+	/* if dependency already exists, run preprocessor on it */
+	for (i = 0; i < pp->dependencies.num; i++) {
+		struct cf_lexer *dep = pp->dependencies.array+i;
+
+		if (strcmp(dep->file, str_file.array) == 0) {
+			tokens = cf_lexer_gettokens(dep);
+			cf_preprocess_tokens(pp, false, &tokens);
+			goto exit;
+		}
+	}
+
+	file = os_fopen(str_file.array, "rb");
+	if (!file) {
+		cf_adderror(pp, file_token, "Could not open file '$1'",
+				file_token->str.array, NULL, NULL);
+		goto exit;
+	}
+
+	os_fread_utf8(file, &file_data);
+	fclose(file);
+
+	cf_lexer_init(&new_lex);
+	cf_lexer_lex(&new_lex, file_data, str_file.array);
+	tokens = cf_lexer_gettokens(&new_lex);
+	cf_preprocess_tokens(pp, false, &tokens);
+	bfree(file_data);
+
+	da_push_back(pp->dependencies, &new_lex);
+
+exit:
+	dstr_free(&str_file);
+}
+
+static inline bool is_sys_include(struct strref *ref)
+{
+	return ref->len >= 2 &&
+	       ref->array[0] == '<' && ref->array[ref->len-1] == '>';
+}
+
+static inline bool is_loc_include(struct strref *ref)
+{
+	return ref->len >= 2 &&
+	       ref->array[0] == '"' && ref->array[ref->len-1] == '"';
+}
+
+static void cf_preprocess_include(struct cf_preprocessor *pp,
+		struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+
+	if (pp->ignore_state) {
+		go_to_newline(p_cur_token);
+		return;
+	}
+
+	next_token(&cur_token, true);
+
+	if (cur_token->type != CFTOKEN_STRING) {
+		cf_adderror_expecting(pp, cur_token, "string");
+		go_to_newline(&cur_token);
+		goto exit;
+	}
+
+	if (is_sys_include(&cur_token->str)) {
+		/* TODO */
+	} else if (is_loc_include(&cur_token->str)) {
+		if (!pp->ignore_state)
+			cf_include_file(pp, cur_token);
+	} else {
+		cf_adderror(pp, cur_token, "Invalid or incomplete string",
+				NULL, NULL, NULL);
+		go_to_newline(&cur_token);
+		goto exit;
+	}
+
+	cur_token++;
+
+exit:
+	*p_cur_token = cur_token;
+}
+
+static bool cf_preprocess_macro_params(struct cf_preprocessor *pp,
+		struct cf_def *def, struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	bool success = false;
+	def->macro = true;
+
+	do {
+		next_token(&cur_token, true);
+		if (cur_token->type != CFTOKEN_NAME) {
+			cf_adderror_expecting(pp, cur_token, "identifier");
+			go_to_newline(&cur_token);
+			goto exit;
+		}
+
+		cf_def_addparam(def, cur_token);
+
+		next_token(&cur_token, true);
+		if (cur_token->type != CFTOKEN_OTHER
+		    || (*cur_token->str.array != ','
+		        && *cur_token->str.array != ')')) {
+
+			cf_adderror_expecting(pp, cur_token, "',' or ')'");
+			go_to_newline(&cur_token);
+			goto exit;
+		}
+	} while (*cur_token->str.array != ')');
+
+	/* ended properly, now go to first define token (or newline) */
+	next_token(&cur_token, true);
+	success = true;
+
+exit:
+	*p_cur_token = cur_token;
+	return success;
+}
+
+static inline size_t cf_preprocess_get_def_idx(struct cf_preprocessor *pp,
+		const struct strref *def_name)
+{
+	struct cf_def *array = pp->defines.array;
+	size_t i;
+
+	for (i = 0; i < pp->defines.num; i++) {
+		struct cf_def *cur_def = array+i;
+
+		if (strref_cmp_strref(&cur_def->name.str, def_name) == 0)
+			return i;
+	}
+
+	return -1;
+}
+
+static inline struct cf_def *cf_preprocess_get_def(struct cf_preprocessor *pp,
+		const struct strref *def_name)
+{
+	size_t idx = cf_preprocess_get_def_idx(pp, def_name);
+	if (idx == -1)
+		return NULL;
+
+	return pp->defines.array+idx;
+}
+
+static char space_filler[2] = " ";
+
+static inline void append_space(struct cf_preprocessor *pp,
+		struct darray *tokens, const struct cf_token *base)
+{
+	struct cf_token token;
+	
+	strref_set(&token.str, space_filler, 1);
+	token.type = CFTOKEN_SPACETAB;
+	if (base) {
+		token.lex = base->lex;
+		strref_copy(&token.unmerged_str, &base->unmerged_str);
+	} else {
+		token.lex = pp->lex;
+		strref_copy(&token.unmerged_str, &token.str);
+	}
+
+	darray_push_back(sizeof(struct cf_token), tokens, &token);
+}
+
+static inline void append_end_token(struct darray *tokens)
+{
+	struct cf_token end;
+	cf_token_clear(&end);
+	darray_push_back(sizeof(struct cf_token), tokens, &end);
+}
+
+static void cf_preprocess_define(struct cf_preprocessor *pp,
+		struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	struct cf_def def;
+
+	if (pp->ignore_state) {
+		go_to_newline(p_cur_token);
+		return;
+	}
+
+	cf_def_init(&def);
+
+	next_token(&cur_token, true);
+	if (cur_token->type != CFTOKEN_NAME) {
+		cf_adderror_expecting(pp, cur_token, "identifier");
+		go_to_newline(&cur_token);
+		goto exit;
+	}
+
+	append_space(pp, &def.tokens.da, NULL);
+	cf_token_copy(&def.name, cur_token);
+
+	if (!next_token(&cur_token, true))
+		goto complete;
+
+	/* process macro */
+	if (*cur_token->str.array == '(') {
+		if (!cf_preprocess_macro_params(pp, &def, &cur_token))
+			goto error;
+	}
+
+	while (cur_token->type != CFTOKEN_NEWLINE &&
+	       cur_token->type != CFTOKEN_NONE)
+		cf_def_addtoken(&def, cur_token++);
+
+complete:
+	append_end_token(&def.tokens.da);
+	append_space(pp, &def.tokens.da, NULL);
+	da_push_back(pp->defines, &def);
+	goto exit;
+
+error:
+	cf_def_free(&def);
+
+exit:
+	*p_cur_token = cur_token;
+}
+
+static inline void cf_preprocess_remove_def_strref(struct cf_preprocessor *pp,
+		const struct strref *ref)
+{
+	size_t def_idx = cf_preprocess_get_def_idx(pp, ref);
+	if (def_idx != -1) {
+		struct cf_def *array = pp->defines.array;
+		cf_def_free(array+def_idx);
+		da_erase(pp->defines, def_idx);
+	}
+}
+
+static void cf_preprocess_undef(struct cf_preprocessor *pp,
+		struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+
+	if (pp->ignore_state) {
+		go_to_newline(p_cur_token);
+		return;
+	}
+
+	next_token(&cur_token, true);
+	if (cur_token->type != CFTOKEN_NAME) {
+		cf_adderror_expecting(pp, cur_token, "identifier");
+		go_to_newline(&cur_token);
+		goto exit;
+	}
+
+	cf_preprocess_remove_def_strref(pp, &cur_token->str);
+	cur_token++;
+
+exit:
+	*p_cur_token = cur_token;
+}
+
+/* Processes an #ifdef/#ifndef/#if/#else/#elif sub block recursively */
+static inline bool cf_preprocess_subblock(struct cf_preprocessor *pp,
+		bool ignore, struct cf_token **p_cur_token)
+{
+	bool eof;
+
+	if (!next_token(p_cur_token, true))
+		return false;
+
+	if (!pp->ignore_state) {
+		pp->ignore_state = ignore;
+		 cf_preprocess_tokens(pp, true, p_cur_token);
+		pp->ignore_state = false;
+	} else {
+		cf_preprocess_tokens(pp, true, p_cur_token);
+	}
+
+	eof = ((*p_cur_token)->type == CFTOKEN_NONE);
+	if (eof)
+		cf_adderror_unexpected_endif_eof(pp, *p_cur_token);
+	return !eof;
+}
+
+static void cf_preprocess_ifdef(struct cf_preprocessor *pp,
+		bool ifnot, struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	struct cf_def *def;
+	bool is_true;
+
+	next_token(&cur_token, true);
+	if (cur_token->type != CFTOKEN_NAME) {
+		cf_adderror_expecting(pp, cur_token, "identifier");
+		go_to_newline(&cur_token);
+		goto exit;
+	}
+
+	def = cf_preprocess_get_def(pp, &cur_token->str);
+	is_true = (def == NULL) == ifnot;
+
+	if (!cf_preprocess_subblock(pp, !is_true, &cur_token))
+		goto exit;
+
+	if (strref_cmp(&cur_token->str, "else") == 0) {
+		if (!cf_preprocess_subblock(pp, is_true, &cur_token))
+			goto exit;
+	/*} else if (strref_cmp(&cur_token->str, "elif") == 0) {*/
+	}
+
+	cur_token++;
+
+exit:
+	*p_cur_token = cur_token;
+}
+
+static bool cf_preprocessor(struct cf_preprocessor *pp,
+		bool if_block, struct cf_token **p_cur_token)
+{
+	struct cf_token *cur_token = *p_cur_token;
+
+	if (strref_cmp(&cur_token->str, "include") == 0) {
+		cf_preprocess_include(pp, p_cur_token);
+
+	} else if (strref_cmp(&cur_token->str, "define") == 0) {
+		cf_preprocess_define(pp, p_cur_token);
+
+	} else if (strref_cmp(&cur_token->str, "undef") == 0) {
+		cf_preprocess_undef(pp, p_cur_token);
+
+	} else if (strref_cmp(&cur_token->str, "ifdef") == 0) {
+		cf_preprocess_ifdef(pp, false, p_cur_token);
+
+	} else if (strref_cmp(&cur_token->str, "ifndef") == 0) {
+		cf_preprocess_ifdef(pp, true, p_cur_token);
+
+	/*} else if (strref_cmp(&cur_token->str, "if") == 0) {
+		TODO;*/
+	} else if (strref_cmp(&cur_token->str, "else") == 0 ||
+	           /*strref_cmp(&cur_token->str, "elif") == 0 ||*/
+	           strref_cmp(&cur_token->str, "endif") == 0) {
+		if (!if_block) {
+			struct dstr name;
+			dstr_init_strref(&name, &cur_token->str);
+			cf_adderror(pp, cur_token,"#$1 outside of "
+			                          "#if/#ifdef/#ifndef block",
+			                          name.array, NULL, NULL);
+			dstr_free(&name);
+			(*p_cur_token)++;
+
+			return true;
+		}
+
+		return false;
+
+	} else if (cur_token->type != CFTOKEN_NEWLINE &&
+	           cur_token->type != CFTOKEN_NONE) {
+		/*
+		 * TODO: language-specific preprocessor stuff should be sent to
+		 * handler of some sort
+		 */
+		(*p_cur_token)++;
+	}
+
+	return true;
+}
+
+static void cf_preprocess_addtoken(struct cf_preprocessor *pp,
+		struct darray *dst, /* struct cf_token */
+		struct cf_token **p_cur_token,
+		const struct cf_token *base,
+		const struct macro_params *params);
+
+/*
+ * collects tokens for a macro parameter
+ *
+ * note that it is important to make sure that any usage of function calls
+ * within a macro parameter is preserved, example MACRO(func(1, 2), 3), do not
+ * let it stop on the comma at "1,"
+ */
+static void cf_preprocess_save_macro_param(struct cf_preprocessor *pp,
+		struct cf_token **p_cur_token, struct macro_param *param,
+		const struct cf_token *base,
+		const struct macro_params *cur_params)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	int brace_count = 0;
+
+	append_space(pp, &param->tokens.da, base);
+
+	while (cur_token->type != CFTOKEN_NONE) {
+		if (*cur_token->str.array == '(') {
+			brace_count++;
+		} else if (*cur_token->str.array == ')') {
+			if (brace_count)
+				brace_count--;
+			else
+				break;
+		} else if (*cur_token->str.array == ',') {
+			if (!brace_count)
+				break;
+		}
+
+		cf_preprocess_addtoken(pp, &param->tokens.da, &cur_token, base,
+				cur_params);
+	}
+
+	if (cur_token->type == CFTOKEN_NONE)
+		cf_adderror_unexpected_eof(pp, cur_token);
+
+	append_space(pp, &param->tokens.da, base);
+	append_end_token(&param->tokens.da);
+
+	*p_cur_token = cur_token;
+}
+
+static inline bool param_is_whitespace(const struct macro_param *param)
+{
+	struct cf_token *array = param->tokens.array;
+	size_t i;
+
+	for (i = 0; i < param->tokens.num; i++)
+		if (array[i].type != CFTOKEN_NONE &&
+		    array[i].type != CFTOKEN_SPACETAB &&
+		    array[i].type != CFTOKEN_NEWLINE)
+			return false;
+
+	return true;
+}
+
+/* collects parameter tokens of a used macro and stores them for the unwrap */
+static void cf_preprocess_save_macro_params(struct cf_preprocessor *pp,
+		struct cf_token **p_cur_token, const struct cf_def *def,
+		const struct cf_token *base,
+		const struct macro_params *cur_params,
+		struct macro_params *dst)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	size_t count = 0;
+
+	next_token(&cur_token, false);
+	if (cur_token->type != CFTOKEN_OTHER || *cur_token->str.array != '(') {
+		cf_adderror_expecting(pp, cur_token, "'('");
+		goto exit;
+	}
+
+	do {
+		struct macro_param param;
+		macro_param_init(&param);
+		cur_token++;
+		count++;
+
+		cf_preprocess_save_macro_param(pp, &cur_token, &param, base,
+				cur_params);
+		if (cur_token->type != CFTOKEN_OTHER
+		    || (*cur_token->str.array != ','
+		        && *cur_token->str.array != ')')) {
+
+			macro_param_free(&param);
+			cf_adderror_expecting(pp, cur_token, "',' or ')'");
+			goto exit;
+		}
+
+		if (param_is_whitespace(&param)) {
+			/* if 0-param macro, ignore first entry */
+			if (count == 1 && !def->params.num &&
+			    *cur_token->str.array == ')') {
+				macro_param_free(&param);
+				break;
+			}
+		}
+
+		if (count <= def->params.num) {
+			cf_token_copy(&param.name,
+					cf_def_getparam(def, count-1));
+			da_push_back(dst->params, &param);
+		} else {
+			macro_param_free(&param);
+		}
+	} while (*cur_token->str.array != ')');
+
+	if (count != def->params.num)
+		cf_adderror(pp, cur_token,
+				"Mismatching number of macro parameters",
+				NULL, NULL, NULL);
+
+exit:
+	*p_cur_token = cur_token;
+}
+
+static inline void cf_preprocess_unwrap_param(struct cf_preprocessor *pp,
+		struct darray *dst, /* struct cf_token */
+		struct cf_token **p_cur_token,
+		const struct cf_token *base,
+		const struct macro_param *param)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	struct cf_token *cur_param_token = param->tokens.array;
+
+	while (cur_param_token->type != CFTOKEN_NONE)
+		cf_preprocess_addtoken(pp, dst, &cur_param_token, base, NULL);
+
+	cur_token++;
+	*p_cur_token = cur_token;
+}
+
+static inline void cf_preprocess_unwrap_define(struct cf_preprocessor *pp,
+		struct darray *dst, /* struct cf_token */
+		struct cf_token **p_cur_token,
+		const struct cf_token *base,
+		const struct cf_def *def,
+		const struct macro_params *cur_params)
+{
+	struct cf_token *cur_token = *p_cur_token;
+	struct macro_params new_params;
+	struct cf_token *cur_def_token = def->tokens.array;
+
+	macro_params_init(&new_params);
+
+	if (def->macro)
+		cf_preprocess_save_macro_params(pp, &cur_token, def, base,
+				cur_params, &new_params);
+
+	while (cur_def_token->type != CFTOKEN_NONE)
+		cf_preprocess_addtoken(pp, dst, &cur_def_token, base,
+				&new_params);
+
+	macro_params_free(&new_params);
+
+	cur_token++;
+	*p_cur_token = cur_token;
+}
+
+static void cf_preprocess_addtoken(struct cf_preprocessor *pp,
+		struct darray *dst, /* struct cf_token */
+		struct cf_token **p_cur_token,
+		const struct cf_token *base,
+		const struct macro_params *params)
+{
+	struct cf_token *cur_token = *p_cur_token;
+
+	if (pp->ignore_state)
+		goto ignore;
+
+	if (!base)
+		base = cur_token;
+
+	if (cur_token->type == CFTOKEN_NAME) {
+		struct cf_def *def;
+		struct macro_param *param;
+
+		param = get_macro_param(params, &cur_token->str);
+		if (param) {
+			cf_preprocess_unwrap_param(pp, dst, &cur_token, base,
+					param);
+			goto exit;
+		}
+
+		def = cf_preprocess_get_def(pp, &cur_token->str);
+		if (def) {
+			cf_preprocess_unwrap_define(pp, dst, &cur_token, base,
+					def, params);
+			goto exit;
+		}
+	}
+
+	darray_push_back(sizeof(struct cf_token), dst, cur_token);
+
+ignore:
+	cur_token++;
+
+exit:
+	*p_cur_token = cur_token;
+}
+
+static void cf_preprocess_tokens(struct cf_preprocessor *pp,
+		bool if_block, struct cf_token **p_cur_token)
+{
+	bool newline = true;
+	bool preprocessor_line = if_block;
+	struct cf_token *cur_token = *p_cur_token;
+
+	while (cur_token->type != CFTOKEN_NONE) {
+		if(cur_token->type != CFTOKEN_SPACETAB &&
+		   cur_token->type != CFTOKEN_NEWLINE) {
+			if (preprocessor_line) {
+				cf_adderror_expected_newline(pp, cur_token);
+				if (!go_to_newline(&cur_token))
+					break;
+			}
+
+			if (newline && *cur_token->str.array == '#') {
+				next_token(&cur_token, true);
+				preprocessor_line = true;
+				if (!cf_preprocessor(pp, if_block, &cur_token))
+					break;
+
+				continue;
+			}
+
+			newline = false;
+		}
+
+		if (cur_token->type == CFTOKEN_NEWLINE) {
+			newline = true;
+			preprocessor_line = false;
+		} else if (cur_token->type == CFTOKEN_NONE) {
+			break;
+		}
+
+		cf_preprocess_addtoken(pp, &pp->tokens.da, &cur_token, NULL,
+				NULL);
+	}
+
+	*p_cur_token = cur_token;
+}
+
+void cf_preprocessor_init(struct cf_preprocessor *pp)
+{
+	da_init(pp->defines);
+	da_init(pp->sys_include_dirs);
+	da_init(pp->dependencies);
+	da_init(pp->tokens);
+	pp->lex = NULL;
+	pp->ed = NULL;
+	pp->ignore_state = false;
+}
+
+void cf_preprocessor_free(struct cf_preprocessor *pp)
+{
+	struct cf_lexer *dependencies = pp->dependencies.array;
+	char **sys_include_dirs = pp->sys_include_dirs.array;
+	struct cf_def *defs = pp->defines.array;
+	size_t i;
+
+	for (i = 0; i <pp->defines.num; i++)
+		cf_def_free(defs+i);
+	for (i = 0; i < pp->sys_include_dirs.num; i++)
+		bfree(sys_include_dirs[i]);
+	for (i = 0; i < pp->dependencies.num; i++)
+		cf_lexer_free(dependencies+i);
+
+	da_free(pp->defines);
+	da_free(pp->sys_include_dirs);
+	da_free(pp->dependencies);
+	da_free(pp->tokens);
+
+	pp->lex = NULL;
+	pp->ed = NULL;
+	pp->ignore_state = false;
+}
+
+bool cf_preprocess(struct cf_preprocessor *pp, struct cf_lexer *lex,
+		struct error_data *ed)
+{
+	struct cf_token *token = cf_lexer_gettokens(lex);
+	if (!token)
+		return false;
+
+	pp->ed = ed;
+	pp->lex = lex;
+	cf_preprocess_tokens(pp, false, &token);
+	da_push_back(pp->tokens, token);
+
+	return !lex->unexpected_eof;
+}
+
+void cf_preprocessor_add_def(struct cf_preprocessor *pp, struct cf_def *def)
+{
+	struct cf_def *existing = cf_preprocess_get_def(pp, &def->name.str);
+
+	if (existing) {
+		struct dstr name;
+		dstr_init_strref(&name, &def->name.str);
+		cf_addwarning(pp, &def->name, "Token $1 already defined",
+				name.array, NULL, NULL);
+		cf_addwarning(pp, &existing->name,
+				"Previous definition of $1 is here",
+				name.array, NULL, NULL);
+
+		cf_def_free(existing);
+		memcpy(existing, def, sizeof(struct cf_def));
+	} else {
+		da_push_back(pp->defines, &def);
+	}
+}
+
+void cf_preprocessor_remove_def(struct cf_preprocessor *pp,
+		const char *def_name)
+{
+	struct strref ref;
+	ref.array = def_name;
+	ref.len   = strlen(def_name);
+	cf_preprocess_remove_def_strref(pp, &ref);
+}

+ 216 - 0
libobs/util/cf-lexer.h

@@ -0,0 +1,216 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef CF_LEXER_H
+#define CF_LEXER_H
+
+#include "lexer.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EXPORT char *cf_literal_to_str(const char *literal, size_t count);
+
+/* ------------------------------------------------------------------------- */
+/*
+ * A C-family lexer token is defined as:
+ *   1.) A generic 'name' token.  (abc123_def456)
+ *   2.) A numeric sequence (usually starting with a number)
+ *   3.) A sequence of generic whitespace defined as spaces and tabs
+ *   4.) A newline
+ *   5.) A string or character sequence (surrounded by single or double quotes)
+ *   6.) A single character of a type not specified above
+ */
+enum cf_token_type {
+	CFTOKEN_NONE,
+	CFTOKEN_NAME,
+	CFTOKEN_NUM,
+	CFTOKEN_SPACETAB,
+	CFTOKEN_NEWLINE,
+	CFTOKEN_STRING,
+	CFTOKEN_OTHER
+};
+
+struct cf_token {
+	const struct cf_lexer *lex;
+	struct strref str;
+	struct strref unmerged_str;
+	enum cf_token_type type;
+};
+
+static inline void cf_token_clear(struct cf_token *t)
+{
+	memset(t, 0, sizeof(struct cf_token));
+}
+
+static inline void cf_token_copy(struct cf_token *dst,
+		const struct cf_token *src)
+{
+	memcpy(dst, src, sizeof(struct cf_token));
+}
+
+static inline void cf_token_add(struct cf_token *dst,
+		const struct cf_token *add)
+{
+	strref_add(&dst->str, &add->str);
+	strref_add(&dst->unmerged_str, &add->unmerged_str);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ *   The c-family lexer is a base lexer for generating a list of string
+ * reference tokens to be used with c-style languages.
+ *
+ *   This base lexer is meant to be used as a stepping stone for an actual
+ * language lexer/parser.
+ *
+ *   It reformats the text in the two following ways:
+ *     1.) Spliced lines (escaped newlines) are merged
+ *     2.) All comments are converted to a single space
+ */
+
+struct cf_lexer {
+	char *file;
+	struct lexer base_lexer;
+	char *reformatted, *write_offset;
+	DARRAY(struct cf_token) tokens;
+	bool unexpected_eof; /* unexpected multi-line comment eof */
+};
+
+EXPORT void cf_lexer_init(struct cf_lexer *lex);
+EXPORT void cf_lexer_free(struct cf_lexer *lex);
+
+static inline struct cf_token *cf_lexer_gettokens(struct cf_lexer *lex)
+{
+	return lex->tokens.array;
+}
+
+EXPORT bool cf_lexer_lex(struct cf_lexer *lex, const char *str,
+		const char *file);
+
+/* ------------------------------------------------------------------------- */
+/* c-family preprocessor definition */
+
+struct cf_def {
+	struct cf_token name;
+	DARRAY(struct cf_token) params;
+	DARRAY(struct cf_token) tokens;
+	bool macro;
+};
+
+static inline void cf_def_init(struct cf_def *cfd)
+{
+	cf_token_clear(&cfd->name);
+	da_init(cfd->params);
+	da_init(cfd->tokens);
+	cfd->macro = false;
+}
+
+static inline void cf_def_addparam(struct cf_def *cfd, struct cf_token *param)
+{
+	da_push_back(cfd->params, param);
+}
+
+static inline void cf_def_addtoken(struct cf_def *cfd, struct cf_token *token)
+{
+	da_push_back(cfd->tokens, token);
+}
+
+static inline struct cf_token *cf_def_getparam(const struct cf_def *cfd,
+		size_t idx)
+{
+	return cfd->params.array+idx;
+}
+
+static inline void cf_def_free(struct cf_def *cfd)
+{
+	cf_token_clear(&cfd->name);
+	da_free(cfd->params);
+	da_free(cfd->tokens);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * C-family preprocessor
+ *
+ *   This preprocessor allows for standard c-style preprocessor directives
+ * to be applied to source text, such as:
+ *
+ *   + #include
+ *   + #define/#undef
+ *   + #ifdef/#ifndef/#if/#elif/#else/#endif
+ *
+ *   Still left to implement (TODO):
+ *   + #if/#elif
+ *   + "defined" preprocessor keyword
+ *   + system includes 
+ *   + variadic macros
+ *   + custom callbacks (for things like pragma)
+ *   + option to exclude features such as #import, variadic macros, and other
+ *     features for certain language implementations
+ *   + macro parameter string operator #
+ *   + macro parameter token concactenation operator ##
+ *   + predefined macros
+ *   + restricted macros
+ */
+
+struct cf_preprocessor {
+	struct cf_lexer *lex;
+	struct error_data *ed;
+	DARRAY(struct cf_def)   defines;
+	DARRAY(char*)           sys_include_dirs;
+	DARRAY(struct cf_lexer) dependencies;
+	DARRAY(struct cf_token) tokens;
+	bool ignore_state;
+};
+
+EXPORT void cf_preprocessor_init(struct cf_preprocessor *pp);
+EXPORT void cf_preprocessor_free(struct cf_preprocessor *pp);
+
+EXPORT bool cf_preprocess(struct cf_preprocessor *pp, struct cf_lexer *lex,
+		struct error_data *ed);
+
+static inline void cf_preprocessor_add_sys_include_dir(
+		struct cf_preprocessor *pp, const char *include_dir)
+{
+	if (include_dir)
+		da_push_back(pp->sys_include_dirs, bstrdup(include_dir));
+}
+
+EXPORT void cf_preprocessor_add_def(struct cf_preprocessor *pp,
+		struct cf_def *def);
+EXPORT void cf_preprocessor_remove_def(struct cf_preprocessor *pp,
+		const char *def_name);
+
+static inline struct cf_token *cf_preprocessor_gettokens(
+		struct cf_preprocessor *pp)
+{
+	return pp->tokens.array;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 72 - 0
libobs/util/cf-parser.c

@@ -0,0 +1,72 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include "cf-parser.h"
+
+void cf_adderror(struct cf_parser *p, const char *error, int level,
+		const char *val1, const char *val2, const char *val3)
+{
+	uint32_t row, col;
+	lexer_getstroffset(&p->cur_token->lex->base_lexer,
+			p->cur_token->unmerged_str.array,
+			&row, &col);
+
+	if (!val1 && !val2 && !val3) {
+		error_data_add(&p->error_list, p->cur_token->lex->file,
+				row, col, error, level);
+	} else {
+		struct dstr formatted;
+		dstr_init(&formatted);
+		dstr_safe_printf(&formatted, error, val1, val2, val3, NULL);
+
+		error_data_add(&p->error_list, p->cur_token->lex->file,
+				row, col, formatted.array, level);
+
+		dstr_free(&formatted);
+	}
+}
+
+bool pass_pair(struct cf_parser *p, char in, char out)
+{
+	if (p->cur_token->type != CFTOKEN_OTHER ||
+	    *p->cur_token->str.array != in)
+		return p->cur_token->type != CFTOKEN_NONE;
+
+	p->cur_token++;
+
+	while (p->cur_token->type != CFTOKEN_NONE) {
+		if (*p->cur_token->str.array == in) {
+			if (!pass_pair(p, in, out))
+				break;
+			continue;
+
+		} else if(*p->cur_token->str.array == out) {
+			p->cur_token++;
+			return true;
+		}
+
+		p->cur_token++;
+	}
+
+	return false;
+}

+ 284 - 0
libobs/util/cf-parser.h

@@ -0,0 +1,284 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef CF_PARSER_H
+#define CF_PARSER_H
+
+#include "cf-lexer.h"
+
+/*
+ * C-family parser
+ *
+ *   Handles preprocessing/lexing/errors when parsing a file, and provides a
+ * set of parsing functions to be able to go through all the resulting tokens
+ * more easily.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PARSE_SUCCESS              0
+#define PARSE_CONTINUE            -1
+#define PARSE_BREAK               -2
+#define PARSE_UNEXPECTED_CONTINUE -3
+#define PARSE_UNEXPECTED_BREAK    -4
+#define PARSE_EOF                 -5
+
+struct cf_parser {
+	struct cf_lexer        lex;
+	struct cf_preprocessor pp;
+	struct error_data      error_list;
+
+	struct cf_token        *cur_token;
+};
+
+static inline void cf_parser_init(struct cf_parser *parser)
+{
+	cf_lexer_init(&parser->lex);
+	cf_preprocessor_init(&parser->pp);
+	error_data_init(&parser->error_list);
+
+	parser->cur_token = NULL;
+}
+
+static inline void cf_parser_free(struct cf_parser *parser)
+{
+	cf_lexer_free(&parser->lex);
+	cf_preprocessor_free(&parser->pp);
+	error_data_free(&parser->error_list);
+
+	parser->cur_token = NULL;
+}
+
+static inline bool cf_parser_parse(struct cf_parser *parser,
+		const char *str, const char *file)
+{
+	if (!cf_lexer_lex(&parser->lex, str, file))
+		return false;
+
+	if (!cf_preprocess(&parser->pp, &parser->lex, &parser->error_list))
+		return false;
+
+	parser->cur_token = cf_preprocessor_gettokens(&parser->pp);
+	return true;
+}
+
+EXPORT void cf_adderror(struct cf_parser *parser, const char *error,
+		int level, const char *val1, const char *val2,
+		const char *val3);
+
+static inline void cf_adderror_expecting(struct cf_parser *p,
+		const char *expected)
+{
+	cf_adderror(p, "Expected $1", LEVEL_ERROR, expected, NULL, NULL);
+}
+
+static inline void cf_adderror_unexpected_eof(struct cf_parser *p)
+{
+	cf_adderror(p, "Unexpected end of file", LEVEL_ERROR,
+			NULL, NULL, NULL);
+}
+
+static inline void cf_adderror_syntax_error(struct cf_parser *p)
+{
+	cf_adderror(p, "Syntax error", LEVEL_ERROR,
+			NULL, NULL, NULL);
+}
+
+static inline bool next_token(struct cf_parser *p)
+{
+	if (p->cur_token->type != CFTOKEN_SPACETAB &&
+	    p->cur_token->type != CFTOKEN_NEWLINE &&
+	    p->cur_token->type != CFTOKEN_NONE)
+		p->cur_token++;
+
+	while (p->cur_token->type == CFTOKEN_SPACETAB ||
+	       p->cur_token->type == CFTOKEN_NEWLINE)
+		p->cur_token++;
+
+	return p->cur_token->type != CFTOKEN_NONE;
+}
+
+static inline bool next_valid_token(struct cf_parser *p)
+{
+	if (!next_token(p)) {
+		cf_adderror_unexpected_eof(p);
+		return false;
+	}
+
+	return true;
+}
+
+EXPORT bool pass_pair(struct cf_parser *p, char in, char out);
+
+static inline bool go_to_token(struct cf_parser *p,
+		const char *str1, const char *str2)
+{
+	while (next_token(p)) {
+		if (strref_cmp(&p->cur_token->str, str1) == 0) {
+			return true;
+		} else if (str2 && strref_cmp(&p->cur_token->str, str2) == 0) {
+			return true;
+		} else if (*p->cur_token->str.array == '{') {
+			if (!pass_pair(p, '{', '}'))
+				break;
+		}
+	}
+
+	return false;
+}
+
+static inline bool go_to_valid_token(struct cf_parser *p,
+		const char *str1, const char *str2)
+{
+	if (!go_to_token(p, str1, str2)) {
+		cf_adderror_unexpected_eof(p);
+		return false;
+	}
+
+	return true;
+}
+
+static inline bool go_to_token_type(struct cf_parser *p,
+		enum cf_token_type type)
+{
+	while (p->cur_token->type != CFTOKEN_NONE &&
+	       p->cur_token->type != CFTOKEN_NEWLINE)
+		p->cur_token++;
+
+	return p->cur_token->type != CFTOKEN_NONE;
+}
+
+static inline int next_token_should_be(struct cf_parser *p,
+		const char *str, const char *goto1, const char *goto2)
+{
+	if (!next_token(p)) {
+		cf_adderror_unexpected_eof(p);
+		return PARSE_EOF;
+	} else if (strref_cmp(&p->cur_token->str, str) == 0) {
+		return PARSE_SUCCESS;
+	}
+
+	if (goto1)
+		go_to_token(p, goto1, goto2);
+
+	cf_adderror_expecting(p, str);
+	return PARSE_CONTINUE;
+}
+
+static inline bool peek_token(struct cf_parser *p, struct cf_token *peek)
+{
+	struct cf_token *cur_token = p->cur_token;
+	bool success = next_token(p);
+
+	*peek = *p->cur_token;
+	p->cur_token = cur_token;
+
+	return success;
+}
+
+static inline bool peek_valid_token(struct cf_parser *p,
+		struct cf_token *peek)
+{
+	bool success = peek_token(p, peek);
+	if (!success)
+		cf_adderror_unexpected_eof(p);
+	return success;
+}
+
+static inline bool token_is(struct cf_parser *p, const char *val)
+{
+	return strref_cmp(&p->cur_token->str, val) == 0;
+}
+
+static inline int token_is_type(struct cf_parser *p,
+		enum cf_token_type type, const char *type_expected,
+		const char *goto_token)
+{
+	if (p->cur_token->type != type) {
+		cf_adderror_expecting(p, type_expected);
+		if (goto_token) {
+			if (!go_to_valid_token(p, goto_token, NULL))
+				return PARSE_EOF;
+		}
+		return PARSE_CONTINUE;
+	}
+
+	return PARSE_SUCCESS;
+}
+
+static inline void copy_token(struct cf_parser *p, char **dst)
+{
+	*dst = bstrdup_n(p->cur_token->str.array, p->cur_token->str.len);
+}
+
+static inline int get_name(struct cf_parser *p, char **dst,
+		const char *name, const char *goto_token)
+{
+	int errcode;
+
+	errcode = token_is_type(p, CFTOKEN_NAME, name, goto_token);
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	*dst = bstrdup_n(p->cur_token->str.array, p->cur_token->str.len);
+	return PARSE_SUCCESS;
+}
+
+static inline int next_name(struct cf_parser *p, char **dst,
+		const char *name, const char *goto_token)
+{
+	if (!next_valid_token(p))
+		return PARSE_EOF;
+
+	return get_name(p, dst, name, goto_token);
+}
+
+static inline int get_name_ref(struct cf_parser *p, struct strref *dst,
+		const char *name, const char *goto_token)
+{
+	int errcode;
+
+	errcode = token_is_type(p, CFTOKEN_NAME, name, goto_token);
+	if (errcode != PARSE_SUCCESS)
+		return errcode;
+
+	strref_copy(dst, &p->cur_token->str);
+	return PARSE_SUCCESS;
+}
+
+static inline int next_name_ref(struct cf_parser *p, struct strref *dst,
+		const char *name, const char *goto_token)
+{
+	if (!next_valid_token(p))
+		return PARSE_EOF;
+
+	return get_name_ref(p, dst, name, goto_token);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 528 - 0
libobs/util/config-file.c

@@ -0,0 +1,528 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <stdio.h>
+#include <wchar.h>
+#include "config-file.h"
+#include "platform.h"
+#include "base.h"
+#include "bmem.h"
+#include "darray.h"
+#include "lexer.h"
+#include "dstr.h"
+
+struct config_item {
+	char *name;
+	char *value;
+};
+
+static inline void config_item_free(struct config_item *item)
+{
+	bfree(item->name);
+	bfree(item->value);
+}
+
+struct config_section {
+	char *name;
+	struct darray items; /* struct config_item */
+};
+
+static inline void config_section_free(struct config_section *section)
+{
+	struct config_item *items = section->items.array;
+	size_t i;
+
+	for (i = 0; i < section->items.num; i++)
+		config_item_free(items+i);
+
+	darray_free(&section->items);
+	bfree(section->name);
+}
+
+struct config_data {
+	char *file;
+	struct darray sections; /* struct config_section */
+	struct darray defaults; /* struct config_section */
+};
+
+config_t config_create(const char *file)
+{
+	struct config_data *config;
+	FILE *f;
+
+	f = os_fopen(file, "wb");
+	if (!f)
+		return NULL;
+	fclose(f);
+
+	config = bmalloc(sizeof(struct config_data));
+	memset(config, 0, sizeof(struct config_data));
+
+	return config;
+}
+
+static inline void remove_ref_whitespace(struct strref *ref)
+{
+	if (ref->array) {
+		while (is_whitespace(*ref->array)) {
+			ref->array++;
+			ref->len--;
+		}
+
+		while (ref->len && is_whitespace(ref->array[ref->len-1]))
+			ref->len--;
+	}
+}
+
+static bool config_parse_string(struct lexer *lex, struct strref *ref,
+		char end)
+{
+	bool success = end != 0;
+	struct base_token token;
+	base_token_clear(&token);
+
+	while (lexer_getbasetoken(lex, &token, false)) {
+		if (end) {
+			if (*token.text.array == end) {
+				success = true;
+				break;
+			} else if (is_newline(*token.text.array)) {
+				success = false;
+				break;
+			}
+		} else {
+			if (is_newline(*token.text.array)) {
+				success = true;
+				break;
+			}
+		}
+
+		strref_add(ref, &token.text);
+	}
+
+	remove_ref_whitespace(ref);
+	return success;
+}
+
+static void config_add_item(struct darray *items, struct strref *name,
+		struct strref *value)
+{
+	struct config_item item;
+	item.name  = bstrdup_n(name->array,  name->len);
+	item.value = bstrdup_n(value->array, value->len);
+	darray_push_back(sizeof(struct config_item), items, &item);
+}
+
+static void config_parse_section(struct config_section *section,
+		struct lexer *lex)
+{
+	struct base_token token;
+
+	while (lexer_getbasetoken(lex, &token, false)) {
+		struct strref name, value;
+
+		while (token.type == BASETOKEN_WHITESPACE) {
+			if (!lexer_getbasetoken(lex, &token, false))
+				return;
+		}
+
+		if (token.type == BASETOKEN_OTHER) {
+			if (*token.text.array == '#') {
+				do {
+					if (!lexer_getbasetoken(lex, &token,
+					                        false))
+						return;
+				} while (!is_newline(*token.text.array));
+
+				continue;
+			} else if (*token.text.array == '[') {
+				lex->offset--;
+				return;
+			}
+		}
+
+		strref_copy(&name, &token.text);
+		if (!config_parse_string(lex, &name, '='))
+			continue;
+
+		strref_clear(&value);
+		config_parse_string(lex, &value, 0);
+
+		config_add_item(&section->items, &name, &value);
+	}
+}
+
+static int config_parse(struct darray *sections, const char *file)
+{
+	char *file_data;
+	struct lexer lex;
+	struct base_token token;
+	struct strref section_name;
+	FILE *f;
+
+	f = os_fopen(file, "rb");
+	if (!f)
+		return CONFIG_FILENOTFOUND;
+
+	os_fread_utf8(f, &file_data);
+	fclose(f);
+
+	if (!file_data)
+		return CONFIG_SUCCESS;
+
+	lexer_init(&lex);
+	lexer_start_move(&lex, file_data);
+
+	base_token_clear(&token);
+
+	while (lexer_getbasetoken(&lex, &token, false)) {
+		struct config_section *section;
+
+		while (token.type == BASETOKEN_WHITESPACE) {
+			if (!lexer_getbasetoken(&lex, &token, false))
+				goto complete;
+		}
+
+		if (*token.text.array != '[') {
+			while (!is_newline(*token.text.array)) {
+				if (!lexer_getbasetoken(&lex, &token, false))
+					goto complete;
+			}
+
+			continue;
+		}
+
+		strref_clear(&section_name);
+		config_parse_string(&lex, &section_name, ']');
+		if (!section_name.len)
+			break;
+
+		section = darray_push_back_new(sizeof(struct config_section),
+				sections);
+		section->name = bstrdup_n(section_name.array,
+				section_name.len);
+		config_parse_section(section, &lex);
+	}
+
+complete:
+	lexer_free(&lex);
+	return CONFIG_SUCCESS;
+}
+
+int config_open(config_t *config, const char *file, bool always_open)
+{
+	int errorcode;
+
+	if (!config)
+		return CONFIG_ERROR;
+
+	*config = bmalloc(sizeof(struct config_data));
+	memset(*config, 0, sizeof(struct config_data));
+	errorcode = config_parse(&(*config)->sections, file);
+
+	if (errorcode != CONFIG_SUCCESS) {
+		config_close(*config);
+		*config = NULL;
+	}
+
+	return errorcode;
+}
+
+int config_open_defaults(config_t config, const char *file)
+{
+	if (!config)
+		return CONFIG_ERROR;
+
+	return config_parse(&config->defaults, file);
+}
+
+int config_save(config_t config)
+{
+	FILE *f;
+	struct dstr str;
+	size_t i, j;
+
+	if (!config)
+		return CONFIG_ERROR;
+
+	dstr_init(&str);
+
+	f = os_fopen(config->file, "wb");
+	if (!f)
+		return CONFIG_FILENOTFOUND;
+
+	for (i = 0; i < config->sections.num; i++) {
+		struct config_section *section = darray_item(
+				sizeof(struct config_section),
+				&config->sections, i);
+
+		if (i) dstr_cat(&str, "\n");
+
+		dstr_cat(&str, "{");
+		dstr_cat(&str, section->name);
+		dstr_cat(&str, "]\n");
+
+		for (j = 0; j < section->items.num; j++) {
+			struct config_item *item = darray_item(
+					sizeof(struct config_item),
+					&section->items, i);
+
+			dstr_cat(&str, item->name);
+			dstr_cat(&str, "=");
+			dstr_cat(&str, item->value);
+			dstr_cat(&str, "\n");
+		}
+	}
+
+#ifdef _WIN32
+	fwrite("\xEF\xBB\xBF", 1, 3, f);
+#endif
+	fwrite(str.array, 1, str.len, f);
+	fclose(f);
+
+	dstr_free(&str);
+
+	return CONFIG_SUCCESS;
+}
+
+void config_close(config_t config)
+{
+	struct config_section *defaults, *sections;
+	size_t i;
+
+	if (!config) return;
+
+	defaults = config->defaults.array;
+	sections = config->sections.array;
+
+	for (i = 0; i < config->defaults.num; i++)
+		config_section_free(defaults+i);
+	for (i = 0; i < config->sections.num; i++)
+		config_section_free(sections+i);
+
+	darray_free(&config->defaults);
+	darray_free(&config->sections);
+	bfree(config->file);
+	bfree(config);
+}
+
+static struct config_item *config_find_item(struct darray *sections,
+		const char *section, const char *name)
+{
+	size_t i, j;
+
+	for (i = 0; i < sections->num; i++) {
+		struct config_section *sec = darray_item(
+				sizeof(struct config_section), sections, i);
+
+		if (astrcmpi(sec->name, section) != 0) {
+			for (j = 0; j < sec->items.num; j++) {
+				struct config_item *item = darray_item(
+						sizeof(struct config_item),
+						&sec->items, i);
+
+				if (astrcmpi(item->name, name) == 0)
+					return item;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static void config_set_item(struct darray *sections, const char *section,
+		const char *name, char *value)
+{
+	struct config_section *sec = NULL;
+	struct config_section *array = sections->array;
+	struct config_item *item;
+	size_t i, j;
+
+	for (i = 0; i < sections->num; i++) {
+		struct config_section *cur_sec = array+i;
+		struct config_item *items = cur_sec->items.array;
+
+		if (astrcmpi(cur_sec->name, section) == 0) {
+			for (j = 0; j < cur_sec->items.num; j++) {
+				item = items+i;
+
+				if (astrcmpi(item->name, name) == 0) {
+					bfree(item->value);
+					item->value = value;
+					return;
+				}
+			}
+
+			sec = cur_sec;
+			break;
+		}
+	}
+
+	if (!sec) {
+		sec = darray_push_back_new(sizeof(struct config_section),
+				sections);
+		sec->name = bstrdup(section);
+	}
+
+	item = darray_push_back_new(sizeof(struct config_item), &sec->items);
+	item->name  = bstrdup(name);
+	item->value = value;
+}
+
+void config_set_string(config_t config, const char *section,
+		const char *name, const char *value)
+{
+	if (!value)
+		value = "";
+	config_set_item(&config->sections, section, name, bstrdup(value));
+}
+
+void config_set_int(config_t config, const char *section,
+		const char *name, int64_t value)
+{
+	struct dstr str;
+	dstr_init(&str);
+	dstr_printf(&str, "%dll", value);
+	config_set_item(&config->sections, section, name, str.array);
+}
+
+void config_set_uint(config_t config, const char *section,
+		const char *name, uint64_t value)
+{
+	struct dstr str;
+	dstr_init(&str);
+	dstr_printf(&str, "%ull", value);
+	config_set_item(&config->sections, section, name, str.array);
+}
+
+void config_set_bool(config_t config, const char *section,
+		const char *name, bool value)
+{
+	char *str = bstrdup(value ? "true" : "false");
+	config_set_item(&config->sections, section, name, str);
+}
+
+void config_set_double(config_t config, const char *section,
+		const char *name, double value)
+{
+	struct dstr str;
+	dstr_init(&str);
+	dstr_printf(&str, "%g", value);
+	config_set_item(&config->sections, section, name, str.array);
+}
+
+void config_set_default_string(config_t config, const char *section,
+		const char *name, const char *value)
+{
+	if (!value)
+		value = "";
+	config_set_item(&config->defaults, section, name, bstrdup(value));
+}
+
+void config_set_default_int(config_t config, const char *section,
+		const char *name, int64_t value)
+{
+	struct dstr str;
+	dstr_init(&str);
+	dstr_printf(&str, "%dll", value);
+	config_set_item(&config->defaults, section, name, str.array);
+}
+
+void config_set_default_uint(config_t config, const char *section,
+		const char *name, uint64_t value)
+{
+	struct dstr str;
+	dstr_init(&str);
+	dstr_printf(&str, "%ull", value);
+	config_set_item(&config->defaults, section, name, str.array);
+}
+
+void config_set_default_bool(config_t config, const char *section,
+		const char *name, bool value)
+{
+	char *str = bstrdup(value ? "true" : "false");
+	config_set_item(&config->defaults, section, name, str);
+}
+
+void config_set_default_double(config_t config, const char *section,
+		const char *name, double value)
+{
+	struct dstr str;
+	dstr_init(&str);
+	dstr_printf(&str, "%g", value);
+	config_set_item(&config->defaults, section, name, str.array);
+}
+
+const char *config_get_string(config_t config, const char *section,
+		const char *name)
+{
+	struct config_item *item = config_find_item(&config->sections,
+			section, name);
+	if (!item)
+		item = config_find_item(&config->defaults, section, name);
+	if (!item)
+		return NULL;
+
+	return item->value;
+}
+
+int64_t config_get_int(config_t config, const char *section,
+		const char *name)
+{
+	const char *value = config_get_string(config, section, name);
+	if (value)
+		return strtoll(value, NULL, 10);
+
+	return 0;
+}
+
+uint64_t config_get_uint(config_t config, const char *section,
+		const char *name)
+{
+	const char *value = config_get_string(config, section, name);
+	if (value)
+		return strtoul(value, NULL, 10);
+
+	return 0;
+}
+
+bool config_get_bool(config_t config, const char *section,
+		const char *name)
+{
+	const char *value = config_get_string(config, section, name);
+	if (value)
+		return astrcmpi(value, "true") == 0 ||
+		       strtoul(value, NULL, 10);
+
+	return false;
+}
+
+double config_get_double(config_t config, const char *section,
+		const char *name)
+{
+	const char *value = config_get_string(config, section, name);
+	if (value)
+		return strtod(value, NULL);
+
+	return 0.0;
+}

+ 87 - 0
libobs/util/config-file.h

@@ -0,0 +1,87 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef CONFIG_FILE_H
+#define CONFIG_FILE_H
+
+#include "c99defs.h"
+
+/*
+ * Generic ini-style config file functions
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct config_data;
+typedef struct config_data *config_t;
+
+#define CONFIG_SUCCESS      0
+#define CONFIG_FILENOTFOUND -1
+#define CONFIG_ERROR        -2
+
+EXPORT config_t config_create(const char *file);
+EXPORT int config_open(config_t *config, const char *file, bool always_open);
+EXPORT int config_open_defaults(config_t config, const char *file);
+EXPORT int config_save(config_t config);
+EXPORT void config_close(config_t config);
+
+EXPORT void config_set_string(config_t config, const char *section,
+		const char *name, const char *value);
+EXPORT void config_set_int(config_t config, const char *section,
+		const char *name, int64_t value);
+EXPORT void config_set_uint(config_t config, const char *section,
+		const char *name, uint64_t value);
+EXPORT void config_set_bool(config_t config, const char *section,
+		const char *name, bool value);
+EXPORT void config_set_double(config_t config, const char *section,
+		const char *name, double value);
+
+EXPORT void config_set_default_string(config_t config, const char *section,
+		const char *name, const char *value);
+EXPORT void config_set_default_int(config_t config, const char *section,
+		const char *name, int64_t value);
+EXPORT void config_set_default_uint(config_t config, const char *section,
+		const char *name, uint64_t value);
+EXPORT void config_set_default_bool(config_t config, const char *section,
+		const char *name, bool value);
+EXPORT void config_set_default_double(config_t config, const char *section,
+		const char *name, double value);
+
+EXPORT const char *config_get_string(config_t config, const char *section,
+		const char *name);
+EXPORT int64_t config_get_int(config_t config, const char *section,
+		const char *name);
+EXPORT uint64_t config_get_uint(config_t config, const char *section,
+		const char *name);
+EXPORT bool config_get_bool(config_t config, const char *section,
+		const char *name);
+EXPORT double config_get_double(config_t config, const char *section,
+		const char *name);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 545 - 0
libobs/util/darray.h

@@ -0,0 +1,545 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef DARRAY_H
+#define DARRAY_H
+
+#include "c99defs.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "bmem.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Dynamic array.
+ *
+ * NOTE: Not type-safe when using directly.
+ *       Specifying size per call with inline maximizes compiler optimizations
+ *
+ *       See DARRAY macro at the bottom of thhe file for slightly safer usage.
+ */
+
+struct darray {
+	void *array;
+	size_t num;
+	size_t capacity;
+};
+
+static inline void darray_init(struct darray *dst)
+{
+	dst->array    = NULL;
+	dst->num      = 0;
+	dst->capacity = 0;
+}
+
+static inline void darray_free(struct darray *dst)
+{
+	baligned_free(dst->array);
+	dst->array    = NULL;
+	dst->num      = 0;
+	dst->capacity = 0;
+}
+
+static inline size_t darray_alloc_size(const size_t element_size,
+		const struct darray *da)
+{
+	return element_size*da->num;
+}
+
+static inline void *darray_item(const size_t element_size,
+		const struct darray *da, size_t idx)
+{
+	return (void*)(((uint8_t*)da->array) + element_size*idx);
+}
+
+static inline void *darray_end(const size_t element_size,
+		const struct darray *da)
+{
+	if (!da->num)
+		return NULL;
+
+	return darray_item(element_size, da, da->num-1);
+}
+
+static inline void darray_reserve(const size_t element_size,
+		struct darray *dst, const size_t capacity)
+{
+	void *ptr;
+	if (capacity == 0 || capacity <= dst->num)
+		return;
+
+	ptr = baligned_malloc(element_size*capacity, 16);
+	if (dst->num)
+		memcpy(ptr, dst->array, element_size*dst->num);
+	if (dst->array)
+		baligned_free(dst->array);
+	dst->array = ptr;
+	dst->capacity = capacity;
+}
+
+static inline void darray_ensure_capacity(const size_t element_size,
+		struct darray *dst, const size_t new_size)
+{
+	size_t new_cap;
+	void *ptr;
+	if (new_size <= dst->capacity)
+		return;
+
+	new_cap = (!dst->capacity) ? new_size : dst->capacity*2;
+	if (new_size > new_cap)
+		new_cap = new_size;
+	ptr = baligned_malloc(element_size*new_cap, 16);
+	if (dst->capacity)
+		memcpy(ptr, dst->array, element_size*dst->capacity);
+	if (dst->array)
+		baligned_free(dst->array);
+	dst->array = ptr;
+	dst->capacity = new_cap;
+}
+
+static inline void darray_resize(const size_t element_size,
+		struct darray *dst, const size_t size)
+{
+	int b_clear;
+	size_t old_num;
+
+	if (size == dst->num) {
+		return;
+	} else if (size == 0) {
+		darray_free(dst);
+		return;
+	}
+
+	b_clear = size > dst->num;
+	old_num = dst->num;
+
+	darray_ensure_capacity(element_size, dst, size);
+	dst->num = size;
+
+	if (b_clear)
+		memset(darray_item(element_size, dst, old_num), 0,
+				element_size * (dst->num-old_num));
+}
+
+static inline void darray_copy(const size_t element_size, struct darray *dst,
+		const struct darray *da)
+{
+	assert(element_size == element_size);
+
+	if (da->num == 0) {
+		darray_free(dst);
+	} else {
+		darray_resize(element_size, dst, da->num);
+		memcpy(dst->array, da->array, element_size*da->num);
+	}
+}
+
+static inline void darray_copy_array(const size_t element_size,
+		struct darray *dst, const void *array, const size_t num)
+{
+	darray_resize(element_size, dst, num);
+	memcpy(dst->array, array, element_size*dst->num);
+}
+
+static inline void darray_move(struct darray *dst, struct darray *src)
+{
+	darray_free(dst);
+	memcpy(dst, src, sizeof(struct darray));
+	src->array = NULL;
+	src->num = 0;
+}
+
+static inline size_t darray_find(const size_t element_size,
+		const struct darray *da, const void *item, const size_t idx)
+{
+	size_t i;
+
+	assert(idx <= da->num);
+
+	for (i = idx; i < da->num; i++) {
+		void *compare = darray_item(element_size, da, i);
+		if (memcmp(compare, item, element_size) == 0)
+			return i;
+	}
+
+	return -1;
+}
+
+static inline size_t darray_push_back(const size_t element_size,
+		struct darray *dst, const void *item)
+{
+	darray_ensure_capacity(element_size, dst, ++dst->num);
+	memcpy(darray_end(element_size, dst), item, element_size);
+
+	return dst->num-1;
+}
+
+static inline void *darray_push_back_new(const size_t element_size,
+		struct darray *dst)
+{
+	void *last;
+
+	darray_ensure_capacity(element_size, dst, ++dst->num);
+
+	last = darray_end(element_size, dst);
+	memset(last, 0, element_size);
+	return last;
+}
+
+static inline size_t darray_push_back_array(const size_t element_size,
+		struct darray *dst, const void *array, const size_t num)
+{
+	size_t old_num = dst->num;
+
+	assert(array != NULL);
+	assert(num != 0);
+
+	darray_resize(element_size, dst, dst->num+num);
+	memcpy(darray_item(element_size, dst, old_num), array,
+			element_size*num);
+
+	return old_num;
+}
+
+static inline size_t darray_push_back_darray(const size_t element_size,
+		struct darray *dst, const struct darray *da)
+{
+	return darray_push_back_array(element_size, dst, da->array, da->num);
+}
+
+static inline void darray_insert(const size_t element_size, struct darray *dst,
+		const size_t idx, const void *item)
+{
+	void *new_item;
+	size_t move_count;
+
+	assert(idx <= dst->num);
+
+	if (idx == dst->num) {
+		darray_push_back(element_size, dst, item);
+		return;
+	}
+
+	move_count = dst->num - idx;
+	darray_ensure_capacity(element_size, dst, ++dst->num);
+
+	new_item = darray_item(element_size, dst, idx);
+
+	memmove(darray_item(element_size, dst, idx+1), new_item,
+			move_count*element_size);
+	memcpy(new_item, item, element_size);
+}
+
+static inline void *darray_insert_new(const size_t element_size,
+		struct darray *dst, const size_t idx)
+{
+	void *item;
+	size_t move_count;
+
+	assert(idx <= dst->num);
+	if (idx == dst->num)
+		return darray_push_back_new(element_size, dst);
+
+	item = darray_item(element_size, dst, idx);
+
+	move_count = dst->num - idx;
+	darray_ensure_capacity(element_size, dst, ++dst->num);
+	memmove(darray_item(element_size, dst, idx+1), item,
+			move_count*element_size);
+
+	memset(item, 0, element_size);
+	return item;
+}
+
+static inline void darray_insert_array(const size_t element_size,
+		struct darray *dst, const size_t idx,
+		const void *array, const size_t num)
+{
+	size_t old_num;
+
+	assert(array != NULL);
+	assert(num != 0);
+	assert(idx < dst->num);
+
+	old_num = dst->num;
+	darray_resize(element_size, dst, dst->num+num);
+
+	memmove(darray_item(element_size, dst, idx+num),
+			darray_item(element_size, dst, idx),
+			element_size*(old_num-idx));
+	memcpy(darray_item(element_size, dst, idx), array, element_size*num);
+}
+
+static inline void darray_insert_darray(const size_t element_size,
+		struct darray *dst, const size_t idx, const struct darray *da)
+{
+	darray_insert_array(element_size, dst, idx, da->array, da->num);
+}
+
+static inline void darray_erase(const size_t element_size, struct darray *dst,
+		const size_t idx)
+{
+	assert(idx < dst->num);
+
+	if (idx >= dst->num)
+		return;
+
+	if (!--dst->num) {
+		darray_free(dst);
+		return;
+	}
+
+	memcpy(darray_item(element_size, dst, idx),
+			darray_item(element_size, dst, idx+1),
+			element_size*(dst->num-idx));
+}
+
+static inline void darray_erase_item(const size_t element_size,
+		struct darray *dst, const void *item)
+{
+	size_t idx = darray_find(element_size, dst, item, 0);
+	if (idx != (size_t)-1)
+		darray_erase(element_size, dst, idx);
+}
+
+static inline void darray_erase_range(const size_t element_size,
+		struct darray *dst, const size_t start, const size_t end)
+{
+	size_t count, move_count;
+
+	assert(start <= dst->num);
+	assert(end <= dst->num);
+	assert(end > start);
+
+	count = end-start;
+	if (count == 1) {
+		darray_erase(element_size, dst, start);
+		return;
+	} else if (count == dst->num) {
+		darray_free(dst);
+		return;
+	}
+
+	move_count = dst->num - end;
+	if (move_count)
+		memmove(darray_item(element_size, dst, start),
+				darray_item(element_size, dst, end),
+				move_count);
+
+	dst->num -= count;
+}
+
+static inline void darray_pop_back(const size_t element_size,
+		struct darray *dst)
+{
+	assert(dst->num != 0);
+
+	if (dst->num)
+		darray_erase(element_size, dst, dst->num-1);
+}
+
+static inline void darray_join(const size_t element_size, struct darray *dst,
+		struct darray *da)
+{
+	assert(element_size == element_size);
+
+	darray_push_back_darray(element_size, dst, da);
+	darray_free(da);
+}
+
+static inline void darray_split(const size_t element_size, struct darray *dst1,
+		struct darray *dst2, const struct darray *da, const size_t idx)
+{
+	struct darray temp;
+
+	assert(da->num >= idx);
+	assert(dst1 != dst2);
+
+	darray_init(&temp);
+	darray_copy(element_size, &temp, da);
+
+	darray_free(dst1);
+	darray_free(dst2);
+
+	if (da->num) {
+		if (idx)
+			darray_copy_array(element_size, dst1, temp.array,
+					temp.num);
+		if (idx < temp.num-1)
+			darray_copy_array(element_size, dst2,
+					darray_item(element_size, &temp, idx),
+					temp.num-idx);
+	}
+
+	darray_free(&temp);
+}
+
+static inline void darray_move_item(const size_t element_size,
+		struct darray *dst, const size_t from, const size_t to)
+{
+	void *temp, *p_from, *p_to;
+
+	if (from == to)
+		return;
+
+	temp   = bmalloc(element_size);
+	p_from = darray_item(element_size, dst, from);
+	p_to   = darray_item(element_size, dst, to);
+
+	memcpy(temp, p_from, element_size);
+
+	if (to < from)
+		memmove(darray_item(element_size, dst, to+1), p_to,
+				element_size*(from-to));
+	else
+		memcpy(p_from, darray_item(element_size, dst, from+1),
+				element_size*(to-from));
+
+	memcpy(p_to, temp, element_size);
+	bfree(temp);
+}
+
+static inline void darray_swap(const size_t element_size,
+		struct darray *dst, const size_t a, const size_t b)
+{
+	void *temp, *a_ptr, *b_ptr;
+
+	assert(a < dst->num);
+	assert(b < dst->num);
+
+	if (a == b)
+		return;
+
+	temp  = bmalloc(element_size);
+	a_ptr = darray_item(element_size, dst, a);
+	b_ptr = darray_item(element_size, dst, b);
+
+	memcpy(temp,  a_ptr, element_size);
+	memcpy(a_ptr, b_ptr, element_size);
+	memcpy(b_ptr, temp,  element_size);
+
+	bfree(temp);
+}
+
+/*
+ * Defines to make dynamic arrays more type-safe.
+ * Note: Still not 100% type-safe but much better than using darray directly
+ *       Makes it a little easier to use as well.
+ *
+ *       I did -not- want to use a gigantic macro to generate a crapload of
+ *       typsafe inline functions per type.  It just feels like a mess to me.
+ */
+
+#define DARRAY(type)                     \
+	union {                          \
+		struct darray da;        \
+		struct {                 \
+			type *array;     \
+			size_t num;      \
+			size_t capacity; \
+		};                       \
+	} 
+
+#define da_init(v) darray_init(&v.da)
+
+#define da_free(v) darray_free(&v.da)
+
+#define da_alloc_size(v) (sizeof(*v.array)*v.num)
+
+#define da_end(v) darray_end(sizeof(*v.array), &v.da)
+
+#define da_reserve(v, capacity) \
+	darray_reserve(sizeof(*v.array), &v.da, capacity)
+
+#define da_resize(v, size) darray_resize(sizeof(*v.array), &v.da, size)
+
+#define da_copy(dst, src)  \
+	darray_copy(sizeof(*dst.array), &dst.da, &src.da)
+
+#define da_copy_array(dst, src_array, n) \
+	darray_copy_array(sizeof(*dst.array), &dst.da, src_array, n)
+
+#define da_move(dst, src) darray_move(&dst.da, &src.da)
+
+#define da_find(v, item, idx) \
+	darray_find(sizeof(*v.array), &v.da, item, idx)
+
+#define da_push_back(v, item) darray_push_back(sizeof(*v.array), &v.da, item)
+
+#define da_push_back_new(v) darray_push_back_new(sizeof(*v.array), &v.da)
+
+#define da_push_back_array(dst, src_array, n) \
+	darray_push_back_array(sizeof(*dst.array), &dst.da, src_array, n)
+
+#define da_push_back_da(dst, src) \
+	darray_push_back_darray(sizeof(*dst.array), &dst.da, &src.da)
+
+#define da_insert(v, idx, item) \
+	darray_insert(sizeof(*v.array), &v.da, idx, item)
+
+#define da_insert_new(v, idx) \
+	darray_insert_new(sizeof(*v.array), &v.da, idx)
+
+#define da_insert_array(dst, idx, src_array, n) \
+	darray_insert_array(sizeof(*dst.array), &dst.da, idx, \
+			src_array, n)
+
+#define da_insert_da(dst, idx, src) \
+	darray_insert_darray(sizeof(*dst.array), &dst.da, idx, \
+			&src.da)
+
+#define da_erase(dst, idx) \
+	darray_erase(sizeof(*dst.array), &dst.da, idx)
+
+#define da_erase_item(dst, item) \
+	darray_erase_item(sizeof(*dst.array), &dst.da, item)
+
+#define da_erase_range(dst, from, to) \
+	darray_erase_range(sizeof(*dst.array), &dst.da, from, to)
+
+#define da_pop_back(dst) \
+	darray_pop_back(sizeof(*dst.array), &dst.da);
+
+#define da_join(dst, src) \
+	darray_join(sizeof(*dst.array), &dst.da, &src.da)
+
+#define da_split(dst1, dst2, src, idx) \
+	darray_split(sizeof(*src.array), &dst1.da, &dst2.da, \
+			&src.da, idx)
+
+#define da_move_item(v, from, to) \
+	darray_move_item(sizeof(*v.array), &v.da, from, to)
+
+#define da_swap_item(v, idx1, idx2) \
+	darray_swap(sizeof(v.array), &v.da, idx1, idx2)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 611 - 0
libobs/util/dstr.c

@@ -0,0 +1,611 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include <wchar.h>
+#include "c99defs.h"
+#include "dstr.h"
+#include "bmem.h"
+#include "utf8.h"
+#include "lexer.h"
+#include "platform.h"
+
+static const char *astrblank = "";
+static const wchar_t *wstrblank = L"";
+
+int astrcmpi(const char *str1, const char *str2)
+{
+	if (!str1)
+		str1 = astrblank;
+	if (!str2)
+		str2 = astrblank;
+
+	do {
+		char ch1 = toupper(*str1);
+		char ch2 = toupper(*str2);
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (*str1++ && *str2++);
+
+	return 0;
+}
+
+int wstrcmpi(const wchar_t *str1, const wchar_t *str2)
+{
+	if (!str1)
+		str1 = wstrblank;
+	if (!str2)
+		str2 = wstrblank;
+
+	do {
+		wchar_t ch1 = towupper(*str1);
+		wchar_t ch2 = towupper(*str2);
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (*str1++ && *str2++);
+
+	return 0;
+}
+
+int astrcmp_n(const char *str1, const char *str2, size_t n)
+{
+	if (!n)
+		return 0;
+	if (!str1)
+		str1 = astrblank;
+	if (!str2)
+		str2 = astrblank;
+
+	do {
+		char ch1 = *str1;
+		char ch2 = *str2;
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (*str1++ && *str2++ && --n);
+
+	return 0;
+}
+
+int wstrcmp_n(const wchar_t *str1, const wchar_t *str2, size_t n)
+{
+	if (!n)
+		return 0;
+	if (!str1)
+		str1 = wstrblank;
+	if (!str2)
+		str2 = wstrblank;
+
+	do {
+		wchar_t ch1 = *str1;
+		wchar_t ch2 = *str2;
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (*str1++ && *str2++ && --n);
+
+	return 0;
+}
+
+int astrcmpi_n(const char *str1, const char *str2, size_t n)
+{
+	if (!n)
+		return 0;
+	if (!str1)
+		str1 = astrblank;
+	if (!str2)
+		str2 = astrblank;
+
+	do {
+		char ch1 = toupper(*str1);
+		char ch2 = toupper(*str2);
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (*str1++ && *str2++ && --n);
+
+	return 0;
+}
+
+int wstrcmpi_n(const wchar_t *str1, const wchar_t *str2, size_t n)
+{
+	if (!n)
+		return 0;
+	if (!str1)
+		str1 = wstrblank;
+	if (!str2)
+		str2 = wstrblank;
+
+	do {
+		wchar_t ch1 = towupper(*str1);
+		wchar_t ch2 = towupper(*str2);
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (*str1++ && *str2++ && --n);
+
+	return 0;
+}
+
+char *strdepad(char *str)
+{
+	char *temp;
+	size_t  len;
+
+	if (!str)
+		return str;
+	if (!*str)
+		return str;
+
+	temp = str;
+
+	/* remove preceding spaces/tabs */
+	while (*temp == ' ' || *temp == '\t')
+		++temp;
+
+	len = strlen(str);
+	if (temp != str)
+		memmove(str, temp, len + 1);
+
+	if (len) {
+		temp = str + (len-1);
+		while (*temp == ' ' || *temp == '\t')
+			*(temp--) = 0;
+	}
+
+	return str;
+}
+
+wchar_t *wcsdepad(wchar_t *str)
+{
+	wchar_t *temp;
+	size_t  len;
+
+	if (!str)
+		return str;
+	if (!*str)
+		return str;
+
+	temp = str;
+
+	/* remove preceding spaces/tabs */
+	while (*temp == ' ' || *temp == '\t')
+		++temp;
+
+	len = wcslen(str);
+	if (temp != str)
+		memmove(str, temp, (len+1) * sizeof(wchar_t));
+
+	if (len) {
+		temp = str + (len-1);
+		while (*temp == ' ' || *temp == '\t')
+			*(temp--) = 0;
+	}
+
+	return str;
+}
+
+void dstr_init_strref(struct dstr *dst, const struct strref *src)
+{
+	dstr_init(dst);
+	dstr_copy_strref(dst, src);
+}
+
+void dstr_copy(struct dstr *dst, const char *array)
+{
+	size_t len;
+
+	if (!array || !*array) {
+		dstr_free(dst);
+		return;
+	}
+
+	len = strlen(array);
+	dstr_ensure_capacity(dst, len + 1);
+	memcpy(dst->array, array, len + 1);
+	dst->len = len;
+}
+
+void dstr_copy_strref(struct dstr *dst, const struct strref *src)
+{
+	if (dst->array)
+		dstr_free(dst);
+
+	dstr_ncopy(dst, src->array, src->len);
+}
+
+static inline size_t size_min(size_t a, size_t b)
+{
+	return (a < b) ? a : b;
+}
+
+void dstr_ncopy(struct dstr *dst, const char *array, const size_t len)
+{
+	if (dst->array)
+		dstr_free(dst);
+
+	if (!len)
+		return;
+
+	dst->array = bmemdup(array, len + 1);
+	dst->len   = len;
+
+	dst->array[len] = 0;
+}
+
+void dstr_ncopy_dstr(struct dstr *dst, const struct dstr *str, const size_t len)
+{
+	size_t newlen;
+
+	if (dst->array)
+		dstr_free(dst);
+
+	if (!len)
+		return;
+
+	newlen = size_min(len, str->len);
+	dst->array = bmemdup(str->array, newlen + 1);
+	dst->len   = newlen;
+
+	dst->array[newlen] = 0;
+}
+
+void dstr_cat_dstr(struct dstr *dst, const struct dstr *str)
+{
+	size_t new_len;
+	if (!str->len)
+		return;
+
+	new_len = dst->len + str->len;
+
+	dstr_ensure_capacity(dst, new_len + 1);
+	memcpy(dst->array+dst->len, str->array, str->len + 1);
+	dst->len = new_len;
+}
+
+void dstr_cat_strref(struct dstr *dst, const struct strref *str)
+{
+	dstr_ncat(dst, str->array, str->len);
+}
+
+void dstr_ncat(struct dstr *dst, const char *array, const size_t len)
+{
+	size_t new_len;
+	if (!array || !*array || !len)
+		return;
+
+	new_len = dst->len + len;
+
+	dstr_ensure_capacity(dst, new_len + 1);
+	memcpy(dst->array+dst->len, array, len);
+
+	dst->len = new_len;
+	dst->array[new_len] = 0;
+}
+
+void dstr_ncat_dstr(struct dstr *dst, const struct dstr *str, const size_t len)
+{
+	size_t new_len, in_len;
+	if (!str->array || !*str->array || !len)
+		return;
+
+	in_len = size_min(len, str->len);
+	new_len = dst->len + in_len;
+
+	dstr_ensure_capacity(dst, new_len + 1);
+	memcpy(dst->array+dst->len, str->array, in_len);
+
+	dst->len = new_len;
+	dst->array[new_len] = 0;
+}
+
+void dstr_insert(struct dstr *dst, const size_t idx, const char *array)
+{
+	size_t new_len, len;
+	if (!array || !*array)
+		return;
+	if (idx == dst->len) {
+		dstr_cat(dst, array);
+		return;
+	}
+
+	len = strlen(array);
+	new_len = dst->len + len;
+
+	dstr_ensure_capacity(dst, new_len + 1);
+	dst->len   = new_len;
+
+	memmove(dst->array+idx+len, dst->array+idx, dst->len - idx + 1);
+	memcpy(dst->array+idx, array, len);
+}
+
+void dstr_insert_dstr(struct dstr *dst, const size_t idx,
+		const struct dstr *str)
+{
+	size_t new_len;
+	if (!str->len)
+		return;
+	if (idx == dst->len) {
+		dstr_cat_dstr(dst, str);
+		return;
+	}
+
+	new_len = dst->len + str->len;
+
+	dstr_ensure_capacity(dst, (new_len+1));
+	dst->len = new_len;
+
+	memmove(dst->array+idx+str->len, dst->array+idx, dst->len - idx + 1);
+	memcpy(dst->array+idx, str->array, str->len);
+}
+
+void dstr_insert_ch(struct dstr *dst, const size_t idx, const char ch)
+{
+	if (idx == dst->len) {
+		dstr_cat_ch(dst, ch);
+		return;
+	}
+
+	dstr_ensure_capacity(dst, (++dst->len+1));
+	memmove(dst->array+idx+1, dst->array+idx, dst->len - idx + 1);
+	dst->array[idx] = ch;
+}
+
+void dstr_remove(struct dstr *dst, const size_t idx, const size_t count)
+{
+	size_t end;
+	if (!count)
+		return;
+	if (count == dst->len) {
+		dstr_free(dst);
+		return;
+	}
+
+	end = idx+count;
+	if (end == dst->len)
+		dst->array[idx] = 0;
+	else
+		memmove(dst->array+idx, dst->array+end, dst->len - end + 1);
+
+	dst->len   -= count;
+}
+
+void dstr_printf(struct dstr *dst, const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	dstr_vprintf(dst, format, args);
+	va_end(args);
+}
+
+void dstr_catf(struct dstr *dst, const char *format, ...)
+{
+	va_list args;
+	va_start(args, format);
+	dstr_vcatf(dst, format, args);
+	va_end(args);
+}
+
+void dstr_vprintf(struct dstr *dst, const char *format, va_list args)
+{
+	dstr_ensure_capacity(dst, 4096);
+	vsnprintf(dst->array, 4095, format, args);
+
+	if (!*dst->array) {
+		dstr_free(dst);
+		return;
+	}
+
+	dst->len = strlen(dst->array);
+}
+
+void dstr_vcatf(struct dstr *dst, const char *format, va_list args)
+{
+	struct dstr temp;
+	dstr_init(&temp);
+	dstr_vprintf(&temp, format, args);
+	dstr_cat_dstr(dst, &temp);
+	dstr_free(&temp);
+}
+
+void dstr_safe_printf(struct dstr *dst, const char *format,
+		const char *val1, const char *val2, const char *val3,
+		const char *val4)
+{
+	dstr_copy(dst, format);
+	if (val1)
+		dstr_replace(dst, "$1", val1);
+	if (val2)
+		dstr_replace(dst, "$2", val2);
+	if (val3)
+		dstr_replace(dst, "$3", val3);
+	if (val4)
+		dstr_replace(dst, "$4", val4);
+}
+
+void dstr_replace(struct dstr *str, const char *find,
+		const char *replace)
+{
+	size_t find_len, replace_len;
+	char *temp;
+
+	if (!replace)
+		replace = "";
+
+	find_len    = strlen(find);
+	replace_len = strlen(replace);
+	temp = str->array;
+
+	if (replace_len < find_len) {
+		int count = 0;
+
+		while ((temp = strstr(temp, find)) != NULL) {
+			char *end = temp+find_len;
+			size_t end_len = strlen(end);
+
+			if (end_len) {
+				memmove(temp+replace_len, end, end_len + 1);
+				if (replace_len)
+					memcpy(temp, replace, replace_len);
+			} else {
+				strcpy(temp, replace);
+			}
+
+			temp += replace_len;
+			++count;
+		}
+
+		if (count)
+			str->len += (replace_len-find_len) * count;
+
+	} else if (replace_len > find_len) {
+		int count = 0;
+
+		while ((temp = strstr(temp, find)) != NULL) {
+			temp += find_len;
+			++count;
+		}
+
+		if (!count)
+			return;
+
+		str->len += (replace_len-find_len) * count;
+		dstr_ensure_capacity(str, str->len + 1);
+		temp = str->array;
+
+		while ((temp = strstr(temp, find)) != NULL) {
+			char *end   = temp+find_len;
+			size_t end_len = strlen(end);
+
+			if (end_len) {
+				memmove(temp+replace_len, end, end_len + 1);
+				memcpy(temp, replace, replace_len);
+			} else {
+				strcpy(temp, replace);
+			}
+
+			temp += replace_len;
+		}
+
+	} else {
+		while ((temp = strstr(temp, find)) != NULL) {
+			memcpy(temp, replace, replace_len);
+			temp += replace_len;
+		}
+	}
+}
+
+void dstr_depad(struct dstr *str)
+{
+	if (str->array) {
+		str->array = strdepad(str->array);
+		if (*str->array)
+			str->len = strlen(str->array);
+		else
+			dstr_free(str);
+	}
+}
+
+void dstr_left(struct dstr *dst, const struct dstr *str, const size_t pos)
+{
+	dstr_resize(dst, pos);
+	if (dst != str)
+		memcpy(dst->array, str->array, pos);
+}
+
+void dstr_mid(struct dstr *dst, const struct dstr *str, const size_t start,
+		const size_t count)
+{
+	struct dstr temp;
+	dstr_init(&temp);
+	dstr_copy_dstr(&temp, str);
+	dstr_ncopy(dst, temp.array+start, count);
+	dstr_free(&temp);
+}
+
+void dstr_right(struct dstr *dst, const struct dstr *str, const size_t pos)
+{
+	struct dstr temp;
+	dstr_init(&temp);
+	dstr_ncopy(&temp, str->array+pos, str->len-pos);
+	dstr_copy_dstr(dst, &temp);
+	dstr_free(&temp);
+}
+
+void dstr_from_mbs(struct dstr *dst, const char *mbstr)
+{
+	dstr_free(dst);
+	dst->len = os_mbs_to_utf8(mbstr, 0, &dst->array);
+}
+
+char *dstr_to_mbs(const struct dstr *str)
+{
+	char *dst;
+	os_mbs_to_utf8(str->array, str->len, &dst);
+	return dst;
+}
+
+void dstr_from_wcs(struct dstr *dst, const wchar_t *wstr)
+{
+	size_t len = wchar_to_utf8(wstr, 0, NULL, 0, 0);
+
+	if (len) {
+		dstr_resize(dst, len);
+		wchar_to_utf8(wstr, 0, dst->array, len+1, 0);
+	} else {
+		dstr_free(dst);
+	}
+}
+
+wchar_t *dstr_to_utf8(const struct dstr *str)
+{
+	wchar_t *out = NULL;
+	size_t len = utf8_to_wchar(str->array, str->len, NULL, 0, 0);
+
+	if (len) {
+		out = bmalloc((len+1) * sizeof(wchar_t));
+		utf8_to_wchar(str->array, str->len, out, len+1, 0);
+		out[len] = 0;
+	}
+
+	return out;
+}

+ 310 - 0
libobs/util/dstr.h

@@ -0,0 +1,310 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef DSTR_H
+#define DSTR_H
+
+#include <string.h>
+#include <stdarg.h>
+#include "c99defs.h"
+#include "bmem.h"
+
+/*
+ * Dynamic string
+ *
+ *   Helper struct/functions for dynamically sizing string buffers.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct strref;
+
+struct dstr {
+	char *array;
+	size_t len; /* number of characters, excluding null terminator */
+	size_t capacity;
+};
+
+EXPORT int astrcmpi(const char *str1, const char *str2);
+EXPORT int wstrcmpi(const wchar_t *str1, const wchar_t *str2);
+EXPORT int astrcmp_n(const char *str1, const char *str2, size_t n);
+EXPORT int wstrcmp_n(const wchar_t *str1, const wchar_t *str2, size_t n);
+EXPORT int astrcmpi_n(const char *str1, const char *str2, size_t n);
+EXPORT int wstrcmpi_n(const wchar_t *str1, const wchar_t *str2, size_t n);
+
+EXPORT char *strdepad(char *str);
+EXPORT wchar_t *wcsdepad(wchar_t *str);
+
+static inline void dstr_init(struct dstr *dst);
+static inline void dstr_init_move(struct dstr *dst, struct dstr *src);
+static inline void dstr_init_move_array(struct dstr *dst, char *str);
+static inline void dstr_init_copy(struct dstr *dst, const char *src);
+static inline void dstr_init_copy_dstr(struct dstr *dst,
+		const struct dstr *src);
+EXPORT void dstr_init_strref(struct dstr *dst, const struct strref *src);
+
+static inline void dstr_free(struct dstr *dst);
+static inline void dstr_array_free(struct dstr *array, const size_t count);
+
+static inline void dstr_move(struct dstr *dst, struct dstr *src);
+static inline void dstr_move_array(struct dstr *dst, char *str);
+
+EXPORT void dstr_copy(struct dstr *dst, const char *array);
+static inline void dstr_copy_dstr(struct dstr *dst, const struct dstr *src);
+EXPORT void dstr_copy_strref(struct dstr *dst, const struct strref *src);
+
+EXPORT void dstr_ncopy(struct dstr *dst, const char *array,
+		const size_t len);
+EXPORT void dstr_ncopy_dstr(struct dstr *dst, const struct dstr *src,
+		const size_t len);
+
+static inline void dstr_resize(struct dstr *dst, const size_t num);
+static inline void dstr_reserve(struct dstr *dst, const size_t num);
+
+static inline bool dstr_isempty(const struct dstr *str);
+
+static inline void dstr_cat(struct dstr *dst, const char *array);
+EXPORT void dstr_cat_dstr(struct dstr *dst, const struct dstr *str);
+EXPORT void dstr_cat_strref(struct dstr *dst, const struct strref *str);
+
+static inline void dstr_cat_ch(struct dstr *dst, char ch);
+
+EXPORT void dstr_ncat(struct dstr *dst, const char *array, const size_t len);
+EXPORT void dstr_ncat_dstr(struct dstr *dst, const struct dstr *str,
+		const size_t len);
+
+EXPORT void dstr_cat_strref(struct dstr *dst, const struct strref *str);
+
+EXPORT void dstr_insert(struct dstr *dst, const size_t idx,
+		const char *array);
+EXPORT void dstr_insert_dstr(struct dstr *dst, const size_t idx,
+		const struct dstr *str);
+EXPORT void dstr_insert_ch(struct dstr *dst, const size_t idx,
+		const char ch);
+
+EXPORT void dstr_remove(struct dstr *dst, const size_t idx, const size_t count);
+
+EXPORT void dstr_printf(struct dstr *dst, const char *format, ...);
+EXPORT void dstr_catf(struct dstr *dst, const char *format, ...);
+
+EXPORT void dstr_vprintf(struct dstr *dst, const char *format, va_list args);
+EXPORT void dstr_vcatf(struct dstr *dst, const char *format, va_list args);
+
+EXPORT void dstr_safe_printf(struct dstr *dst, const char *format,
+		const char *val1, const char *val2, const char *val3,
+		const char *val4);
+
+static inline const char *dstr_find(const struct dstr *str,
+		const char *find);
+
+EXPORT void dstr_replace(struct dstr *str, const char *find,
+		const char *replace);
+
+static inline int dstr_cmp(const struct dstr *str1, const char *str2);
+static inline int dstr_cmpi(const struct dstr *str1, const char *str2);
+static inline int dstr_ncmp(const struct dstr *str1, const char *str2,
+		const size_t n);
+static inline int dstr_ncmpi(const struct dstr *str1, const char *str2,
+		const size_t n);
+
+EXPORT void dstr_depad(struct dstr *dst);
+
+EXPORT void dstr_left(struct dstr *dst, const struct dstr *str,
+		const size_t pos);
+EXPORT void dstr_mid(struct dstr *dst, const struct dstr *str,
+		const size_t start, const size_t count);
+EXPORT void dstr_right(struct dstr *dst, const struct dstr *str,
+		const size_t pos);
+
+EXPORT void dstr_from_mbs(struct dstr *dst, const char *mbstr);
+EXPORT char *dstr_to_mbs(const struct dstr *str);
+EXPORT void dstr_from_wcs(struct dstr *dst, const wchar_t *wstr);
+EXPORT wchar_t *dstr_to_wcs(const struct dstr *str);
+
+/* ------------------------------------------------------------------------- */
+
+static inline void dstr_init(struct dstr *dst)
+{
+	dst->array    = NULL;
+	dst->len      = 0;
+	dst->capacity = 0;
+}
+
+static inline void dstr_init_move_array(struct dstr *dst, char *str)
+{
+	dst->array = str;
+	dst->len = (!str) ? 0 : strlen(str);
+}
+
+static inline void dstr_init_move(struct dstr *dst, struct dstr *src)
+{
+	*dst = *src;
+	dstr_init(src);
+}
+
+static inline void dstr_init_copy(struct dstr *dst, const char *str)
+{
+	dstr_init(dst);
+	dstr_copy(dst, str);
+}
+
+static inline void dstr_init_copy_dstr(struct dstr *dst, const struct dstr *src)
+{
+	dstr_init(dst);
+	dstr_copy_dstr(dst, src);
+}
+
+static inline void dstr_free(struct dstr *dst)
+{
+	bfree(dst->array);
+	dst->array    = NULL;
+	dst->len      = 0;
+	dst->capacity = 0;
+}
+
+static inline void dstr_array_free(struct dstr *array, const size_t count)
+{
+	size_t i;
+	for (i = 0; i < count; i++)
+		dstr_free(array+i);
+}
+
+static inline void dstr_move_array(struct dstr *dst, char *str)
+{
+	dstr_free(dst);
+	dst->array = str;
+	dst->len = (!str) ? 0 : strlen(str);
+}
+
+static inline void dstr_move(struct dstr *dst, struct dstr *src)
+{
+	dstr_free(dst);
+	dstr_init_move(dst, src);
+}
+
+static inline void dstr_ensure_capacity(struct dstr *dst, const size_t new_size)
+{
+	size_t new_cap;
+	if (new_size <= dst->capacity)
+		return;
+
+	new_cap = (!dst->capacity) ? new_size : dst->capacity*2;
+	if (new_size > new_cap)
+		new_cap = new_size;
+	dst->array = (char*)brealloc(dst->array, new_cap);
+	dst->capacity = new_cap;
+}
+
+static inline void dstr_copy_dstr(struct dstr *dst, const struct dstr *src)
+{
+	if (dst->array)
+		dstr_free(dst);
+
+	dstr_ensure_capacity(dst, src->len + 1);
+	memcpy(dst->array, src->array, src->len + 1);
+	dst->len = src->len;
+}
+
+static inline void dstr_reserve(struct dstr *dst, const size_t capacity)
+{
+	if (capacity == 0 || capacity <= dst->len)
+		return;
+
+	dst->array = (char*)brealloc(dst->array, capacity);
+	dst->capacity = capacity;
+}
+
+static inline void dstr_resize(struct dstr *dst, const size_t num)
+{
+	if (!num) {
+		dstr_free(dst);
+		return;
+	}
+
+	dstr_ensure_capacity(dst, num + 1);
+	dst->array[num] = 0;
+	dst->len = num;
+}
+
+static inline bool dstr_isempty(const struct dstr *str)
+{
+	if (!str->array || !str->len)
+		return true;
+	if (!*str->array)
+		return true;
+
+	return false;
+}
+
+static inline void dstr_cat(struct dstr *dst, const char *array)
+{
+	size_t len;
+	if (!array || !*array)
+		return;
+
+	len = strlen(array);
+	dstr_ncat(dst, array, len);
+}
+
+static inline void dstr_cat_ch(struct dstr *dst, char ch)
+{
+	dstr_ensure_capacity(dst, ++dst->len + 1);
+	dst->array[dst->len-1] = ch;
+	dst->array[dst->len]   = 0;
+}
+
+static inline const char *dstr_find(const struct dstr *str,
+		const char *find)
+{
+	return strstr(str->array, find);
+}
+
+static inline int dstr_cmp(const struct dstr *str1, const char *str2)
+{
+	return strcmp(str1->array, str2);
+}
+
+static inline int dstr_cmpi(const struct dstr *str1, const char *str2)
+{
+	return astrcmpi(str1->array, str2);
+}
+
+static inline int dstr_ncmp(const struct dstr *str1, const char *str2,
+		const size_t n)
+{
+	return astrcmp_n(str1->array, str2, n);
+}
+
+static inline int dstr_ncmpi(const struct dstr *str1, const char *str2,
+		const size_t n)
+{
+	return astrcmpi_n(str1->array, str2, n);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 321 - 0
libobs/util/lexer.c

@@ -0,0 +1,321 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <ctype.h>
+#include "lexer.h"
+
+static const char *astrblank = "";
+
+static inline bool strref_is_empty(const struct strref *str)
+{
+	return !str || !str->array || !str->len || !*str->array;
+}
+
+int strref_cmp(const struct strref *str1, const char *str2)
+{
+	size_t i = 0;
+
+	if (strref_is_empty(str1))
+		return (!str2 || !*str2) ? 0 : -1;
+	if (!str2)
+		str2 = astrblank;
+
+	do {
+		char ch1, ch2;
+
+		ch1 = (i < str1->len) ? str1->array[i] : 0;
+                ch2 = *str2;
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (i++ < str1->len && *str2++);
+
+	return 0;
+}
+
+int strref_cmpi(const struct strref *str1, const char *str2)
+{
+	size_t i = 0;
+
+	if (strref_is_empty(str1))
+		return (!str2 || !*str2) ? 0 : -1;
+	if (!str2)
+		str2 = astrblank;
+
+	do {
+		char ch1, ch2;
+
+		ch1 = (i < str1->len) ? toupper(str1->array[i]) : 0;
+                ch2 = toupper(*str2);
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+	} while (i++ < str1->len && *str2++);
+
+	return 0;
+}
+
+int strref_cmp_strref(const struct strref *str1, const struct strref *str2)
+{
+	size_t i = 0;
+
+	if (strref_is_empty(str1))
+		return strref_is_empty(str2) ? 0 : -1;
+	if (strref_is_empty(str2))
+		return -1;
+
+	do {
+		char ch1, ch2;
+
+		ch1 = (i < str1->len) ? str1->array[i] : 0;
+                ch2 = (i < str2->len) ? str2->array[i] : 0;
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+
+		i++;
+	} while (i <= str1->len && i <= str2->len);
+
+	return 0;
+}
+
+int strref_cmpi_strref(const struct strref *str1, const struct strref *str2)
+{
+	size_t i = 0;
+
+	if (strref_is_empty(str1))
+		return strref_is_empty(str2) ? 0 : -1;
+	if (strref_is_empty(str2))
+		return -1;
+
+	do {
+		char ch1, ch2;
+
+		ch1 = (i < str1->len) ? toupper(str1->array[i]) : 0;
+		ch2 = (i < str2->len) ? toupper(str2->array[i]) : 0;
+
+		if (ch1 < ch2)
+			return -1;
+		else if (ch1 > ch2)
+			return 1;
+
+		i++;
+	} while (i <= str1->len && i <= str2->len);
+
+	return 0;
+}
+/* ------------------------------------------------------------------------- */
+
+bool valid_int_str(const char *str, size_t n)
+{
+	bool found_num = false;
+
+	if (!str)
+		return false;
+	if (!*str)
+		return false;
+
+	if (!n)
+		n = strlen(str);
+	if (*str == '-' || *str == '+')
+		++str;
+
+	do {
+		if (*str > '9' || *str < '0')
+			return false;
+
+		found_num = true;
+	} while(*++str && --n);
+
+	return found_num;
+}
+
+bool valid_float_str(const char *str, size_t n)
+{
+	bool found_num = false;
+	bool found_exp = false;
+	bool found_dec = false;
+
+	if (!str)
+		return false;
+	if (!*str)
+		return false;
+
+	if (!n)
+		n = strlen(str);
+	if (*str == '-' || *str == '+')
+		++str;
+
+	do {
+		if (*str == '.') {
+			if (found_dec || found_exp || !found_num)
+				return false;
+
+			found_dec = true;
+
+		} else if (*str == 'e') {
+			if (found_exp || !found_num)
+				return false;
+
+			found_exp = true;
+			found_num = false;
+
+		} else if (*str == '-' || *str == '+') {
+			if (!found_exp || !found_num)
+				return false;
+
+		} else if (*str > '9' || *str < '0') {
+			return false;
+		} else {
+			found_num = true;
+		}
+	} while(*++str && --n);
+
+	return found_num;
+}
+
+/* ------------------------------------------------------------------------- */
+
+void error_data_add(struct error_data *data, const char *file,
+		uint32_t row, uint32_t column, const char *msg, int level)
+{
+	struct error_item item;
+
+	if (!data)
+		return;
+
+	item.file   = file;
+	item.row    = row;
+	item.column = column;
+	item.level  = level;
+	item.error  = bstrdup(msg);
+
+	da_push_back(data->errors, &item);
+}
+
+char *error_data_buildstring(struct error_data *ed)
+{
+	struct dstr str;
+	struct error_item *items = ed->errors.array;
+	size_t i;
+
+	dstr_init(&str);
+	for (i = 0; i < ed->errors.num; i++) {
+		struct error_item *item = items+i;
+		dstr_catf(&str, "%s (%u, %u): %s\n", item->file, item->row,
+				item->column, item->error);
+	}
+
+	return str.array;
+}
+
+/* ------------------------------------------------------------------------- */
+
+static inline enum base_token_type get_char_token_type(const char ch)
+{
+	if (is_whitespace(ch))
+		return BASETOKEN_WHITESPACE;
+	else if (isdigit(ch))
+		return BASETOKEN_DIGIT;
+	else if (isalpha(ch))
+		return BASETOKEN_ALPHA;
+
+	return BASETOKEN_OTHER;
+}
+
+bool lexer_getbasetoken(struct lexer *lex, struct base_token *token,
+		bool ignore_whitespace)
+{
+	const char *offset = lex->offset;
+	const char *token_start = NULL;
+	enum base_token_type type = BASETOKEN_NONE;
+
+	if (!offset)
+		return false;
+
+	while (*offset != 0) {
+		char ch = *(offset++);
+		enum base_token_type new_type = get_char_token_type(ch);
+
+		if (type == BASETOKEN_NONE) {
+			if (new_type == BASETOKEN_WHITESPACE &&
+					ignore_whitespace)
+				continue;
+
+			token_start = offset-1;
+			type = new_type;
+
+			if (type != BASETOKEN_DIGIT ||
+			    type != BASETOKEN_ALPHA) {
+				if (is_newline(ch) &&
+				    is_newline_pair(ch, *offset)) {
+					offset++;
+				}
+				break;
+			}
+		} else if (type != new_type) {
+			break;
+		}
+	}
+
+	lex->offset = offset;
+
+	if (token_start && offset > token_start) {
+		strref_set(&token->text, token_start, offset-token_start);
+		token->type = type;
+		return true;
+	}
+
+	return false;
+}
+
+void lexer_getstroffset(const struct lexer *lex, const char *str,
+		uint32_t *row, uint32_t *col)
+{
+	uint32_t cur_col = 1, cur_row = 1;
+	const char *text = lex->text;
+
+	if (!str)
+		return;
+
+	while (text < str) {
+		if (is_newline(*text)) {
+			text += newline_size(text)-1;
+			cur_col = 1;
+			cur_row++;
+		} else {
+			cur_col++;
+		}
+
+		text++;
+	}
+
+	*row = cur_row;
+	*col = cur_col;
+}

+ 294 - 0
libobs/util/lexer.h

@@ -0,0 +1,294 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef LEXER_H
+#define LEXER_H
+
+#include "c99defs.h"
+#include "dstr.h"
+#include "darray.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* ------------------------------------------------------------------------- */
+/* string reference (string segment within an already existing array) */
+
+struct strref {
+	const char *array;
+	size_t len;
+};
+
+static inline void strref_clear(struct strref *dst)
+{
+	dst->array = NULL;
+	dst->len   = 0;
+}
+
+static inline void strref_set(struct strref *dst, const char *array, size_t len)
+{
+	dst->array = array;
+	dst->len   = len;
+}
+
+static inline void strref_copy(struct strref *dst, const struct strref *src)
+{
+	dst->array = src->array;
+	dst->len   = src->len;
+}
+
+static inline void strref_add(struct strref *dst, const struct strref *t)
+{
+	if (!dst->array)
+		strref_copy(dst, t);
+	else
+		dst->len += t->len;
+}
+
+static inline bool strref_isempty(const struct strref *str)
+{
+	if (!str->array || !str->len)
+		return true;
+	if (!*str->array)
+		return true;
+
+	return false;
+}
+
+EXPORT int strref_cmp(const struct strref *str1, const char *str2);
+EXPORT int strref_cmpi(const struct strref *str1, const char *str2);
+EXPORT int strref_cmp_strref(const struct strref *str1,
+		const struct strref *str2);
+EXPORT int strref_cmpi_strref(const struct strref *str1,
+		const struct strref *str2);
+
+/* ------------------------------------------------------------------------- */
+
+EXPORT bool valid_int_str(const char *str, size_t n);
+EXPORT bool valid_float_str(const char *str, size_t n);
+
+static inline bool valid_int_strref(const struct strref *str)
+{
+	return valid_int_str(str->array, str->len);
+}
+
+static inline bool valid_float_strref(const struct strref *str)
+{
+	return valid_float_str(str->array, str->len);
+}
+
+static inline bool is_whitespace(char ch)
+{
+	return ch == ' ' || ch == '\r' || ch == '\t' || ch == '\n';
+}
+
+static inline bool is_newline(char ch)
+{
+	return ch == '\r' || ch == '\n';
+}
+
+static inline bool is_space_or_tab(const char ch)
+{
+	return ch == ' ' || ch == '\t';
+}
+
+static inline bool is_newline_pair(char ch1, char ch2)
+{
+	return (ch1 == '\r' && ch2 == '\n') ||
+	       (ch1 == '\n' && ch2 == '\r');
+}
+
+static inline int newline_size(const char *array)
+{
+	if (strncmp(array, "\r\n", 2) == 0 || strncmp(array, "\n\r", 2) == 0)
+		return 2;
+	else if (*array == '\r' || *array == '\n')
+		return 1;
+
+	return 0;
+}
+
+/* ------------------------------------------------------------------------- */
+
+/* 
+ * A "base" token is one of four things:
+ *   1.) A sequence of alpha characters
+ *   2.) A sequence of numeric characters
+ *   3.) A single whitespace character if whitespace is not ignored
+ *   4.) A single character that does not fall into the above 3 categories
+ */
+
+enum base_token_type {
+	BASETOKEN_NONE,
+	BASETOKEN_ALPHA,
+	BASETOKEN_DIGIT,
+	BASETOKEN_WHITESPACE,
+	BASETOKEN_OTHER,
+};
+
+struct base_token {
+	struct strref text;
+	enum base_token_type type;
+	bool passed_whitespace;
+};
+
+static inline void base_token_clear(struct base_token *t)
+{
+	memset(t, 0, sizeof(struct base_token));
+}
+
+static inline void base_token_copy(struct base_token *dst,
+		struct base_token *src)
+{
+	memcpy(dst, src, sizeof(struct base_token));
+}
+
+/* ------------------------------------------------------------------------- */
+
+#define LEVEL_ERROR   0
+#define LEVEL_WARNING 1
+
+struct error_item {
+	char *error;
+	const char *file;
+	uint32_t row, column;
+	int level;
+};
+
+static inline void error_item_init(struct error_item *ei)
+{
+	memset(ei, 0, sizeof(struct error_item));
+}
+
+static inline void error_item_free(struct error_item *ei)
+{
+	bfree(ei->error);
+	error_item_init(ei);
+}
+
+static inline void error_item_array_free(struct error_item *array, size_t num)
+{
+	size_t i;
+	for (i = 0; i < num; i++)
+		error_item_free(array+i);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct error_data {
+	DARRAY(struct error_item) errors;
+};
+
+static inline void error_data_init(struct error_data *data)
+{
+	da_init(data->errors);
+}
+
+static inline void error_data_free(struct error_data *data)
+{
+	error_item_array_free(data->errors.array, data->errors.num);
+	da_free(data->errors);
+}
+
+static inline const struct error_item *error_data_item(struct error_data *ed,
+		size_t idx)
+{
+	return ed->errors.array+idx;
+}
+
+EXPORT char *error_data_buildstring(struct error_data *ed);
+
+EXPORT void error_data_add(struct error_data *ed, const char *file,
+		uint32_t row, uint32_t column, const char *msg, int level);
+
+static inline size_t error_data_type_count(struct error_data *ed,
+		int type)
+{
+	size_t count = 0, i;
+	for (i = 0; i < ed->errors.num; i++) {
+		if (ed->errors.array[i].level == type)
+			count++;
+	}
+
+	return count;
+}
+
+static inline bool error_data_has_errors(struct error_data *ed)
+{
+	size_t i;
+	for (i = 0; i < ed->errors.num; i++)
+		if (ed->errors.array[i].level == LEVEL_ERROR)
+			return true;
+
+	return false;
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct lexer {
+	char *text;
+	const char *offset;
+};
+
+static inline void lexer_init(struct lexer *lex)
+{
+	memset(lex, 0, sizeof(struct lexer));
+}
+
+static inline void lexer_free(struct lexer *lex)
+{
+	bfree(lex->text);
+	lexer_init(lex);
+}
+
+static inline void lexer_start(struct lexer *lex, const char *text)
+{
+	lexer_free(lex);
+	lex->text   = bstrdup(text);
+	lex->offset = lex->text;
+}
+
+static inline void lexer_start_move(struct lexer *lex, char *text)
+{
+	lexer_free(lex);
+	lex->text   = text;
+	lex->offset = lex->text;
+}
+
+static inline void lexer_reset(struct lexer *lex)
+{
+	lex->offset = lex->text;
+}
+
+EXPORT bool lexer_getbasetoken(struct lexer *lex, struct base_token *t,
+		bool ignore_whitespace);
+
+EXPORT void lexer_getstroffset(const struct lexer *lex, const char *str,
+		uint32_t *row, uint32_t *col);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 139 - 0
libobs/util/platform-windows.c

@@ -0,0 +1,139 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifdef _WIN32
+
+#include <windows.h>
+#include "base.h"
+#include "platform.h"
+#include "bmem.h"
+
+static bool have_clockfreq = false;
+static LARGE_INTEGER clock_freq;
+static uint32_t winver = 0;
+
+static inline uint64_t get_clockfreq(void)
+{
+	if (!have_clockfreq)
+		QueryPerformanceFrequency(&clock_freq);
+	return clock_freq.QuadPart;
+}
+
+static inline uint32_t get_winver(void)
+{
+	if (!winver) {
+		OSVERSIONINFO osvi;
+		memset(&osvi, 0, sizeof(osvi));
+		winver = (osvi.dwMajorVersion << 16) | (osvi.dwMinorVersion);
+	}
+
+	return winver;	
+}
+
+void *os_dlopen(const char *path)
+{
+	wchar_t *wpath;
+	HMODULE h_library = NULL;
+
+	os_utf8_to_wcs(path, 0, &wpath);
+	h_library = LoadLibraryW(wpath);
+	bfree(wpath);
+
+	if (!h_library)
+		blog(LOG_INFO, "LoadLibrary failed for '%s', error: %u",
+				path, GetLastError());
+
+	return h_library;
+}
+
+void *os_dlsym(void *module, const char *func)
+{
+	return (void*)GetProcAddress(module, func);
+}
+
+void os_dlclose(void *module)
+{
+	FreeLibrary(module);
+}
+
+void os_sleepto_ns(uint64_t time_target)
+{
+	uint64_t t = os_gettime_ns();
+	uint32_t milliseconds;
+
+	if (t >= time_target)
+		return;
+
+	milliseconds = (uint32_t)((time_target - t)/1000000);
+	if (milliseconds > 1)
+		os_sleep_ms(milliseconds);
+
+	for (;;) {
+		t = os_gettime_ns();
+		if (t >= time_target)
+			return;
+
+#if 1
+		Sleep(1);
+#else
+		Sleep(0);
+#endif
+	}
+}
+
+void os_sleep_ms(uint32_t duration)
+{
+	/* windows 8+ appears to have decreased sleep precision */
+	if (get_winver() >= 0x0602 && duration > 0)
+		duration--;
+
+	Sleep(duration);
+}
+
+uint64_t os_gettime_ns(void)
+{
+	LARGE_INTEGER current_time;
+	double time_val;
+
+	QueryPerformanceCounter(&current_time);
+	time_val = (double)current_time.QuadPart;
+	time_val *= 1000000000.0;
+	time_val /= (double)get_clockfreq();
+
+	return (uint64_t)time_val;
+}
+
+uint64_t os_gettime_ms(void)
+{
+	LARGE_INTEGER current_time;
+	uint64_t time_val;
+
+	QueryPerformanceCounter(&current_time);
+	time_val = current_time.QuadPart;
+	time_val *= 1000;
+	time_val /= get_clockfreq();
+
+	return time_val;
+}
+
+#endif

+ 305 - 0
libobs/util/platform.c

@@ -0,0 +1,305 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include <errno.h>
+#include <stdlib.h>
+#include "c99defs.h"
+#include "platform.h"
+#include "bmem.h"
+#include "utf8.h"
+#include "dstr.h"
+
+FILE *os_wfopen(const wchar_t *path, const char *mode)
+{
+	FILE *file;
+#ifdef _MSC_VER
+	wchar_t *wcs_mode;
+
+	os_utf8_to_wcs(mode, 0, &wcs_mode);
+	file = _wfopen(path, wcs_mode);
+	bfree(wcs_mode);
+#else
+	char *mbs_path;
+
+	os_wcs_to_utf8(path, 0, &mbs_path);
+	file = fopen(mbs_path, mode);
+	bfree(mbs_path);
+#endif
+	return file;
+}
+
+FILE *os_fopen(const char *path, const char *mode)
+{
+#ifdef _WIN32
+	wchar_t *wpath = NULL;
+	FILE *file = NULL;
+	os_utf8_to_wcs(path, 0, &wpath);
+	file = os_wfopen(wpath, mode);
+	bfree(wpath);
+
+	return file;
+#else
+	return fopen(path, mode);
+#endif
+}
+
+off_t os_fgetsize(FILE *file)
+{
+	off_t cur_offset = ftello(file);
+	off_t size;
+	int errval = 0;
+
+	if (fseeko(file, 0, SEEK_END) == -1)
+		return -1;
+
+	size = ftello(file);
+	if (size == -1)
+		errval = errno;
+
+	if (fseeko(file, cur_offset, SEEK_SET) != 0 && errval != 0)
+		errno = errval;
+
+	return size;
+}
+
+size_t os_fread_mbs(FILE *file, char **pstr)
+{
+	off_t size = 0;
+	size_t len = 0;
+
+	fseeko(file, 0, SEEK_END);
+	size = ftello(file);
+
+	if (size > 0) {
+		char *mbstr = bmalloc(size+1);
+
+		fseeko(file, 0, SEEK_SET);
+		fread(mbstr, 1, size, file);
+		mbstr[size] = 0;
+		len = os_mbs_to_utf8(mbstr, size, pstr);
+		bfree(mbstr);
+	} else {
+		*pstr = NULL;
+	}
+
+	return len;
+}
+
+size_t os_fread_utf8(FILE *file, char **pstr)
+{
+	off_t size = 0;
+	size_t len = 0;
+
+	fseeko(file, 0, SEEK_END);
+	size = ftello(file);
+
+	if (size > 0) {
+		char bom[3];
+		char *utf8str = bmalloc(size+1);
+		off_t offset;
+
+		/* remove the ghastly BOM if present */
+		fseeko(file, 0, SEEK_SET);
+		fread(bom, 1, 3, file);
+		offset = (astrcmp_n(bom, "\xEF\xBB\xBF", 3) == 0) ? 3 : 0;
+
+		fseeko(file, offset, SEEK_SET);
+		fread(utf8str, 1, size, file);
+		utf8str[size] = 0;
+
+		*pstr = utf8str;
+	} else {
+		*pstr = NULL;
+	}
+
+	return len;
+}
+
+char *os_quick_read_mbs_file(const char *path)
+{
+	FILE *f = os_fopen(path, "rb");
+	char *file_string = NULL;
+
+	if (!f)
+		return NULL;
+
+	os_fread_mbs(f, &file_string);
+	fclose(f);
+
+	return file_string;
+}
+
+char *os_quick_read_utf8_file(const char *path)
+{
+	FILE *f = os_fopen(path, "rb");
+	char *file_string = NULL;
+
+	if (!f)
+		return NULL;
+
+	os_fread_utf8(f, &file_string);
+	fclose(f);
+
+	return file_string;
+}
+
+bool os_quick_write_mbs_file(const char *path, const char *str, size_t len)
+{
+	FILE *f = os_fopen(path, "wb");
+	char *mbs = NULL;
+	size_t mbs_len = 0;
+	if (!f)
+		return false;
+
+	mbs_len = os_utf8_to_mbs(str, len, &mbs);
+	if (mbs_len)
+		fwrite(mbs, 1, mbs_len, f);
+	bfree(mbs);
+	fclose(f);
+
+	return true;
+}
+
+bool os_quick_write_utf8_file(const char *path, const char *str, size_t len,
+		bool marker)
+{
+	FILE *f = os_fopen(path, "wb");
+	if (!f)
+		return false;
+
+	if (marker)
+		fwrite("\xEF\xBB\xBF", 1, 3, f);
+	if (len)
+		fwrite(str, 1, len, f);
+	fclose(f);
+
+	return true;
+}
+
+size_t os_mbs_to_wcs(const char *str, size_t len, wchar_t **pstr)
+{
+	size_t out_len = mbstowcs(NULL, str, len);
+	wchar_t *dst = NULL;
+
+	if (len) {
+		dst = bmalloc((out_len+1) * sizeof(wchar_t));
+		mbstowcs(dst, str, out_len+1);
+		dst[out_len] = 0;
+	}
+
+	*pstr = dst;
+	return out_len;
+}
+
+size_t os_utf8_to_wcs(const char *str, size_t len, wchar_t **pstr)
+{
+	size_t in_len = len ? len : strlen(str);
+	size_t out_len = utf8_to_wchar(str, in_len, NULL, 0, 0);
+	wchar_t *dst = NULL;
+
+	if (out_len) {
+		dst = bmalloc((out_len+1) * sizeof(wchar_t));
+		utf8_to_wchar(str, in_len, dst, out_len+1, 0);
+		dst[out_len] = 0;
+	}
+
+	*pstr = dst;
+	return out_len;
+}
+
+size_t os_wcs_to_mbs(const wchar_t *str, size_t len, char **pstr)
+{
+	size_t out_len = wcstombs(NULL, str, len);
+	char *dst = NULL;
+
+	if (len) {
+		dst = bmalloc(out_len+1);
+		wcstombs(dst, str, out_len+1);
+		dst[out_len] = 0;
+	}
+
+	*pstr = dst;
+	return out_len;
+}
+
+size_t os_wcs_to_utf8(const wchar_t *str, size_t len, char **pstr)
+{
+	size_t in_len = wcslen(str);
+	size_t out_len = wchar_to_utf8(str, in_len, NULL, 0, 0);
+	char *dst = NULL;
+
+	if (out_len) {
+		dst = bmalloc(out_len+1);
+		wchar_to_utf8(str, in_len, dst, out_len+1, 0);
+		dst[out_len] = 0;
+	}
+
+	*pstr = dst;
+	return out_len;
+}
+
+size_t os_utf8_to_mbs(const char *str, size_t len, char **pstr)
+{
+	wchar_t *wstr = NULL;
+	char *dst = NULL;
+	size_t wlen = os_utf8_to_wcs(str, len, &wstr);
+	size_t out_len = os_wcs_to_mbs(wstr, wlen, &dst);
+
+	bfree(wstr);
+	*pstr = dst;
+
+	return out_len;
+}
+
+size_t os_mbs_to_utf8(const char *str, size_t len, char **pstr)
+{
+	wchar_t *wstr = NULL;
+	char *dst = NULL;
+	size_t wlen = os_mbs_to_wcs(str, len, &wstr);
+	size_t out_len = os_wcs_to_utf8(wstr, wlen, &dst);
+
+	bfree(wstr);
+	*pstr = dst;
+
+	return out_len;
+}
+
+#ifdef _MSC_VER
+int fseeko(FILE *stream, off_t offset, int whence)
+{
+#if _FILE_OFFSET_BITS == 64
+	return _fseeki64(stream, offset, whence);
+#else
+	return fseek(stream, offset, whence);
+#endif /* _FILE_OFFSET_BITS == 64 */
+}
+
+off_t ftello(FILE *stream)
+{
+#if _FILE_OFFSET_BITS == 64
+	return _ftelli64(stream);
+#else
+	return ftell(stream);
+#endif /* _FILE_OFFSET_BITS == 64 */
+}
+#endif /* _MSC_VER */

+ 82 - 0
libobs/util/platform.h

@@ -0,0 +1,82 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include <stdio.h>
+#include <wchar.h>
+#include <sys/types.h>
+#include "c99defs.h"
+
+/*
+ * Platform-independent functions for Accessing files, encoding, DLLs,
+ * sleep, timer, and timing.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+EXPORT FILE *os_wfopen(const wchar_t *path, const char *mode);
+EXPORT FILE *os_fopen(const char *path, const char *mode);
+EXPORT off_t os_fgetsize(FILE *file);
+
+EXPORT size_t os_fread_mbs(FILE *file, char **pstr);
+EXPORT size_t os_fread_utf8(FILE *file, char **pstr);
+
+/* functions purely for convenience */
+EXPORT char *os_quick_read_utf8_file(const char *path);
+EXPORT bool os_quick_write_utf8_file(const char *path, const char *str,
+		size_t len, bool marker);
+
+EXPORT size_t os_mbs_to_wcs(const char *str, size_t len, wchar_t **pstr);
+EXPORT size_t os_utf8_to_wcs(const char *str, size_t len, wchar_t **pstr);
+EXPORT size_t os_wcs_to_mbs(const wchar_t *str, size_t len, char **pstr);
+EXPORT size_t os_wcs_to_utf8(const wchar_t *str, size_t len, char **pstr);
+
+EXPORT size_t os_utf8_to_mbs(const char *str, size_t len, char **pstr);
+EXPORT size_t os_mbs_to_utf8(const char *str, size_t len, char **pstr);
+
+EXPORT void *os_dlopen(const char *path);
+EXPORT void *os_dlsym(void *module, const char *func);
+EXPORT void os_dlclose(void *module);
+
+EXPORT void os_sleepto_ns(uint64_t time_target);
+EXPORT void os_sleep_ms(uint32_t duration);
+
+EXPORT uint64_t os_gettime_ns(void);
+EXPORT uint64_t os_gettime_ms(void);
+
+#ifdef _MSC_VER
+EXPORT int fseeko(FILE *stream, off_t offset, int whence);
+EXPORT off_t ftello(FILE *stream);
+#endif
+
+#define strtoll _strtoi64
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 128 - 0
libobs/util/serializer.h

@@ -0,0 +1,128 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#ifndef SERIALIZER_H
+#define SERIALIZER_H
+
+/*
+ *   General programmable serialization functions.  (A shared interface to
+ * various reading/writing to/from different inputs/outputs)
+ *
+ * TODO: Not currently implemented
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum serialize_seek_type {
+	SERIALIZE_SEEK_START,
+	SERIALIZE_SEEK_CURRENT,
+	SERIALIZE_SEEK_END
+};
+
+struct serializer {
+	void *param;
+	size_t (*serialize)(struct serializer, void *, size_t);
+	uint64_t (*seek)(struct serializer, int64_t, enum serialize_seek_type);
+	uint64_t (*getpos)(struct serializer);
+};
+
+static inline size_t serialize(struct serializer *s, void *data, size_t len)
+{
+	if (s->serialize)
+		return s->serialize(s, data, len);
+
+	return 0;
+}
+
+static inline uint64_t serializer_seek(struct serializer *s, int64_t offset,
+		enum serialize_seek_type seek_type)
+{
+	if (s->seek)
+		return s->seek(s, offset, seek_type);
+	return 0;
+}
+
+static inline uint64_t serializer_getpos(struct serializer *s)
+{
+	if (s->getpos)
+		return s->getpos(s);
+	return 0;
+}
+
+static inline void serializer_write_u8(struct serializer *s, uint8_t u8)
+{
+	serialize(s, &u8, sizeof(uint8_t));
+}
+
+static inline void serializer_write_i8(struct serializer *s, int8_t i8)
+{
+	serialize(s, &i8, sizeof(int8_t));
+}
+
+static inline void serializer_write_u16(struct serializer *s, uint16_t u16)
+{
+	serialize(s, &u16, sizeof(uint16_t));
+}
+
+static inline void serializer_write_i16(struct serializer *s, int16_t i16)
+{
+	serialize(s, &i16, sizeof(int16_t));
+}
+
+static inline void serializer_write_u32(struct serializer *s, uint32_t u32)
+{
+	serialize(s, &u32, sizeof(uint32_t));
+}
+
+static inline void serializer_write_i32(struct serializer *s, int32_t i32)
+{
+	serialize(s, &i32, sizeof(int32_t));
+}
+
+static inline void serializer_write_u64(struct serializer *s, uint32_t u64)
+{
+	serialize(s, &u64, sizeof(uint64_t));
+}
+
+static inline void serializer_write_i64(struct serializer *s, int32_t i64)
+{
+	serialize(s, &i64, sizeof(int64_t));
+}
+
+static inline void serializer_write_float(struct serializer *s, float f)
+{
+	serialize(s, &f, sizeof(float));
+}
+
+static inline void serializer_write_double(struct serializer *s, double d)
+{
+	serialize(s, &d, sizeof(double));
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 371 - 0
libobs/util/text-lookup.c

@@ -0,0 +1,371 @@
+/******************************************************************************
+  Copyright (c) 2013 by Hugh Bailey <[email protected]>
+
+  This software is provided 'as-is', without any express or implied
+  warranty. In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source
+     distribution.
+******************************************************************************/
+
+#include "dstr.h"
+#include "darray.h"
+#include "text-lookup.h"
+#include "lexer.h"
+#include "platform.h"
+
+/* ------------------------------------------------------------------------- */
+
+struct text_leaf {
+	char *lookup, *value;
+};
+
+static inline void text_leaf_free(struct text_leaf *leaf)
+{
+	bfree(leaf->lookup);
+	bfree(leaf->value);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct text_node {
+	struct dstr str;
+	struct darray subnodes; /* struct text_node * */
+	struct text_leaf *leaf;
+};
+
+static void text_node_destroy(struct text_node *node)
+{
+	struct text_node **subnodes;
+	size_t i;
+
+	if (!node)
+		return;
+
+	subnodes = node->subnodes.array;
+	dstr_free(&node->str);
+	for (i = 0; i < node->subnodes.num; i++)
+		text_node_destroy(subnodes[i]);
+	if (node->leaf)
+		text_leaf_free(node->leaf);
+	darray_free(&node->subnodes);
+	bfree(node);
+}
+
+static struct text_node *text_node_bychar(struct text_node *node, char ch)
+{
+	size_t i;
+	struct text_node **subnodes = node->subnodes.array;
+
+	for (i = 0; i < node->subnodes.num; i++) {
+		struct text_node *child = subnodes[i];
+		if (!dstr_isempty(&child->str) && child->str.array[0] == ch)
+			return child;
+	}
+
+	return NULL;
+}
+
+static struct text_node *text_node_byname(struct text_node *node,
+		const char *name)
+{
+	size_t i;
+	struct text_node **subnodes = node->subnodes.array;
+
+	for (i = 0; i < node->subnodes.num; i++) {
+		struct text_node *child = subnodes[i];
+		if (astrcmpi_n(child->str.array, name, child->str.len) == 0)
+			return child;
+	}
+
+	return NULL;
+}
+
+static inline void text_node_removesubnode(struct text_node *node,
+		struct text_node *child, size_t idx)
+{
+	darray_erase(sizeof(struct text_node*), &node->subnodes, idx);
+	text_node_destroy(child);
+}
+
+/* ------------------------------------------------------------------------- */
+
+struct text_lookup {
+	struct dstr language;
+	struct text_node *top;
+};
+
+static void lookup_createsubnode(const char *lookup_val,
+		struct text_leaf *leaf, struct text_node *node)
+{
+	struct text_node *new = bmalloc(sizeof(struct text_node));
+	memset(new, 0, sizeof(struct text_node));
+
+	new->leaf = leaf;
+	dstr_copy(&new->str, lookup_val);
+
+	darray_push_back(sizeof(struct text_node*), &new->subnodes, &new);
+}
+
+static void lookup_splitnode(const char *lookup_val, size_t len,
+		struct text_leaf *leaf, struct text_node *node)
+{
+	struct text_node *split = bmalloc(sizeof(struct text_node));
+	memset(split, 0, sizeof(struct text_node));
+
+	dstr_copy(&split->str, node->str.array+len);
+	split->leaf = node->leaf;
+	darray_move(&split->subnodes, &node->subnodes);
+
+	dstr_resize(&node->str, len);
+	darray_push_back(sizeof(struct text_node), &node->subnodes, &split);
+
+	if (lookup_val[len] != 0) {
+		node->leaf = NULL;
+		lookup_createsubnode(lookup_val+len, leaf, node);
+	} else {
+		node->leaf = leaf;
+	}
+}
+
+static bool lookup_addstring(const char *lookup_val, struct text_leaf *leaf,
+		struct text_node *node)
+{
+	struct text_node *child;
+
+	if (!lookup_val || !*lookup_val)
+		return false;
+
+	child = text_node_bychar(node, *lookup_val);
+	if (child) {
+		size_t len;
+
+		for (len = 0; len < child->str.len; len++) {
+			char val1 = child->str.array[len],
+			        val2 = lookup_val[len];
+
+			if (val1 >= 'A' && val1 <= 'Z')
+				val1 += 0x20;
+			if (val2 >= 'A' && val2 <= 'Z')
+				val2 += 0x20;
+
+			if (val1 != val2)
+				break;
+		}
+
+		if (len == child->str.len)
+			return lookup_addstring(lookup_val+len, leaf, child);
+		else
+			lookup_splitnode(lookup_val, len, leaf, child);
+	} else {
+		lookup_createsubnode(lookup_val, leaf, child);
+	}
+
+	return true;
+}
+
+static void lookup_getstringtoken(struct lexer *lex, struct strref *token)
+{
+	const char *temp = lex->offset;
+	bool was_backslash  = false;
+
+	while (*temp != 0 && *temp != '\n') {
+		if (!was_backslash) {
+			if (*temp == '\\') {
+				was_backslash = true;
+			} else if (*temp == '"') {
+				++temp;
+				break;
+			}
+		} else {
+			was_backslash = false;
+		}
+
+		++temp;
+	}
+
+	token->len += temp - lex->offset - 1; /* include starting " char */
+	lex->offset = temp;
+}
+
+static bool lookup_gettoken(struct lexer *lex, struct strref *str)
+{
+	struct base_token temp;
+
+	base_token_clear(&temp);
+	strref_clear(str);
+
+	while (lexer_getbasetoken(lex, &temp, false)) {
+		char ch = *temp.text.array;
+
+		if (!str->array) {
+			/* comments are designated with a #, and end at LF */
+			if (ch == '#') {
+				while(ch != '\n' && ch != 0)
+					ch = *(++lex->offset);
+			} else {
+				strref_copy(str, &temp.text);
+			}
+		} else {
+			if (temp.type == BASETOKEN_WHITESPACE) {
+				lex->offset -= temp.text.len;
+				break;
+			}
+
+			if (ch == '"') {
+				lookup_getstringtoken(lex, str);
+				break;
+			} else if (ch == '#') {
+				lex->offset--;
+				break;
+			}
+
+			str->len += temp.text.len;
+		}
+	}
+
+	return (str->len != 0);
+}
+
+static inline bool lookup_goto_nextline(struct lexer *p)
+{
+	struct strref val;
+	bool success = true;
+
+	strref_clear(&val);
+
+	while (true) {
+		if (!lookup_gettoken(p, &val)) {
+			success = false;
+			break;
+		}
+		if (*val.array == '\n')
+			break;
+	}
+
+	return success;
+}
+
+static void lookup_addfiledata(struct text_lookup *lookup,
+		const char *file_data)
+{
+	struct lexer lex;
+	struct strref name, value;
+
+	lexer_start(&lex, file_data);
+	strref_clear(&name);
+	strref_clear(&value);
+
+	while (lookup_gettoken(&lex, &name)) {
+		struct text_leaf *leaf;
+		bool got_eq = false;
+
+		if (*name.array == '\n')
+			continue;
+getval:
+		if (!lookup_gettoken(&lex, &value))
+			break;
+		if (*value.array == '\n')
+			continue;
+		else if (!got_eq && *value.array == '=') {
+			got_eq = true;
+			goto getval;
+		}
+
+		leaf = bmalloc(sizeof(struct text_leaf));
+		leaf->lookup = bstrdup_n(name.array,  name.len);
+		leaf->value  = bstrdup_n(value.array, value.len);
+
+		lookup_addstring(leaf->lookup, leaf, lookup->top);
+
+		if (!lookup_goto_nextline(&lex))
+			break;
+	}
+}
+
+static inline bool lookup_getstring(const char *lookup_val,
+		const char **out, struct text_node *node)
+{
+	struct text_node *child;
+	char ch;
+
+	if (!node)
+		return false;
+
+	child = text_node_byname(node, lookup_val);
+	if (!child)
+		return false;
+
+	lookup_val += child->str.len;
+	ch = *lookup_val;
+	if (ch)
+		return lookup_getstring(lookup_val, out, child);
+
+	if (!child->leaf)
+		return false;
+
+	*out = child->leaf->value;
+	return true;
+}
+
+/* ------------------------------------------------------------------------- */
+
+lookup_t text_lookup_create(const char *path)
+{
+	struct text_lookup *lookup;
+	struct dstr file_str;
+	char *temp = NULL;
+	FILE *file;
+
+	file = os_fopen(path, "rb");
+	if (!file)
+		return NULL;
+
+	os_fread_utf8(file, &temp);
+	dstr_init_move_array(&file_str, temp);
+	fclose(file);
+
+	if (!file_str.array)
+		return NULL;
+
+	lookup = bmalloc(sizeof(struct text_lookup));
+	memset(lookup, 0, sizeof(struct text_lookup));
+
+	lookup->top = bmalloc(sizeof(struct text_node));
+	memset(lookup->top, 0, sizeof(struct text_node));
+
+	dstr_replace(&file_str, "\r", " ");
+	lookup_addfiledata(lookup, file_str.array);
+	dstr_free(&file_str);
+
+	return lookup;
+}
+
+void text_lookup_destroy(lookup_t lookup)
+{
+	if (lookup) {
+		dstr_free(&lookup->language);
+		text_node_destroy(lookup->top);
+
+		bfree(lookup);
+	}
+}
+
+bool text_lookup_getstr(lookup_t lookup, const char *lookup_val,
+		const char **out)
+{
+	return lookup_getstring(lookup_val, out, lookup->top);
+}

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä